|
|
@@ -27,6 +27,9 @@ TASK_PRESET_BY_KIND = {
|
|
|
"root-delivery": "script_root_worker",
|
|
|
}
|
|
|
|
|
|
+_MAX_STRUCTURED_ARGUMENT_BYTES = 2 * 1024 * 1024
|
|
|
+_MAX_STRUCTURED_LIST_ITEMS = 1_000
|
|
|
+
|
|
|
|
|
|
def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGateway) -> None:
|
|
|
"""Install Host wrappers into the supplied Runner registry.
|
|
|
@@ -35,6 +38,23 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
isolated tests deterministic while production creates only one composition.
|
|
|
"""
|
|
|
|
|
|
+ async def complete_retrieval(
|
|
|
+ source_type: str,
|
|
|
+ tool_name: str,
|
|
|
+ query: dict[str, Any],
|
|
|
+ context: dict[str, Any],
|
|
|
+ ) -> ToolResult:
|
|
|
+ evidence = await gateway.retrieve(source_type, tool_name, query, context)
|
|
|
+ submission = await gateway.submit_current_attempt(context)
|
|
|
+ result_json = _json({"evidence": evidence, "submission": submission})
|
|
|
+ return ToolResult(
|
|
|
+ title="Evidence frozen and attempt submitted",
|
|
|
+ output=result_json,
|
|
|
+ long_term_memory="The unique retrieval Evidence was frozen and submitted",
|
|
|
+ terminate_run=True,
|
|
|
+ result_summary=result_json,
|
|
|
+ )
|
|
|
+
|
|
|
async def read_input_snapshot(context: dict[str, Any] | None = None) -> str:
|
|
|
"""Read the immutable input snapshot owned by this mission."""
|
|
|
|
|
|
@@ -63,6 +83,7 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
) -> str | ToolResult:
|
|
|
"""Validate and atomically create Tasks from complete business contracts."""
|
|
|
|
|
|
+ contracts = _structured_object_list(contracts, "contracts", max_items=50)
|
|
|
return _json(
|
|
|
await gateway.plan_script_tasks(
|
|
|
contracts=contracts,
|
|
|
@@ -71,7 +92,12 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
)
|
|
|
)
|
|
|
|
|
|
- _register(registry, plan_script_tasks, capabilities=["task_control"])
|
|
|
+ _register(
|
|
|
+ registry,
|
|
|
+ plan_script_tasks,
|
|
|
+ capabilities=["task_control"],
|
|
|
+ schema=_plan_script_tasks_schema(),
|
|
|
+ )
|
|
|
|
|
|
async def decide_script_task(
|
|
|
task_id: str,
|
|
|
@@ -95,13 +121,20 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
"cancel",
|
|
|
}:
|
|
|
raise ValueError("action is not supported by decide_script_task")
|
|
|
+ if replacement_contract is not None:
|
|
|
+ replacement_contract = _structured_object(
|
|
|
+ replacement_contract, "replacement_contract"
|
|
|
+ )
|
|
|
+ child_contracts = _structured_object_list(
|
|
|
+ child_contracts or [], "child_contracts", max_items=50
|
|
|
+ )
|
|
|
result = await gateway.decide_script_task(
|
|
|
task_id=task_id,
|
|
|
action=action,
|
|
|
reason=reason,
|
|
|
validation_id=validation_id,
|
|
|
replacement_contract=replacement_contract,
|
|
|
- child_contracts=child_contracts or [],
|
|
|
+ child_contracts=child_contracts,
|
|
|
context=context or {},
|
|
|
)
|
|
|
result_json = _json(result)
|
|
|
@@ -120,12 +153,17 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
)
|
|
|
return result_json
|
|
|
|
|
|
- _register(registry, decide_script_task, capabilities=["task_control"])
|
|
|
+ _register(
|
|
|
+ registry,
|
|
|
+ decide_script_task,
|
|
|
+ capabilities=["task_control"],
|
|
|
+ schema=_decide_script_task_schema(),
|
|
|
+ )
|
|
|
|
|
|
async def inspect_script_plan(
|
|
|
context: dict[str, Any] | None = None,
|
|
|
) -> str:
|
|
|
- """Inspect a bounded business view of the current Task tree and contracts."""
|
|
|
+ """Inspect Tasks; copy complete accepted_decision_ref objects into new contracts."""
|
|
|
|
|
|
return _json(await gateway.inspect_script_plan(context=context or {}))
|
|
|
|
|
|
@@ -144,16 +182,16 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
|
|
|
_register(registry, dispatch_script_tasks, capabilities=["task_control"])
|
|
|
|
|
|
- async def query_pattern_qa(message: str, context: dict[str, Any] | None = None) -> str:
|
|
|
+ async def query_pattern_qa(
|
|
|
+ message: str, context: dict[str, Any] | None = None
|
|
|
+ ) -> ToolResult:
|
|
|
"""Query the configured Pattern service and freeze its evidence."""
|
|
|
|
|
|
- return _json(
|
|
|
- await gateway.retrieve(
|
|
|
- "pattern",
|
|
|
- "query_pattern_qa",
|
|
|
- {"message": message},
|
|
|
- context or {},
|
|
|
- )
|
|
|
+ return await complete_retrieval(
|
|
|
+ "pattern",
|
|
|
+ "query_pattern_qa",
|
|
|
+ {"message": message},
|
|
|
+ context or {},
|
|
|
)
|
|
|
|
|
|
_register(registry, query_pattern_qa, capabilities=["external_send", "write"])
|
|
|
@@ -165,7 +203,7 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
match_fields: dict[str, str] | None = None,
|
|
|
top_k: int = 3,
|
|
|
context: dict[str, Any] | None = None,
|
|
|
- ) -> str:
|
|
|
+ ) -> ToolResult:
|
|
|
"""Search the frozen script-decode index and freeze ranked evidence."""
|
|
|
|
|
|
if return_field not in {"主脉络", "元素", "主题", "形式", "作用", "感受"}:
|
|
|
@@ -174,25 +212,25 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
raise ValueError("keyword and account_name cannot both be empty")
|
|
|
if not 1 <= top_k <= 5:
|
|
|
raise ValueError("top_k must be between 1 and 5")
|
|
|
+ if match_fields is not None:
|
|
|
+ match_fields = _structured_object(match_fields, "match_fields")
|
|
|
valid_columns = {"主题", "形式", "作用", "感受"}
|
|
|
valid_subfields = {"维度名", "维度值"}
|
|
|
if match_fields and (
|
|
|
set(match_fields) - valid_columns or set(match_fields.values()) - valid_subfields
|
|
|
):
|
|
|
raise ValueError("match_fields contains an unsupported column or subfield")
|
|
|
- return _json(
|
|
|
- await gateway.retrieve(
|
|
|
- "decode",
|
|
|
- "search_script_decode_case",
|
|
|
- {
|
|
|
- "return_field": return_field,
|
|
|
- "keyword": keyword,
|
|
|
- "account_name": account_name,
|
|
|
- "match_fields": match_fields or {},
|
|
|
- "top_k": top_k,
|
|
|
- },
|
|
|
- context or {},
|
|
|
- )
|
|
|
+ return await complete_retrieval(
|
|
|
+ "decode",
|
|
|
+ "search_script_decode_case",
|
|
|
+ {
|
|
|
+ "return_field": return_field,
|
|
|
+ "keyword": keyword,
|
|
|
+ "account_name": account_name,
|
|
|
+ "match_fields": match_fields or {},
|
|
|
+ "top_k": top_k,
|
|
|
+ },
|
|
|
+ context or {},
|
|
|
)
|
|
|
|
|
|
_register(
|
|
|
@@ -207,24 +245,22 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
platform_channel: str = "xhs",
|
|
|
max_count: int = 5,
|
|
|
context: dict[str, Any] | None = None,
|
|
|
- ) -> str:
|
|
|
+ ) -> ToolResult:
|
|
|
"""Search the allowlisted external service without legacy log writes."""
|
|
|
|
|
|
if platform_channel not in {"xhs", "zhihu"}:
|
|
|
raise ValueError("platform_channel must be xhs or zhihu")
|
|
|
if not 1 <= max_count <= 20:
|
|
|
raise ValueError("max_count must be between 1 and 20")
|
|
|
- return _json(
|
|
|
- await gateway.retrieve(
|
|
|
- "external",
|
|
|
- "external_search_case",
|
|
|
- {
|
|
|
- "keyword": keyword,
|
|
|
- "platform_channel": platform_channel,
|
|
|
- "max_count": max_count,
|
|
|
- },
|
|
|
- context or {},
|
|
|
- )
|
|
|
+ return await complete_retrieval(
|
|
|
+ "external",
|
|
|
+ "external_search_case",
|
|
|
+ {
|
|
|
+ "keyword": keyword,
|
|
|
+ "platform_channel": platform_channel,
|
|
|
+ "max_count": max_count,
|
|
|
+ },
|
|
|
+ context or {},
|
|
|
)
|
|
|
|
|
|
_register(
|
|
|
@@ -245,18 +281,16 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
keyword: str,
|
|
|
max_count: int = 3,
|
|
|
context: dict[str, Any] | None = None,
|
|
|
- ) -> str:
|
|
|
+ ) -> ToolResult:
|
|
|
"""Search the frozen internal knowledge corpus and freeze evidence."""
|
|
|
|
|
|
if not 1 <= max_count <= 20:
|
|
|
raise ValueError("max_count must be between 1 and 20")
|
|
|
- return _json(
|
|
|
- await gateway.retrieve(
|
|
|
- "knowledge",
|
|
|
- "search_knowledge",
|
|
|
- {"keyword": keyword, "max_count": max_count},
|
|
|
- context or {},
|
|
|
- )
|
|
|
+ return await complete_retrieval(
|
|
|
+ "knowledge",
|
|
|
+ "search_knowledge",
|
|
|
+ {"keyword": keyword, "max_count": max_count},
|
|
|
+ context or {},
|
|
|
)
|
|
|
|
|
|
_register(
|
|
|
@@ -279,6 +313,9 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
) -> str:
|
|
|
"""Freeze one direction candidate inside the protected Attempt."""
|
|
|
|
|
|
+ goals = _structured_object_list(goals, "goals")
|
|
|
+ criteria = _structured_object_list(criteria, "criteria")
|
|
|
+ domain_criteria = _structured_object_list(domain_criteria or [], "domain_criteria")
|
|
|
return _json(
|
|
|
await gateway.save_direction_candidate(
|
|
|
goals=goals,
|
|
|
@@ -293,7 +330,12 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
)
|
|
|
)
|
|
|
|
|
|
- _register(registry, save_direction_candidate, capabilities=["write"])
|
|
|
+ _register(
|
|
|
+ registry,
|
|
|
+ save_direction_candidate,
|
|
|
+ capabilities=["write"],
|
|
|
+ schema=_save_direction_candidate_schema(),
|
|
|
+ )
|
|
|
|
|
|
async def read_accepted_input_bundle(
|
|
|
context: dict[str, Any] | None = None,
|
|
|
@@ -358,6 +400,13 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
) -> str:
|
|
|
"""Create one Paragraph inside the protected Attempt workspace."""
|
|
|
|
|
|
+ content_range = _structured_object(content_range, "content_range")
|
|
|
+ theme_elements = _structured_object_list(theme_elements or [], "theme_elements")
|
|
|
+ form_elements = _structured_object_list(form_elements or [], "form_elements")
|
|
|
+ function_elements = _structured_object_list(
|
|
|
+ function_elements or [], "function_elements"
|
|
|
+ )
|
|
|
+ feeling_elements = _structured_object_list(feeling_elements or [], "feeling_elements")
|
|
|
return _json(
|
|
|
await gateway.candidate_command(
|
|
|
"create_script_paragraph",
|
|
|
@@ -386,6 +435,7 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
) -> str:
|
|
|
"""Append unique atoms to one allowlisted Paragraph column."""
|
|
|
|
|
|
+ atoms = _structured_object_list(atoms, "atoms")
|
|
|
return _json(
|
|
|
await gateway.candidate_command(
|
|
|
"append_paragraph_atoms",
|
|
|
@@ -426,6 +476,7 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
) -> str:
|
|
|
"""Atomically validate and update a Paragraph batch in this workspace."""
|
|
|
|
|
|
+ updates = _structured_object_list(updates, "updates")
|
|
|
return _json(
|
|
|
await gateway.candidate_command(
|
|
|
"batch_update_script_paragraphs", updates, context or {}
|
|
|
@@ -446,6 +497,15 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
) -> str:
|
|
|
"""Create one Element inside the protected Attempt workspace."""
|
|
|
|
|
|
+ if commonality_analysis is not None:
|
|
|
+ commonality_analysis = _structured_object(
|
|
|
+ commonality_analysis, "commonality_analysis"
|
|
|
+ )
|
|
|
+ if topic_support is not None:
|
|
|
+ topic_support = _structured_object(topic_support, "topic_support")
|
|
|
+ if weight_score is not None:
|
|
|
+ weight_score = _structured_object(weight_score, "weight_score")
|
|
|
+ support_elements = _structured_object_list(support_elements or [], "support_elements")
|
|
|
return _json(
|
|
|
await gateway.candidate_command(
|
|
|
"create_script_element",
|
|
|
@@ -471,6 +531,7 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
) -> str:
|
|
|
"""Update one Element with an allowlisted, workspace-local patch."""
|
|
|
|
|
|
+ updates = _structured_object(updates, "updates")
|
|
|
return _json(
|
|
|
await gateway.candidate_command(
|
|
|
"update_script_element",
|
|
|
@@ -487,6 +548,7 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
) -> str:
|
|
|
"""Atomically link active local Paragraphs and Elements."""
|
|
|
|
|
|
+ links = _structured_object_list(links, "links")
|
|
|
return _json(
|
|
|
await gateway.candidate_command("batch_link_paragraph_elements", links, context or {})
|
|
|
)
|
|
|
@@ -499,6 +561,7 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
) -> str:
|
|
|
"""Atomically remove exact Paragraph/Element links from this workspace."""
|
|
|
|
|
|
+ links = _structured_object_list(links, "links")
|
|
|
return _json(
|
|
|
await gateway.candidate_command("delete_paragraph_element_links", links, context or {})
|
|
|
)
|
|
|
@@ -513,6 +576,7 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
) -> str:
|
|
|
"""Freeze one comparison over the candidates pinned by the Task contract."""
|
|
|
|
|
|
+ criterion_results = _structured_object_list(criterion_results, "criterion_results")
|
|
|
return _json(
|
|
|
await gateway.candidate_command(
|
|
|
"save_comparison_candidate",
|
|
|
@@ -546,6 +610,7 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
) -> str:
|
|
|
"""Freeze governance closure with one adopted StructuredScript."""
|
|
|
|
|
|
+ unresolved_defects = _structured_object_list(unresolved_defects, "unresolved_defects")
|
|
|
return _json(
|
|
|
await gateway.candidate_command(
|
|
|
"save_candidate_portfolio",
|
|
|
@@ -633,6 +698,23 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
) -> ToolResult:
|
|
|
"""Submit bounded criterion results plus structured, evidence-bound defects."""
|
|
|
|
|
|
+ criterion_results = _structured_object_list(criterion_results, "criterion_results")
|
|
|
+ defects = _structured_object_list(defects, "defects")
|
|
|
+ criterion_results = [
|
|
|
+ {
|
|
|
+ **item,
|
|
|
+ "reason": str(item.get("reason", ""))[:300],
|
|
|
+ }
|
|
|
+ for item in criterion_results
|
|
|
+ ]
|
|
|
+ defects = [
|
|
|
+ {
|
|
|
+ **item,
|
|
|
+ "observed_excerpt": str(item.get("observed_excerpt", ""))[:500],
|
|
|
+ }
|
|
|
+ for item in defects
|
|
|
+ ]
|
|
|
+ recommendation = recommendation[:200]
|
|
|
result = await gateway.submit_structured_validation(
|
|
|
verdict=verdict,
|
|
|
criterion_results=criterion_results,
|
|
|
@@ -651,7 +733,12 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
result_summary=result_json,
|
|
|
)
|
|
|
|
|
|
- _register(registry, submit_validation, capabilities=["validation_submit"])
|
|
|
+ _register(
|
|
|
+ registry,
|
|
|
+ submit_validation,
|
|
|
+ capabilities=["validation_submit"],
|
|
|
+ schema=_submit_validation_schema(),
|
|
|
+ )
|
|
|
|
|
|
|
|
|
def _register(
|
|
|
@@ -707,6 +794,404 @@ def _decode_schema() -> dict[str, Any]:
|
|
|
}
|
|
|
|
|
|
|
|
|
+def _plan_script_tasks_schema() -> dict[str, Any]:
|
|
|
+ return {
|
|
|
+ "type": "function",
|
|
|
+ "function": {
|
|
|
+ "name": "plan_script_tasks",
|
|
|
+ "description": (
|
|
|
+ "Create bounded Tasks. Every contracts item must be a complete "
|
|
|
+ "script-task-contract/v1 object; do not send preset or display fields."
|
|
|
+ ),
|
|
|
+ "parameters": {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "contracts": {
|
|
|
+ "type": "array",
|
|
|
+ "items": _task_contract_schema(),
|
|
|
+ "minItems": 1,
|
|
|
+ "maxItems": 50,
|
|
|
+ },
|
|
|
+ "parent_task_id": {"type": ["string", "null"], "default": None},
|
|
|
+ },
|
|
|
+ "required": ["contracts"],
|
|
|
+ "additionalProperties": False,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+def _decide_script_task_schema() -> dict[str, Any]:
|
|
|
+ return {
|
|
|
+ "type": "function",
|
|
|
+ "function": {
|
|
|
+ "name": "decide_script_task",
|
|
|
+ "description": (
|
|
|
+ "Apply one validated Planner decision. Replacement and child contracts, "
|
|
|
+ "when required by the action, must be complete script-task-contract/v1 objects."
|
|
|
+ ),
|
|
|
+ "parameters": {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "task_id": {"type": "string"},
|
|
|
+ "action": {
|
|
|
+ "type": "string",
|
|
|
+ "enum": [
|
|
|
+ "accept",
|
|
|
+ "repair",
|
|
|
+ "retry",
|
|
|
+ "revise",
|
|
|
+ "split",
|
|
|
+ "block",
|
|
|
+ "supersede",
|
|
|
+ "cancel",
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ "reason": {"type": "string"},
|
|
|
+ "validation_id": {"type": ["string", "null"], "default": None},
|
|
|
+ "replacement_contract": {
|
|
|
+ "oneOf": [_task_contract_schema(), {"type": "null"}],
|
|
|
+ "default": None,
|
|
|
+ },
|
|
|
+ "child_contracts": {
|
|
|
+ "type": "array",
|
|
|
+ "items": _task_contract_schema(),
|
|
|
+ "maxItems": 50,
|
|
|
+ "default": [],
|
|
|
+ },
|
|
|
+ },
|
|
|
+ "required": ["task_id", "action", "reason"],
|
|
|
+ "additionalProperties": False,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+def _task_contract_schema() -> dict[str, Any]:
|
|
|
+ task_kinds = [
|
|
|
+ "direction",
|
|
|
+ "pattern-retrieval",
|
|
|
+ "decode-retrieval",
|
|
|
+ "external-retrieval",
|
|
|
+ "knowledge-retrieval",
|
|
|
+ "structure",
|
|
|
+ "paragraph",
|
|
|
+ "element-set",
|
|
|
+ "compare",
|
|
|
+ "compose",
|
|
|
+ "candidate-portfolio",
|
|
|
+ ]
|
|
|
+ artifact_ref = {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "uri": {"type": "string"},
|
|
|
+ "kind": {"type": "string"},
|
|
|
+ "version": {"type": ["string", "null"]},
|
|
|
+ "digest": {"type": ["string", "null"]},
|
|
|
+ "summary": {"type": ["string", "null"]},
|
|
|
+ "metadata": {"type": "object"},
|
|
|
+ },
|
|
|
+ "required": ["uri", "kind"],
|
|
|
+ "additionalProperties": False,
|
|
|
+ }
|
|
|
+ decision_ref = {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "decision_id": {"type": "string"},
|
|
|
+ "artifact_ref": artifact_ref,
|
|
|
+ "scope_ref": {
|
|
|
+ "type": "string",
|
|
|
+ "description": "A script-build:// control URI from the accepted decision.",
|
|
|
+ },
|
|
|
+ "expected_task_kind": {"type": "string", "enum": task_kinds},
|
|
|
+ },
|
|
|
+ "required": ["decision_id", "artifact_ref", "scope_ref", "expected_task_kind"],
|
|
|
+ "additionalProperties": False,
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "schema_version": {
|
|
|
+ "type": "string",
|
|
|
+ "const": "script-task-contract/v1",
|
|
|
+ },
|
|
|
+ "task_kind": {"type": "string", "enum": task_kinds},
|
|
|
+ "scope_ref": {
|
|
|
+ "type": "string",
|
|
|
+ "description": "Stable logical scope URI beginning with script-build://.",
|
|
|
+ },
|
|
|
+ "intent_class": {
|
|
|
+ "type": "string",
|
|
|
+ "enum": [
|
|
|
+ "explore",
|
|
|
+ "expand",
|
|
|
+ "replace",
|
|
|
+ "repair",
|
|
|
+ "compare",
|
|
|
+ "compose",
|
|
|
+ "portfolio",
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ "objective": {"type": "string", "maxLength": 2000},
|
|
|
+ "input_decision_refs": {"type": "array", "items": decision_ref},
|
|
|
+ "base_artifact_ref": {
|
|
|
+ "oneOf": [artifact_ref, {"type": "null"}],
|
|
|
+ },
|
|
|
+ "write_scope": {
|
|
|
+ "type": "array",
|
|
|
+ "items": {
|
|
|
+ "type": "string",
|
|
|
+ "description": "A bounded script-build:// control URI.",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ "gap_ref": {"type": ["string", "null"]},
|
|
|
+ "output_schema": {
|
|
|
+ "type": "string",
|
|
|
+ "enum": [
|
|
|
+ "script-direction/v1",
|
|
|
+ "evidence-record/v1",
|
|
|
+ "structure-artifact/v1",
|
|
|
+ "paragraph-artifact/v1",
|
|
|
+ "paragraph-patch/v1",
|
|
|
+ "element-set-artifact/v1",
|
|
|
+ "element-set-patch/v1",
|
|
|
+ "comparison-artifact/v1",
|
|
|
+ "structured-script/v1",
|
|
|
+ "candidate-portfolio/v1",
|
|
|
+ ],
|
|
|
+ "description": "Must match task_kind; use evidence-record/v1 for retrieval.",
|
|
|
+ },
|
|
|
+ "criteria": {
|
|
|
+ "type": "array",
|
|
|
+ "minItems": 1,
|
|
|
+ "maxItems": 16,
|
|
|
+ "items": {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "criterion_id": {"type": "string"},
|
|
|
+ "description": {"type": "string"},
|
|
|
+ "hard": {"type": "boolean"},
|
|
|
+ },
|
|
|
+ "required": ["criterion_id", "description", "hard"],
|
|
|
+ "additionalProperties": False,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ "budget": {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "max_attempts": {"type": "integer", "minimum": 1, "maximum": 4},
|
|
|
+ "max_tokens": {
|
|
|
+ "type": "integer",
|
|
|
+ "minimum": 500000,
|
|
|
+ "maximum": 1000000,
|
|
|
+ },
|
|
|
+ "max_seconds": {"type": "integer", "minimum": 1, "maximum": 900},
|
|
|
+ "max_external_queries": {
|
|
|
+ "type": "integer",
|
|
|
+ "minimum": 1,
|
|
|
+ "maximum": 40,
|
|
|
+ },
|
|
|
+ "max_no_improvement": {
|
|
|
+ "type": "integer",
|
|
|
+ "minimum": 1,
|
|
|
+ "maximum": 3,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ "required": [
|
|
|
+ "max_attempts",
|
|
|
+ "max_tokens",
|
|
|
+ "max_seconds",
|
|
|
+ "max_external_queries",
|
|
|
+ "max_no_improvement",
|
|
|
+ ],
|
|
|
+ "additionalProperties": False,
|
|
|
+ },
|
|
|
+ "supersedes_decision_ids": {"type": "array", "items": {"type": "string"}},
|
|
|
+ "candidate_closure_decision_refs": {"type": "array", "items": decision_ref},
|
|
|
+ "adopted_decision_ids": {"type": "array", "items": {"type": "string"}},
|
|
|
+ "held_or_rejected_decision_ids": {
|
|
|
+ "type": "array",
|
|
|
+ "items": {"type": "string"},
|
|
|
+ },
|
|
|
+ "compose_order": {"type": "array", "items": {"type": "string"}},
|
|
|
+ "comparison_decision_refs": {"type": "array", "items": decision_ref},
|
|
|
+ },
|
|
|
+ "required": [
|
|
|
+ "schema_version",
|
|
|
+ "task_kind",
|
|
|
+ "scope_ref",
|
|
|
+ "intent_class",
|
|
|
+ "objective",
|
|
|
+ "input_decision_refs",
|
|
|
+ "base_artifact_ref",
|
|
|
+ "write_scope",
|
|
|
+ "gap_ref",
|
|
|
+ "output_schema",
|
|
|
+ "criteria",
|
|
|
+ "budget",
|
|
|
+ "supersedes_decision_ids",
|
|
|
+ "candidate_closure_decision_refs",
|
|
|
+ "adopted_decision_ids",
|
|
|
+ "held_or_rejected_decision_ids",
|
|
|
+ "compose_order",
|
|
|
+ "comparison_decision_refs",
|
|
|
+ ],
|
|
|
+ "additionalProperties": False,
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+def _save_direction_candidate_schema() -> dict[str, Any]:
|
|
|
+ criterion = {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "criterion_id": {"type": "string"},
|
|
|
+ "description": {"type": "string"},
|
|
|
+ },
|
|
|
+ "required": ["criterion_id", "description"],
|
|
|
+ "additionalProperties": False,
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ "type": "function",
|
|
|
+ "function": {
|
|
|
+ "name": "save_direction_candidate",
|
|
|
+ "description": (
|
|
|
+ "Freeze one Direction. evidence_refs must be exact artifact_ref.uri values "
|
|
|
+ "from accepted retrieval children; never invent a reference."
|
|
|
+ ),
|
|
|
+ "parameters": {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "goals": {
|
|
|
+ "type": "array",
|
|
|
+ "minItems": 1,
|
|
|
+ "maxItems": 3,
|
|
|
+ "items": {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "goal_id": {"type": "string"},
|
|
|
+ "statement": {"type": "string"},
|
|
|
+ "rationale": {"type": "string"},
|
|
|
+ },
|
|
|
+ "required": ["goal_id", "statement", "rationale"],
|
|
|
+ "additionalProperties": False,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ "criteria": {"type": "array", "items": criterion},
|
|
|
+ "domain_criteria": {"type": "array", "items": criterion, "default": []},
|
|
|
+ "topic_refs": {"type": "array", "items": {"type": "string"}, "default": []},
|
|
|
+ "persona_refs": {
|
|
|
+ "type": "array",
|
|
|
+ "items": {"type": "string"},
|
|
|
+ "default": [],
|
|
|
+ },
|
|
|
+ "strategy_refs": {
|
|
|
+ "type": "array",
|
|
|
+ "items": {"type": "string"},
|
|
|
+ "default": [],
|
|
|
+ },
|
|
|
+ "evidence_refs": {
|
|
|
+ "type": "array",
|
|
|
+ "minItems": 1,
|
|
|
+ "items": {"type": "string"},
|
|
|
+ },
|
|
|
+ "legacy_markdown": {"type": "string"},
|
|
|
+ },
|
|
|
+ "required": ["goals", "criteria", "evidence_refs", "legacy_markdown"],
|
|
|
+ "additionalProperties": False,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+def _submit_validation_schema() -> dict[str, Any]:
|
|
|
+ artifact_ref = {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "uri": {"type": "string"},
|
|
|
+ "kind": {"type": "string"},
|
|
|
+ "version": {"type": ["string", "null"]},
|
|
|
+ "digest": {"type": ["string", "null"]},
|
|
|
+ },
|
|
|
+ "required": ["uri", "kind"],
|
|
|
+ "additionalProperties": False,
|
|
|
+ }
|
|
|
+ defect = {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "defect_code": {"type": "string"},
|
|
|
+ "criterion_id": {"type": "string"},
|
|
|
+ "scope_ref": {"type": "string"},
|
|
|
+ "observed_excerpt": {"type": "string", "maxLength": 500},
|
|
|
+ "evidence_refs": {"type": "array", "items": artifact_ref},
|
|
|
+ "severity": {"type": "string", "enum": ["warning", "hard", "critical"]},
|
|
|
+ "invalidated_inputs": {"type": "array", "items": {"type": "string"}},
|
|
|
+ "recommended_action_class": {
|
|
|
+ "type": "string",
|
|
|
+ "enum": [
|
|
|
+ "repair",
|
|
|
+ "retry",
|
|
|
+ "revise",
|
|
|
+ "split",
|
|
|
+ "retrieve",
|
|
|
+ "replace",
|
|
|
+ "compare",
|
|
|
+ "block",
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ },
|
|
|
+ "required": [
|
|
|
+ "defect_code",
|
|
|
+ "criterion_id",
|
|
|
+ "scope_ref",
|
|
|
+ "observed_excerpt",
|
|
|
+ "evidence_refs",
|
|
|
+ "severity",
|
|
|
+ "invalidated_inputs",
|
|
|
+ "recommended_action_class",
|
|
|
+ ],
|
|
|
+ "additionalProperties": False,
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ "type": "function",
|
|
|
+ "function": {
|
|
|
+ "name": "submit_validation",
|
|
|
+ "description": (
|
|
|
+ "Submit every Task criterion exactly once. Passed reports have no hard or "
|
|
|
+ "critical defects; failed/inconclusive reports must contain defects."
|
|
|
+ ),
|
|
|
+ "parameters": {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "verdict": {
|
|
|
+ "type": "string",
|
|
|
+ "enum": ["passed", "failed", "inconclusive"],
|
|
|
+ },
|
|
|
+ "criterion_results": {
|
|
|
+ "type": "array",
|
|
|
+ "items": {
|
|
|
+ "type": "object",
|
|
|
+ "properties": {
|
|
|
+ "criterion_id": {"type": "string"},
|
|
|
+ "verdict": {
|
|
|
+ "type": "string",
|
|
|
+ "enum": ["passed", "failed", "inconclusive"],
|
|
|
+ },
|
|
|
+ "reason": {"type": "string", "maxLength": 300},
|
|
|
+ },
|
|
|
+ "required": ["criterion_id", "verdict", "reason"],
|
|
|
+ "additionalProperties": False,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ "defects": {"type": "array", "items": defect},
|
|
|
+ "recommendation": {"type": "string", "maxLength": 200, "default": ""},
|
|
|
+ },
|
|
|
+ "required": ["verdict", "criterion_results", "defects"],
|
|
|
+ "additionalProperties": False,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
def _external_schema() -> dict[str, Any]:
|
|
|
return {
|
|
|
"type": "function",
|
|
|
@@ -754,6 +1239,47 @@ def _json(value: Any) -> str:
|
|
|
return json.dumps(value, ensure_ascii=False, sort_keys=True, default=str)
|
|
|
|
|
|
|
|
|
+def _structured_object(value: Any, label: str) -> dict[str, Any]:
|
|
|
+ normalized = _decode_bounded_structured_value(value, label)
|
|
|
+ if not isinstance(normalized, dict):
|
|
|
+ raise ValueError(f"{label} must be a JSON object")
|
|
|
+ return normalized
|
|
|
+
|
|
|
+
|
|
|
+def _structured_object_list(
|
|
|
+ value: Any,
|
|
|
+ label: str,
|
|
|
+ *,
|
|
|
+ max_items: int = _MAX_STRUCTURED_LIST_ITEMS,
|
|
|
+) -> list[dict[str, Any]]:
|
|
|
+ normalized = _decode_bounded_structured_value(value, label)
|
|
|
+ if not isinstance(normalized, list):
|
|
|
+ raise ValueError(f"{label} must be a JSON array")
|
|
|
+ if len(normalized) > max_items:
|
|
|
+ raise ValueError(f"{label} exceeds the item limit")
|
|
|
+ return [
|
|
|
+ _structured_object(item, f"{label}[{index}]")
|
|
|
+ for index, item in enumerate(normalized)
|
|
|
+ ]
|
|
|
+
|
|
|
+
|
|
|
+def _decode_bounded_structured_value(value: Any, label: str) -> Any:
|
|
|
+ if isinstance(value, str):
|
|
|
+ if len(value.encode("utf-8")) > _MAX_STRUCTURED_ARGUMENT_BYTES:
|
|
|
+ raise ValueError(f"{label} exceeds the byte limit")
|
|
|
+ try:
|
|
|
+ value = json.loads(value)
|
|
|
+ except json.JSONDecodeError as exc:
|
|
|
+ raise ValueError(f"{label} must contain valid JSON") from exc
|
|
|
+ try:
|
|
|
+ encoded = json.dumps(value, ensure_ascii=False, separators=(",", ":"))
|
|
|
+ except (TypeError, ValueError) as exc:
|
|
|
+ raise ValueError(f"{label} must be JSON-serializable") from exc
|
|
|
+ if len(encoded.encode("utf-8")) > _MAX_STRUCTURED_ARGUMENT_BYTES:
|
|
|
+ raise ValueError(f"{label} exceeds the byte limit")
|
|
|
+ return value
|
|
|
+
|
|
|
+
|
|
|
def _validated_images(
|
|
|
values: Any,
|
|
|
) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|