|
@@ -59,26 +59,29 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
result_summary=result_json,
|
|
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."""
|
|
|
|
|
-
|
|
|
|
|
- return _json(await gateway.read_input_snapshot(context or {}))
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, read_input_snapshot, capabilities=["read"])
|
|
|
|
|
-
|
|
|
|
|
- async def load_frozen_strategy(strategy_ref: str, context: dict[str, Any] | None = None) -> str:
|
|
|
|
|
- """Load an exact on-demand strategy URI copied from read_input_snapshot; never guess it."""
|
|
|
|
|
-
|
|
|
|
|
- return _json(await gateway.load_frozen_strategy(strategy_ref, context or {}))
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, load_frozen_strategy, capabilities=["read"])
|
|
|
|
|
-
|
|
|
|
|
- async def read_accepted_artifact(context: dict[str, Any] | None = None) -> str:
|
|
|
|
|
- """Read only direct-child artifacts frozen into this attempt."""
|
|
|
|
|
|
|
+ async def load_frozen_strategy(
|
|
|
|
|
+ strategy_handle: str,
|
|
|
|
|
+ cursor: int = 0,
|
|
|
|
|
+ context: dict[str, Any] | None = None,
|
|
|
|
|
+ ) -> ToolResult:
|
|
|
|
|
+ """Load one on-demand strategy selected by its Workbench handle."""
|
|
|
|
|
|
|
|
- return _json(await gateway.read_accepted_artifacts(context or {}))
|
|
|
|
|
|
|
+ value = _json(
|
|
|
|
|
+ await gateway.load_frozen_strategy(strategy_handle, context or {}, cursor=cursor)
|
|
|
|
|
+ )
|
|
|
|
|
+ return ToolResult(
|
|
|
|
|
+ title="Frozen strategy",
|
|
|
|
|
+ output=value,
|
|
|
|
|
+ long_term_memory=f"Loaded frozen strategy {strategy_handle}",
|
|
|
|
|
+ include_output_only_once=True,
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
- _register(registry, read_accepted_artifact, capabilities=["read"])
|
|
|
|
|
|
|
+ _register(
|
|
|
|
|
+ registry,
|
|
|
|
|
+ load_frozen_strategy,
|
|
|
|
|
+ capabilities=["read"],
|
|
|
|
|
+ schema=_load_frozen_strategy_schema(),
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
async def plan_script_tasks(
|
|
async def plan_script_tasks(
|
|
|
contracts: list[dict[str, Any]],
|
|
contracts: list[dict[str, Any]],
|
|
@@ -164,14 +167,56 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
schema=_decide_script_task_schema(),
|
|
schema=_decide_script_task_schema(),
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- async def inspect_script_plan(
|
|
|
|
|
|
|
+ async def read_workbench_detail(
|
|
|
|
|
+ view: str,
|
|
|
|
|
+ handle: str | None = None,
|
|
|
|
|
+ known_revision: str | None = None,
|
|
|
|
|
+ cursor: int = 0,
|
|
|
context: dict[str, Any] | None = None,
|
|
context: dict[str, Any] | None = None,
|
|
|
- ) -> str:
|
|
|
|
|
- """Inspect Tasks; copy complete accepted_decision_ref objects into new contracts."""
|
|
|
|
|
|
|
+ ) -> ToolResult:
|
|
|
|
|
+ """Read one bounded, revision-aware Workbench detail page."""
|
|
|
|
|
|
|
|
- return _json(await gateway.inspect_script_plan(context=context or {}))
|
|
|
|
|
|
|
+ value = await gateway.read_workbench_detail(
|
|
|
|
|
+ view=view,
|
|
|
|
|
+ handle=handle,
|
|
|
|
|
+ known_revision=known_revision,
|
|
|
|
|
+ cursor=cursor,
|
|
|
|
|
+ context=context or {},
|
|
|
|
|
+ )
|
|
|
|
|
+ output = _json(value)
|
|
|
|
|
+ return ToolResult(
|
|
|
|
|
+ title="Mission Workbench detail",
|
|
|
|
|
+ output=output,
|
|
|
|
|
+ long_term_memory=(
|
|
|
|
|
+ f"Workbench {view} revision={value.get('state_revision')} "
|
|
|
|
|
+ f"items={len(value.get('items', []))}"
|
|
|
|
|
+ ),
|
|
|
|
|
+ include_output_only_once=True,
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
- _register(registry, inspect_script_plan, capabilities=["read"])
|
|
|
|
|
|
|
+ _register(
|
|
|
|
|
+ registry,
|
|
|
|
|
+ read_workbench_detail,
|
|
|
|
|
+ capabilities=["read"],
|
|
|
|
|
+ schema={
|
|
|
|
|
+ "type": "function",
|
|
|
|
|
+ "function": {
|
|
|
|
|
+ "name": "read_workbench_detail",
|
|
|
|
|
+ "description": "Read a bounded page omitted from the automatic Mission context.",
|
|
|
|
|
+ "parameters": {
|
|
|
|
|
+ "type": "object",
|
|
|
|
|
+ "properties": {
|
|
|
|
|
+ "view": {"type": "string", "enum": ["task_tree", "source"]},
|
|
|
|
|
+ "handle": {"type": ["string", "null"]},
|
|
|
|
|
+ "known_revision": {"type": ["string", "null"]},
|
|
|
|
|
+ "cursor": {"type": "integer", "minimum": 0, "default": 0},
|
|
|
|
|
+ },
|
|
|
|
|
+ "required": ["view"],
|
|
|
|
|
+ "additionalProperties": False,
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
async def dispatch_script_tasks(
|
|
async def dispatch_script_tasks(
|
|
|
task_ids: list[str], context: dict[str, Any] | None = None
|
|
task_ids: list[str], context: dict[str, Any] | None = None
|
|
@@ -311,29 +356,48 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
async def save_direction_candidate(
|
|
async def save_direction_candidate(
|
|
|
- goals: list[dict[str, Any]],
|
|
|
|
|
- constraints: list[dict[str, Any]],
|
|
|
|
|
- preferences: list[dict[str, Any]],
|
|
|
|
|
- topic_refs: list[str] | None = None,
|
|
|
|
|
- persona_refs: list[str] | None = None,
|
|
|
|
|
- strategy_refs: list[str] | None = None,
|
|
|
|
|
- evidence_refs: list[str] | None = None,
|
|
|
|
|
|
|
+ goals: Any,
|
|
|
|
|
+ constraints: Any = None,
|
|
|
|
|
+ preferences: Any = None,
|
|
|
|
|
+ strategy_handles: list[str] | None = None,
|
|
|
|
|
+ evidence_decision_ids: list[str] | None = None,
|
|
|
context: dict[str, Any] | None = None,
|
|
context: dict[str, Any] | None = None,
|
|
|
) -> str:
|
|
) -> str:
|
|
|
"""Freeze one direction candidate inside the protected Attempt."""
|
|
"""Freeze one direction candidate inside the protected Attempt."""
|
|
|
|
|
|
|
|
|
|
+ normalized = _decode_bounded_structured_value(goals, "goals")
|
|
|
|
|
+ if isinstance(normalized, dict) and "goals" in normalized:
|
|
|
|
|
+ envelope = normalized
|
|
|
|
|
+ goals = envelope["goals"]
|
|
|
|
|
+ constraints = envelope.get("constraints", constraints)
|
|
|
|
|
+ preferences = envelope.get("preferences", preferences)
|
|
|
|
|
+ strategy_handles = envelope.get("strategy_handles", strategy_handles)
|
|
|
|
|
+ evidence_decision_ids = envelope.get("evidence_decision_ids", evidence_decision_ids)
|
|
|
goals = _structured_object_list(goals, "goals")
|
|
goals = _structured_object_list(goals, "goals")
|
|
|
constraints = _structured_object_list(constraints, "constraints")
|
|
constraints = _structured_object_list(constraints, "constraints")
|
|
|
preferences = _structured_object_list(preferences, "preferences")
|
|
preferences = _structured_object_list(preferences, "preferences")
|
|
|
|
|
+ _require_object_fields(
|
|
|
|
|
+ goals,
|
|
|
|
|
+ ("client_key", "statement", "rationale", "success_criteria"),
|
|
|
|
|
+ "goals",
|
|
|
|
|
+ )
|
|
|
|
|
+ _require_object_fields(
|
|
|
|
|
+ constraints,
|
|
|
|
|
+ ("client_key", "statement", "rationale"),
|
|
|
|
|
+ "constraints",
|
|
|
|
|
+ )
|
|
|
|
|
+ _require_object_fields(
|
|
|
|
|
+ preferences,
|
|
|
|
|
+ ("client_key", "statement", "rationale"),
|
|
|
|
|
+ "preferences",
|
|
|
|
|
+ )
|
|
|
return _json(
|
|
return _json(
|
|
|
await gateway.save_direction_candidate(
|
|
await gateway.save_direction_candidate(
|
|
|
goals=goals,
|
|
goals=goals,
|
|
|
constraints=constraints,
|
|
constraints=constraints,
|
|
|
preferences=preferences,
|
|
preferences=preferences,
|
|
|
- topic_refs=topic_refs or [],
|
|
|
|
|
- persona_refs=persona_refs or [],
|
|
|
|
|
- strategy_refs=strategy_refs or [],
|
|
|
|
|
- evidence_refs=evidence_refs or [],
|
|
|
|
|
|
|
+ strategy_handles=strategy_handles or [],
|
|
|
|
|
+ evidence_decision_ids=evidence_decision_ids or [],
|
|
|
context=context or {},
|
|
context=context or {},
|
|
|
)
|
|
)
|
|
|
)
|
|
)
|
|
@@ -345,36 +409,13 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
schema=_save_direction_candidate_schema(),
|
|
schema=_save_direction_candidate_schema(),
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- async def read_accepted_input_bundle(
|
|
|
|
|
- context: dict[str, Any] | None = None,
|
|
|
|
|
- ) -> str:
|
|
|
|
|
- """Read the ordered, immutable direct and explicitly imported inputs."""
|
|
|
|
|
-
|
|
|
|
|
- return _json(await gateway.read_accepted_input_bundle(context or {}))
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, read_accepted_input_bundle, capabilities=["read"])
|
|
|
|
|
-
|
|
|
|
|
- async def read_active_frontier(context: dict[str, Any] | None = None) -> str:
|
|
|
|
|
- """Read only adopted, non-superseded candidate inputs pinned by Compose."""
|
|
|
|
|
-
|
|
|
|
|
- return _json(await gateway.read_active_frontier(context or {}))
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, read_active_frontier, capabilities=["read"])
|
|
|
|
|
-
|
|
|
|
|
- async def read_pinned_candidates(context: dict[str, Any] | None = None) -> str:
|
|
|
|
|
- """Read only immutable candidates explicitly pinned for Compare or Portfolio."""
|
|
|
|
|
-
|
|
|
|
|
- return _json(await gateway.read_pinned_candidates(context or {}))
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, read_pinned_candidates, capabilities=["read"])
|
|
|
|
|
-
|
|
|
|
|
async def view_frozen_images(
|
|
async def view_frozen_images(
|
|
|
- raw_artifact_refs: list[str],
|
|
|
|
|
|
|
+ image_handles: list[str],
|
|
|
context: dict[str, Any] | None = None,
|
|
context: dict[str, Any] | None = None,
|
|
|
) -> ToolResult:
|
|
) -> ToolResult:
|
|
|
"""Load verified bytes from frozen raw artifacts, never from network URLs."""
|
|
"""Load verified bytes from frozen raw artifacts, never from network URLs."""
|
|
|
|
|
|
|
|
- values = await gateway.view_frozen_images(raw_artifact_refs, context or {})
|
|
|
|
|
|
|
+ values = await gateway.view_frozen_images(image_handles, context or {})
|
|
|
images, manifest = _validated_images(values)
|
|
images, manifest = _validated_images(values)
|
|
|
output = _json({"images": manifest})
|
|
output = _json({"images": manifest})
|
|
|
return ToolResult(
|
|
return ToolResult(
|
|
@@ -387,210 +428,63 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
|
|
|
|
|
_register(registry, view_frozen_images, capabilities=["read"])
|
|
_register(registry, view_frozen_images, capabilities=["read"])
|
|
|
|
|
|
|
|
- async def read_attempt_workspace(context: dict[str, Any] | None = None) -> str:
|
|
|
|
|
- """Read the current Attempt's isolated Paragraph/Element/Link workspace."""
|
|
|
|
|
-
|
|
|
|
|
- return _json(await gateway.read_attempt_workspace(context or {}))
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, read_attempt_workspace, capabilities=["read"])
|
|
|
|
|
-
|
|
|
|
|
- async def create_script_paragraph(
|
|
|
|
|
- paragraph_index: int,
|
|
|
|
|
- name: str,
|
|
|
|
|
- content_range: dict[str, Any],
|
|
|
|
|
- level: int = 1,
|
|
|
|
|
- parent_paragraph_id: int | None = None,
|
|
|
|
|
- theme_elements: list[dict[str, Any]] | None = None,
|
|
|
|
|
- form_elements: list[dict[str, Any]] | None = None,
|
|
|
|
|
- function_elements: list[dict[str, Any]] | None = None,
|
|
|
|
|
- feeling_elements: list[dict[str, Any]] | None = None,
|
|
|
|
|
- context: dict[str, Any] | None = None,
|
|
|
|
|
- ) -> 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",
|
|
|
|
|
- {
|
|
|
|
|
- "paragraph_index": paragraph_index,
|
|
|
|
|
- "name": name,
|
|
|
|
|
- "content_range": content_range,
|
|
|
|
|
- "level": level,
|
|
|
|
|
- "parent_paragraph_id": parent_paragraph_id,
|
|
|
|
|
- "theme_elements": theme_elements or [],
|
|
|
|
|
- "form_elements": form_elements or [],
|
|
|
|
|
- "function_elements": function_elements or [],
|
|
|
|
|
- "feeling_elements": feeling_elements or [],
|
|
|
|
|
- },
|
|
|
|
|
- context or {},
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, create_script_paragraph, capabilities=["write"])
|
|
|
|
|
-
|
|
|
|
|
- async def create_script_paragraphs(
|
|
|
|
|
|
|
+ async def save_script_paragraphs(
|
|
|
paragraphs: list[dict[str, Any]],
|
|
paragraphs: list[dict[str, Any]],
|
|
|
|
|
+ expected_state_revision: str,
|
|
|
context: dict[str, Any] | None = None,
|
|
context: dict[str, Any] | None = None,
|
|
|
) -> str:
|
|
) -> str:
|
|
|
- """Atomically create a parent-linked Paragraph batch using client keys."""
|
|
|
|
|
-
|
|
|
|
|
- return _json(
|
|
|
|
|
- await gateway.candidate_command(
|
|
|
|
|
- "create_script_paragraphs",
|
|
|
|
|
- [_structured_object(item, "paragraph") for item in paragraphs],
|
|
|
|
|
- context or {},
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, create_script_paragraphs, capabilities=["write"])
|
|
|
|
|
-
|
|
|
|
|
- async def append_paragraph_atoms(
|
|
|
|
|
- paragraph_id: int,
|
|
|
|
|
- column: str,
|
|
|
|
|
- atoms: list[dict[str, Any]],
|
|
|
|
|
- context: dict[str, Any] | None = None,
|
|
|
|
|
- ) -> str:
|
|
|
|
|
- """Append unique atoms to one allowlisted Paragraph column."""
|
|
|
|
|
|
|
+ """Atomically upsert Paragraphs using semantic client or target keys."""
|
|
|
|
|
|
|
|
- atoms = _structured_object_list(atoms, "atoms")
|
|
|
|
|
return _json(
|
|
return _json(
|
|
|
await gateway.candidate_command(
|
|
await gateway.candidate_command(
|
|
|
- "append_paragraph_atoms",
|
|
|
|
|
- {"paragraph_id": paragraph_id, "column": column, "atoms": atoms},
|
|
|
|
|
- context or {},
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, append_paragraph_atoms, capabilities=["write"])
|
|
|
|
|
-
|
|
|
|
|
- async def delete_paragraph_atom(
|
|
|
|
|
- paragraph_id: int,
|
|
|
|
|
- column: str,
|
|
|
|
|
- dimension: str,
|
|
|
|
|
- atom_value: str,
|
|
|
|
|
- context: dict[str, Any] | None = None,
|
|
|
|
|
- ) -> str:
|
|
|
|
|
- """Delete one exact atom from the current Paragraph workspace."""
|
|
|
|
|
-
|
|
|
|
|
- return _json(
|
|
|
|
|
- await gateway.candidate_command(
|
|
|
|
|
- "delete_paragraph_atom",
|
|
|
|
|
|
|
+ "save_script_paragraphs",
|
|
|
{
|
|
{
|
|
|
- "paragraph_id": paragraph_id,
|
|
|
|
|
- "column": column,
|
|
|
|
|
- "dimension": dimension,
|
|
|
|
|
- "atom_value": atom_value,
|
|
|
|
|
|
|
+ "paragraphs": [_structured_object(item, "paragraph") for item in paragraphs],
|
|
|
|
|
+ "expected_state_revision": expected_state_revision,
|
|
|
},
|
|
},
|
|
|
context or {},
|
|
context or {},
|
|
|
)
|
|
)
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- _register(registry, delete_paragraph_atom, capabilities=["write"])
|
|
|
|
|
|
|
+ _register(
|
|
|
|
|
+ registry,
|
|
|
|
|
+ save_script_paragraphs,
|
|
|
|
|
+ capabilities=["write"],
|
|
|
|
|
+ schema=_save_script_paragraphs_schema(),
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
- async def batch_update_script_paragraphs(
|
|
|
|
|
- updates: list[dict[str, Any]],
|
|
|
|
|
|
|
+ async def save_script_elements(
|
|
|
|
|
+ elements: list[dict[str, Any]],
|
|
|
|
|
+ links: list[dict[str, Any]],
|
|
|
|
|
+ expected_state_revision: str,
|
|
|
context: dict[str, Any] | None = None,
|
|
context: dict[str, Any] | None = None,
|
|
|
) -> str:
|
|
) -> 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 {}
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, batch_update_script_paragraphs, capabilities=["write"])
|
|
|
|
|
|
|
+ """Atomically save Elements and link them to semantic Paragraph targets."""
|
|
|
|
|
|
|
|
- async def create_script_element(
|
|
|
|
|
- name: str,
|
|
|
|
|
- dimension_primary: str,
|
|
|
|
|
- dimension_secondary: str,
|
|
|
|
|
- commonality_analysis: dict[str, Any] | None = None,
|
|
|
|
|
- topic_support: dict[str, Any] | None = None,
|
|
|
|
|
- weight_score: dict[str, Any] | None = None,
|
|
|
|
|
- support_elements: list[dict[str, Any]] | None = None,
|
|
|
|
|
- context: dict[str, Any] | None = None,
|
|
|
|
|
- ) -> 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")
|
|
|
|
|
|
|
+ elements = _structured_object_list(elements, "elements", max_items=100)
|
|
|
|
|
+ links = _structured_object_list(links, "links", max_items=100)
|
|
|
return _json(
|
|
return _json(
|
|
|
await gateway.candidate_command(
|
|
await gateway.candidate_command(
|
|
|
- "create_script_element",
|
|
|
|
|
|
|
+ "save_script_elements",
|
|
|
{
|
|
{
|
|
|
- "name": name,
|
|
|
|
|
- "dimension_primary": dimension_primary,
|
|
|
|
|
- "dimension_secondary": dimension_secondary,
|
|
|
|
|
- "commonality_analysis": commonality_analysis,
|
|
|
|
|
- "topic_support": topic_support,
|
|
|
|
|
- "weight_score": weight_score,
|
|
|
|
|
- "support_elements": support_elements or [],
|
|
|
|
|
|
|
+ "elements": elements,
|
|
|
|
|
+ "links": links,
|
|
|
|
|
+ "expected_state_revision": expected_state_revision,
|
|
|
},
|
|
},
|
|
|
context or {},
|
|
context or {},
|
|
|
)
|
|
)
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- _register(registry, create_script_element, capabilities=["write"])
|
|
|
|
|
-
|
|
|
|
|
- async def update_script_element(
|
|
|
|
|
- element_id: int,
|
|
|
|
|
- updates: dict[str, Any],
|
|
|
|
|
- context: dict[str, Any] | None = None,
|
|
|
|
|
- ) -> str:
|
|
|
|
|
- """Update one Element with an allowlisted, workspace-local patch."""
|
|
|
|
|
-
|
|
|
|
|
- updates = _structured_object(updates, "updates")
|
|
|
|
|
- return _json(
|
|
|
|
|
- await gateway.candidate_command(
|
|
|
|
|
- "update_script_element",
|
|
|
|
|
- {"element_id": element_id, "updates": updates},
|
|
|
|
|
- context or {},
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, update_script_element, capabilities=["write"])
|
|
|
|
|
-
|
|
|
|
|
- async def batch_link_paragraph_elements(
|
|
|
|
|
- links: list[dict[str, Any]],
|
|
|
|
|
- context: dict[str, Any] | None = None,
|
|
|
|
|
- ) -> 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 {})
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, batch_link_paragraph_elements, capabilities=["write"])
|
|
|
|
|
-
|
|
|
|
|
- async def delete_paragraph_element_links(
|
|
|
|
|
- links: list[dict[str, Any]],
|
|
|
|
|
- context: dict[str, Any] | None = None,
|
|
|
|
|
- ) -> 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 {})
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, delete_paragraph_element_links, capabilities=["write"])
|
|
|
|
|
|
|
+ _register(
|
|
|
|
|
+ registry,
|
|
|
|
|
+ save_script_elements,
|
|
|
|
|
+ capabilities=["write"],
|
|
|
|
|
+ schema=_save_script_elements_schema(),
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
async def save_comparison_candidate(
|
|
async def save_comparison_candidate(
|
|
|
criterion_results: list[dict[str, Any]],
|
|
criterion_results: list[dict[str, Any]],
|
|
|
- recommendation: str,
|
|
|
|
|
|
|
+ recommended_decision_id: str,
|
|
|
conflicts: list[str] | None = None,
|
|
conflicts: list[str] | None = None,
|
|
|
context: dict[str, Any] | None = None,
|
|
context: dict[str, Any] | None = None,
|
|
|
) -> str:
|
|
) -> str:
|
|
@@ -603,7 +497,7 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
{
|
|
{
|
|
|
"criterion_results": criterion_results,
|
|
"criterion_results": criterion_results,
|
|
|
"conflicts": conflicts or [],
|
|
"conflicts": conflicts or [],
|
|
|
- "recommendation": recommendation,
|
|
|
|
|
|
|
+ "recommended_decision_id": recommended_decision_id,
|
|
|
},
|
|
},
|
|
|
context or {},
|
|
context or {},
|
|
|
)
|
|
)
|
|
@@ -611,46 +505,6 @@ def register_script_tools(registry: ToolRegistry, gateway: LegacyScriptToolGatew
|
|
|
|
|
|
|
|
_register(registry, save_comparison_candidate, capabilities=["write"])
|
|
_register(registry, save_comparison_candidate, capabilities=["write"])
|
|
|
|
|
|
|
|
- async def save_structured_script_candidate(
|
|
|
|
|
- acceptance_notes: list[str] | None = None,
|
|
|
|
|
- context: dict[str, Any] | None = None,
|
|
|
|
|
- ) -> str:
|
|
|
|
|
- """Materialize and freeze exactly one StructuredScript from active frontier."""
|
|
|
|
|
-
|
|
|
|
|
- return _json(
|
|
|
|
|
- await gateway.save_structured_script_candidate(acceptance_notes or [], context or {})
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, save_structured_script_candidate, capabilities=["write"])
|
|
|
|
|
-
|
|
|
|
|
- async def save_candidate_portfolio(
|
|
|
|
|
- unresolved_defects: list[dict[str, Any]],
|
|
|
|
|
- superseded_decision_ids: list[str] | None = None,
|
|
|
|
|
- context: dict[str, Any] | None = None,
|
|
|
|
|
- ) -> 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",
|
|
|
|
|
- {
|
|
|
|
|
- "superseded_decision_ids": superseded_decision_ids or [],
|
|
|
|
|
- "unresolved_defects": unresolved_defects,
|
|
|
|
|
- },
|
|
|
|
|
- context or {},
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, save_candidate_portfolio, capabilities=["write"])
|
|
|
|
|
-
|
|
|
|
|
- async def read_accepted_portfolio(context: dict[str, Any] | None = None) -> str:
|
|
|
|
|
- """Read the Root Attempt's accepted Direction, Portfolio and adopted script."""
|
|
|
|
|
-
|
|
|
|
|
- return _json(await gateway.read_accepted_portfolio(context or {}))
|
|
|
|
|
-
|
|
|
|
|
- _register(registry, read_accepted_portfolio, capabilities=["read"])
|
|
|
|
|
-
|
|
|
|
|
async def legacy_projection_dry_run(
|
|
async def legacy_projection_dry_run(
|
|
|
build_summary: str,
|
|
build_summary: str,
|
|
|
context: dict[str, Any] | None = None,
|
|
context: dict[str, Any] | None = None,
|
|
@@ -810,9 +664,7 @@ def _strongest_cycle_failure(results: Any) -> FailureDetail | None:
|
|
|
strongest: FailureDetail | None = None
|
|
strongest: FailureDetail | None = None
|
|
|
for result in results:
|
|
for result in results:
|
|
|
value = (
|
|
value = (
|
|
|
- result.get("failure")
|
|
|
|
|
- if isinstance(result, dict)
|
|
|
|
|
- else getattr(result, "failure", None)
|
|
|
|
|
|
|
+ result.get("failure") if isinstance(result, dict) else getattr(result, "failure", None)
|
|
|
)
|
|
)
|
|
|
if value is None:
|
|
if value is None:
|
|
|
continue
|
|
continue
|
|
@@ -833,6 +685,18 @@ def _strongest_cycle_failure(results: Any) -> FailureDetail | None:
|
|
|
source_tool=failure.source_tool,
|
|
source_tool=failure.source_tool,
|
|
|
details=details,
|
|
details=details,
|
|
|
)
|
|
)
|
|
|
|
|
+ if failure.code == "NO_PROGRESS" and failure.disposition is FailureDisposition.ABORT_RUN:
|
|
|
|
|
+ failure = FailureDetail(
|
|
|
|
|
+ code=failure.code,
|
|
|
|
|
+ message=failure.message,
|
|
|
|
|
+ disposition=FailureDisposition.REPLAN_TASK,
|
|
|
|
|
+ source_tool="dispatch_script_tasks",
|
|
|
|
|
+ details={
|
|
|
|
|
+ **details,
|
|
|
|
|
+ "child_disposition": FailureDisposition.ABORT_RUN.value,
|
|
|
|
|
+ "child_source_tool": failure.source_tool,
|
|
|
|
|
+ },
|
|
|
|
|
+ )
|
|
|
if strongest is None or priority[failure.disposition] > priority[strongest.disposition]:
|
|
if strongest is None or priority[failure.disposition] > priority[strongest.disposition]:
|
|
|
strongest = failure
|
|
strongest = failure
|
|
|
return strongest
|
|
return strongest
|
|
@@ -976,9 +840,18 @@ def _planner_task_input_schema() -> dict[str, Any]:
|
|
|
"type": "object",
|
|
"type": "object",
|
|
|
"properties": {
|
|
"properties": {
|
|
|
"task_kind": {"type": "string", "enum": task_kinds},
|
|
"task_kind": {"type": "string", "enum": task_kinds},
|
|
|
- "scope_ref": {
|
|
|
|
|
- "type": "string",
|
|
|
|
|
- "description": "Stable logical scope URI beginning with script-build://.",
|
|
|
|
|
|
|
+ "scope_selector": {
|
|
|
|
|
+ "type": "object",
|
|
|
|
|
+ "properties": {
|
|
|
|
|
+ "anchor": {"type": "string", "enum": ["mission", "parent"]},
|
|
|
|
|
+ "path": {
|
|
|
|
|
+ "type": "array",
|
|
|
|
|
+ "maxItems": 4,
|
|
|
|
|
+ "items": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ "required": ["anchor", "path"],
|
|
|
|
|
+ "additionalProperties": False,
|
|
|
},
|
|
},
|
|
|
"intent_class": {
|
|
"intent_class": {
|
|
|
"type": "string",
|
|
"type": "string",
|
|
@@ -1012,19 +885,19 @@ def _planner_task_input_schema() -> dict[str, Any]:
|
|
|
"uniqueItems": True,
|
|
"uniqueItems": True,
|
|
|
"default": [],
|
|
"default": [],
|
|
|
},
|
|
},
|
|
|
- "gap_ref": {"type": ["string", "null"]},
|
|
|
|
|
|
|
+ "gap_key": {"type": ["string", "null"]},
|
|
|
"criteria": {
|
|
"criteria": {
|
|
|
"type": "array",
|
|
"type": "array",
|
|
|
- "minItems": 1,
|
|
|
|
|
|
|
+ "minItems": 0,
|
|
|
"maxItems": 16,
|
|
"maxItems": 16,
|
|
|
"items": {
|
|
"items": {
|
|
|
"type": "object",
|
|
"type": "object",
|
|
|
"properties": {
|
|
"properties": {
|
|
|
- "criterion_id": {"type": "string"},
|
|
|
|
|
|
|
+ "client_key": {"type": "string"},
|
|
|
"description": {"type": "string"},
|
|
"description": {"type": "string"},
|
|
|
"hard": {"type": "boolean"},
|
|
"hard": {"type": "boolean"},
|
|
|
},
|
|
},
|
|
|
- "required": ["criterion_id", "description", "hard"],
|
|
|
|
|
|
|
+ "required": ["client_key", "description", "hard"],
|
|
|
"additionalProperties": False,
|
|
"additionalProperties": False,
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
@@ -1041,7 +914,7 @@ def _planner_task_input_schema() -> dict[str, Any]:
|
|
|
},
|
|
},
|
|
|
"required": [
|
|
"required": [
|
|
|
"task_kind",
|
|
"task_kind",
|
|
|
- "scope_ref",
|
|
|
|
|
|
|
+ "scope_selector",
|
|
|
"intent_class",
|
|
"intent_class",
|
|
|
"objective",
|
|
"objective",
|
|
|
"criteria",
|
|
"criteria",
|
|
@@ -1057,10 +930,8 @@ def _root_replacement_schema() -> dict[str, Any]:
|
|
|
"type": "object",
|
|
"type": "object",
|
|
|
"properties": {
|
|
"properties": {
|
|
|
"objective": {"type": "string", "maxLength": 2000},
|
|
"objective": {"type": "string", "maxLength": 2000},
|
|
|
- "acceptance_criteria": {"type": "array", "items": {"type": "object"}},
|
|
|
|
|
- "context_refs": {"type": "array", "items": {"type": "string"}},
|
|
|
|
|
},
|
|
},
|
|
|
- "required": ["objective", "acceptance_criteria", "context_refs"],
|
|
|
|
|
|
|
+ "required": ["objective"],
|
|
|
"additionalProperties": False,
|
|
"additionalProperties": False,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1069,22 +940,22 @@ def _save_direction_candidate_schema() -> dict[str, Any]:
|
|
|
constraint = {
|
|
constraint = {
|
|
|
"type": "object",
|
|
"type": "object",
|
|
|
"properties": {
|
|
"properties": {
|
|
|
- "constraint_id": {"type": "string", "minLength": 1},
|
|
|
|
|
|
|
+ "client_key": {"type": "string", "minLength": 1},
|
|
|
"statement": {"type": "string", "minLength": 1},
|
|
"statement": {"type": "string", "minLength": 1},
|
|
|
"rationale": {"type": "string"},
|
|
"rationale": {"type": "string"},
|
|
|
},
|
|
},
|
|
|
- "required": ["constraint_id", "statement", "rationale"],
|
|
|
|
|
|
|
+ "required": ["client_key", "statement", "rationale"],
|
|
|
"additionalProperties": False,
|
|
"additionalProperties": False,
|
|
|
}
|
|
}
|
|
|
preference = {
|
|
preference = {
|
|
|
"type": "object",
|
|
"type": "object",
|
|
|
"properties": {
|
|
"properties": {
|
|
|
- "preference_id": {"type": "string", "minLength": 1},
|
|
|
|
|
|
|
+ "client_key": {"type": "string", "minLength": 1},
|
|
|
"statement": {"type": "string", "minLength": 1},
|
|
"statement": {"type": "string", "minLength": 1},
|
|
|
"rationale": {"type": "string"},
|
|
"rationale": {"type": "string"},
|
|
|
"priority": {"type": "integer", "minimum": 1, "maximum": 3},
|
|
"priority": {"type": "integer", "minimum": 1, "maximum": 3},
|
|
|
},
|
|
},
|
|
|
- "required": ["preference_id", "statement", "rationale"],
|
|
|
|
|
|
|
+ "required": ["client_key", "statement", "rationale"],
|
|
|
"additionalProperties": False,
|
|
"additionalProperties": False,
|
|
|
}
|
|
}
|
|
|
return {
|
|
return {
|
|
@@ -1092,8 +963,7 @@ def _save_direction_candidate_schema() -> dict[str, Any]:
|
|
|
"function": {
|
|
"function": {
|
|
|
"name": "save_direction_candidate",
|
|
"name": "save_direction_candidate",
|
|
|
"description": (
|
|
"description": (
|
|
|
- "Freeze one Direction. evidence_refs must be exact artifact_ref.uri values "
|
|
|
|
|
- "from accepted retrieval children; never invent a reference."
|
|
|
|
|
|
|
+ "Freeze one Direction using semantic client keys and accepted Evidence decisions."
|
|
|
),
|
|
),
|
|
|
"parameters": {
|
|
"parameters": {
|
|
|
"type": "object",
|
|
"type": "object",
|
|
@@ -1131,24 +1001,203 @@ def _save_direction_candidate_schema() -> dict[str, Any]:
|
|
|
},
|
|
},
|
|
|
"constraints": {"type": "array", "items": constraint},
|
|
"constraints": {"type": "array", "items": constraint},
|
|
|
"preferences": {"type": "array", "items": preference},
|
|
"preferences": {"type": "array", "items": preference},
|
|
|
- "topic_refs": {"type": "array", "items": {"type": "string"}, "default": []},
|
|
|
|
|
- "persona_refs": {
|
|
|
|
|
|
|
+ "strategy_handles": {
|
|
|
"type": "array",
|
|
"type": "array",
|
|
|
"items": {"type": "string"},
|
|
"items": {"type": "string"},
|
|
|
"default": [],
|
|
"default": [],
|
|
|
},
|
|
},
|
|
|
- "strategy_refs": {
|
|
|
|
|
|
|
+ "evidence_decision_ids": {
|
|
|
"type": "array",
|
|
"type": "array",
|
|
|
|
|
+ "minItems": 1,
|
|
|
"items": {"type": "string"},
|
|
"items": {"type": "string"},
|
|
|
- "default": [],
|
|
|
|
|
},
|
|
},
|
|
|
- "evidence_refs": {
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ "required": [
|
|
|
|
|
+ "goals",
|
|
|
|
|
+ "constraints",
|
|
|
|
|
+ "preferences",
|
|
|
|
|
+ "evidence_decision_ids",
|
|
|
|
|
+ ],
|
|
|
|
|
+ "additionalProperties": False,
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _load_frozen_strategy_schema() -> dict[str, Any]:
|
|
|
|
|
+ return {
|
|
|
|
|
+ "type": "function",
|
|
|
|
|
+ "function": {
|
|
|
|
|
+ "name": "load_frozen_strategy",
|
|
|
|
|
+ "description": (
|
|
|
|
|
+ "Load one on-demand strategy using the exact Workbench strategy handle."
|
|
|
|
|
+ ),
|
|
|
|
|
+ "parameters": {
|
|
|
|
|
+ "type": "object",
|
|
|
|
|
+ "properties": {
|
|
|
|
|
+ "strategy_handle": {
|
|
|
|
|
+ "type": "string",
|
|
|
|
|
+ "pattern": "^strategy_[0-9a-f]{20}$",
|
|
|
|
|
+ },
|
|
|
|
|
+ "cursor": {"type": "integer", "minimum": 0, "default": 0},
|
|
|
|
|
+ },
|
|
|
|
|
+ "required": ["strategy_handle"],
|
|
|
|
|
+ "additionalProperties": False,
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _save_script_paragraphs_schema() -> dict[str, Any]:
|
|
|
|
|
+ creative_atom = {
|
|
|
|
|
+ "type": "object",
|
|
|
|
|
+ "properties": {
|
|
|
|
|
+ "原子点": {"type": "string", "minLength": 1},
|
|
|
|
|
+ "维度": {"type": "string", "minLength": 1},
|
|
|
|
|
+ "维度类型": {"type": "string", "enum": ["主维度", "从维度"]},
|
|
|
|
|
+ },
|
|
|
|
|
+ "required": ["原子点", "维度", "维度类型"],
|
|
|
|
|
+ "additionalProperties": False,
|
|
|
|
|
+ }
|
|
|
|
|
+ feeling_atom = {
|
|
|
|
|
+ "type": "object",
|
|
|
|
|
+ "properties": {
|
|
|
|
|
+ "原子点": {"type": "string", "minLength": 1},
|
|
|
|
|
+ "维度": {"type": "string", "minLength": 1},
|
|
|
|
|
+ },
|
|
|
|
|
+ "required": ["原子点", "维度"],
|
|
|
|
|
+ "additionalProperties": False,
|
|
|
|
|
+ }
|
|
|
|
|
+ paragraph = {
|
|
|
|
|
+ "type": "object",
|
|
|
|
|
+ "properties": {
|
|
|
|
|
+ "client_key": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
|
|
|
+ "paragraph_target_key": {
|
|
|
|
|
+ "type": "string",
|
|
|
|
|
+ "pattern": "^pt_[0-9a-f]{20}$",
|
|
|
|
|
+ },
|
|
|
|
|
+ "parent_client_key": {
|
|
|
|
|
+ "type": ["string", "null"],
|
|
|
|
|
+ "minLength": 1,
|
|
|
|
|
+ "maxLength": 128,
|
|
|
|
|
+ },
|
|
|
|
|
+ "parent_target_key": {
|
|
|
|
|
+ "type": ["string", "null"],
|
|
|
|
|
+ "pattern": "^pt_[0-9a-f]{20}$",
|
|
|
|
|
+ },
|
|
|
|
|
+ "paragraph_index": {"type": "integer", "minimum": 1},
|
|
|
|
|
+ "name": {"type": "string", "minLength": 1},
|
|
|
|
|
+ "content_range": {"type": "object"},
|
|
|
|
|
+ "level": {"type": "integer", "enum": [1, 2]},
|
|
|
|
|
+ "theme_elements": {"type": "array", "minItems": 1, "items": creative_atom},
|
|
|
|
|
+ "form_elements": {"type": "array", "minItems": 1, "items": creative_atom},
|
|
|
|
|
+ "function_elements": {
|
|
|
|
|
+ "type": "array",
|
|
|
|
|
+ "minItems": 1,
|
|
|
|
|
+ "items": creative_atom,
|
|
|
|
|
+ },
|
|
|
|
|
+ "feeling_elements": {"type": "array", "minItems": 1, "items": feeling_atom},
|
|
|
|
|
+ **{
|
|
|
|
|
+ field: {"type": "string", "minLength": 1}
|
|
|
|
|
+ for field in (
|
|
|
|
|
+ "theme",
|
|
|
|
|
+ "form",
|
|
|
|
|
+ "function",
|
|
|
|
|
+ "feeling",
|
|
|
|
|
+ "description",
|
|
|
|
|
+ "full_description",
|
|
|
|
|
+ )
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ "oneOf": [
|
|
|
|
|
+ {"required": ["client_key"], "not": {"required": ["paragraph_target_key"]}},
|
|
|
|
|
+ {"required": ["paragraph_target_key"], "not": {"required": ["client_key"]}},
|
|
|
|
|
+ ],
|
|
|
|
|
+ "additionalProperties": False,
|
|
|
|
|
+ }
|
|
|
|
|
+ return {
|
|
|
|
|
+ "type": "function",
|
|
|
|
|
+ "function": {
|
|
|
|
|
+ "name": "save_script_paragraphs",
|
|
|
|
|
+ "description": (
|
|
|
|
|
+ "Atomically create or update Paragraphs. New rows use client_key; existing "
|
|
|
|
|
+ "rows use paragraph_target_key from the protected role context."
|
|
|
|
|
+ ),
|
|
|
|
|
+ "parameters": {
|
|
|
|
|
+ "type": "object",
|
|
|
|
|
+ "properties": {
|
|
|
|
|
+ "paragraphs": {
|
|
|
"type": "array",
|
|
"type": "array",
|
|
|
"minItems": 1,
|
|
"minItems": 1,
|
|
|
- "items": {"type": "string"},
|
|
|
|
|
|
|
+ "items": paragraph,
|
|
|
},
|
|
},
|
|
|
|
|
+ "expected_state_revision": {"type": "string", "minLength": 1},
|
|
|
|
|
+ },
|
|
|
|
|
+ "required": ["paragraphs", "expected_state_revision"],
|
|
|
|
|
+ "additionalProperties": False,
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _save_script_elements_schema() -> dict[str, Any]:
|
|
|
|
|
+ element = {
|
|
|
|
|
+ "type": "object",
|
|
|
|
|
+ "properties": {
|
|
|
|
|
+ "client_key": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
|
|
|
+ "name": {"type": "string", "minLength": 1},
|
|
|
|
|
+ "dimension_primary": {"type": "string", "enum": ["实质", "形式"]},
|
|
|
|
|
+ "dimension_secondary": {"type": "string", "minLength": 1},
|
|
|
|
|
+ "commonality_analysis": {"type": ["object", "null"]},
|
|
|
|
|
+ "topic_support": {"type": ["object", "null"]},
|
|
|
|
|
+ "weight_score": {"type": ["object", "null"]},
|
|
|
|
|
+ "support_elements": {
|
|
|
|
|
+ "type": "array",
|
|
|
|
|
+ "items": {"type": "object"},
|
|
|
|
|
+ "default": [],
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ "required": [
|
|
|
|
|
+ "client_key",
|
|
|
|
|
+ "name",
|
|
|
|
|
+ "dimension_primary",
|
|
|
|
|
+ "dimension_secondary",
|
|
|
|
|
+ ],
|
|
|
|
|
+ "additionalProperties": False,
|
|
|
|
|
+ }
|
|
|
|
|
+ link = {
|
|
|
|
|
+ "type": "object",
|
|
|
|
|
+ "properties": {
|
|
|
|
|
+ "paragraph_target_key": {
|
|
|
|
|
+ "type": "string",
|
|
|
|
|
+ "pattern": "^pt_[0-9a-f]{20}$",
|
|
|
|
|
+ },
|
|
|
|
|
+ "element_client_keys": {
|
|
|
|
|
+ "type": "array",
|
|
|
|
|
+ "minItems": 1,
|
|
|
|
|
+ "uniqueItems": True,
|
|
|
|
|
+ "items": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ "required": ["paragraph_target_key", "element_client_keys"],
|
|
|
|
|
+ "additionalProperties": False,
|
|
|
|
|
+ }
|
|
|
|
|
+ return {
|
|
|
|
|
+ "type": "function",
|
|
|
|
|
+ "function": {
|
|
|
|
|
+ "name": "save_script_elements",
|
|
|
|
|
+ "description": (
|
|
|
|
|
+ "Atomically create every Element and all Paragraph links in one call. "
|
|
|
|
|
+ "Links use Paragraph target keys and batch-local Element client keys."
|
|
|
|
|
+ ),
|
|
|
|
|
+ "parameters": {
|
|
|
|
|
+ "type": "object",
|
|
|
|
|
+ "properties": {
|
|
|
|
|
+ "elements": {"type": "array", "minItems": 1, "items": element},
|
|
|
|
|
+ "links": {"type": "array", "minItems": 1, "items": link},
|
|
|
|
|
+ "expected_state_revision": {"type": "string", "minLength": 1},
|
|
|
},
|
|
},
|
|
|
- "required": ["goals", "constraints", "preferences", "evidence_refs"],
|
|
|
|
|
|
|
+ "required": ["elements", "links", "expected_state_revision"],
|
|
|
"additionalProperties": False,
|
|
"additionalProperties": False,
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
@@ -1156,15 +1205,18 @@ def _save_direction_candidate_schema() -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
def _submit_validation_schema() -> dict[str, Any]:
|
|
def _submit_validation_schema() -> dict[str, Any]:
|
|
|
- artifact_ref = {
|
|
|
|
|
|
|
+ scope_selector = {
|
|
|
"type": "object",
|
|
"type": "object",
|
|
|
"properties": {
|
|
"properties": {
|
|
|
- "uri": {"type": "string"},
|
|
|
|
|
- "kind": {"type": "string"},
|
|
|
|
|
- "version": {"type": ["string", "null"]},
|
|
|
|
|
- "digest": {"type": ["string", "null"]},
|
|
|
|
|
|
|
+ "anchor": {"type": "string", "enum": ["mission"]},
|
|
|
|
|
+ "path": {
|
|
|
|
|
+ "type": "array",
|
|
|
|
|
+ "minItems": 1,
|
|
|
|
|
+ "maxItems": 4,
|
|
|
|
|
+ "items": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
|
|
|
+ },
|
|
|
},
|
|
},
|
|
|
- "required": ["uri", "kind"],
|
|
|
|
|
|
|
+ "required": ["anchor", "path"],
|
|
|
"additionalProperties": False,
|
|
"additionalProperties": False,
|
|
|
}
|
|
}
|
|
|
defect = {
|
|
defect = {
|
|
@@ -1172,9 +1224,9 @@ def _submit_validation_schema() -> dict[str, Any]:
|
|
|
"properties": {
|
|
"properties": {
|
|
|
"defect_code": {"type": "string"},
|
|
"defect_code": {"type": "string"},
|
|
|
"criterion_id": {"type": "string"},
|
|
"criterion_id": {"type": "string"},
|
|
|
- "scope_ref": {"type": "string"},
|
|
|
|
|
|
|
+ "scope_selector": scope_selector,
|
|
|
"observed_excerpt": {"type": "string", "maxLength": 500},
|
|
"observed_excerpt": {"type": "string", "maxLength": 500},
|
|
|
- "evidence_refs": {"type": "array", "items": artifact_ref},
|
|
|
|
|
|
|
+ "evidence_handle_ids": {"type": "array", "items": {"type": "string"}},
|
|
|
"severity": {"type": "string", "enum": ["warning", "hard", "critical"]},
|
|
"severity": {"type": "string", "enum": ["warning", "hard", "critical"]},
|
|
|
"invalidated_inputs": {"type": "array", "items": {"type": "string"}},
|
|
"invalidated_inputs": {"type": "array", "items": {"type": "string"}},
|
|
|
"recommended_action_class": {
|
|
"recommended_action_class": {
|
|
@@ -1194,9 +1246,9 @@ def _submit_validation_schema() -> dict[str, Any]:
|
|
|
"required": [
|
|
"required": [
|
|
|
"defect_code",
|
|
"defect_code",
|
|
|
"criterion_id",
|
|
"criterion_id",
|
|
|
- "scope_ref",
|
|
|
|
|
|
|
+ "scope_selector",
|
|
|
"observed_excerpt",
|
|
"observed_excerpt",
|
|
|
- "evidence_refs",
|
|
|
|
|
|
|
+ "evidence_handle_ids",
|
|
|
"severity",
|
|
"severity",
|
|
|
"invalidated_inputs",
|
|
"invalidated_inputs",
|
|
|
"recommended_action_class",
|
|
"recommended_action_class",
|
|
@@ -1229,9 +1281,9 @@ def _submit_validation_schema() -> dict[str, Any]:
|
|
|
"enum": ["passed", "failed", "inconclusive"],
|
|
"enum": ["passed", "failed", "inconclusive"],
|
|
|
},
|
|
},
|
|
|
"reason": {"type": "string", "maxLength": 300},
|
|
"reason": {"type": "string", "maxLength": 300},
|
|
|
- "evidence_refs": {
|
|
|
|
|
|
|
+ "evidence_handle_ids": {
|
|
|
"type": "array",
|
|
"type": "array",
|
|
|
- "items": artifact_ref,
|
|
|
|
|
|
|
+ "items": {"type": "string"},
|
|
|
"maxItems": 64,
|
|
"maxItems": 64,
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
@@ -1239,7 +1291,7 @@ def _submit_validation_schema() -> dict[str, Any]:
|
|
|
"criterion_id",
|
|
"criterion_id",
|
|
|
"verdict",
|
|
"verdict",
|
|
|
"reason",
|
|
"reason",
|
|
|
- "evidence_refs",
|
|
|
|
|
|
|
+ "evidence_handle_ids",
|
|
|
],
|
|
],
|
|
|
"additionalProperties": False,
|
|
"additionalProperties": False,
|
|
|
},
|
|
},
|
|
@@ -1322,6 +1374,15 @@ def _structured_object_list(
|
|
|
return [_structured_object(item, f"{label}[{index}]") for index, item in enumerate(normalized)]
|
|
return [_structured_object(item, f"{label}[{index}]") for index, item in enumerate(normalized)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+def _require_object_fields(
|
|
|
|
|
+ items: list[dict[str, Any]], required: tuple[str, ...], label: str
|
|
|
|
|
+) -> None:
|
|
|
|
|
+ for index, item in enumerate(items):
|
|
|
|
|
+ missing = [field for field in required if field not in item]
|
|
|
|
|
+ if missing:
|
|
|
|
|
+ raise ValueError(f"{label}[{index}] is missing required fields: {', '.join(missing)}")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
def _decode_bounded_structured_value(value: Any, label: str) -> Any:
|
|
def _decode_bounded_structured_value(value: Any, label: str) -> Any:
|
|
|
if isinstance(value, str):
|
|
if isinstance(value, str):
|
|
|
if len(value.encode("utf-8")) > _MAX_STRUCTURED_ARGUMENT_BYTES:
|
|
if len(value.encode("utf-8")) > _MAX_STRUCTURED_ARGUMENT_BYTES:
|
|
@@ -1369,21 +1430,16 @@ def _validated_images(
|
|
|
expected_digest = item.get("sha256")
|
|
expected_digest = item.get("sha256")
|
|
|
if expected_digest is not None and expected_digest != digest:
|
|
if expected_digest is not None and expected_digest != digest:
|
|
|
raise ValueError("frozen image digest does not match its bytes")
|
|
raise ValueError("frozen image digest does not match its bytes")
|
|
|
- raw_artifact_ref = item.get("raw_artifact_ref")
|
|
|
|
|
- if (
|
|
|
|
|
- not isinstance(raw_artifact_ref, str)
|
|
|
|
|
- or len(raw_artifact_ref) > 200
|
|
|
|
|
- or not raw_artifact_ref.startswith("script-build://raw-artifacts/sha256/")
|
|
|
|
|
- ):
|
|
|
|
|
- raise ValueError("frozen image must identify one immutable raw artifact")
|
|
|
|
|
|
|
+ image_handle = item.get("image_handle")
|
|
|
|
|
+ if not isinstance(image_handle, str) or not image_handle.startswith("image_"):
|
|
|
|
|
+ raise ValueError("frozen image must identify one Workbench image handle")
|
|
|
images.append({"type": "base64", "media_type": media_type, "data": data})
|
|
images.append({"type": "base64", "media_type": media_type, "data": data})
|
|
|
manifest.append(
|
|
manifest.append(
|
|
|
{
|
|
{
|
|
|
"index": index,
|
|
"index": index,
|
|
|
"media_type": media_type,
|
|
"media_type": media_type,
|
|
|
"byte_size": len(content),
|
|
"byte_size": len(content),
|
|
|
- "sha256": digest,
|
|
|
|
|
- "raw_artifact_ref": raw_artifact_ref,
|
|
|
|
|
|
|
+ "image_handle": image_handle,
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
return images, manifest
|
|
return images, manifest
|