Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ tasks:
desc: Run unit tests
cmds:
- task python:check
- "PYTHONPATH=. {{.PYTHON}} -m pytest tests/test_unit.py tests/test_worker_fallback.py tests/test_supporting_components.py tests/test_tool_context.py tests/test_remediation.py tests/test_worker_approval_resume.py"
- "PYTHONPATH=. {{.PYTHON}} -m pytest tests/test_unit.py tests/test_worker_fallback.py tests/test_supporting_components.py tests/test_tool_context.py tests/test_remediation.py tests/test_worker_approval_resume.py tests/test_resource_bindings.py"

contracts:check:
desc: Run contract documentation checks
Expand Down
1 change: 1 addition & 0 deletions constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ uvicorn==0.46.0
httpx==0.28.1
pydantic==2.13.4
pydantic-settings==2.14.2
rfc8785==0.1.4
tenacity==9.1.4
anyio==4.13.0
prometheus-client==0.25.0
Expand Down
10 changes: 10 additions & 0 deletions docs/contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ The execution engine owns run execution and talks only to the control plane and

- The control plane owns run, workspace, target, workflow, session, and message identifiers. Execution-engine echoes them; it does not mint replacements.
- Tool permission, provider/model permission, native tool permission, write availability, and skill snapshots are upstream policy. The engine treats bootstrap snapshots and run JWTs as authoritative.
- Platform functions are callable only when their provider-safe `model_alias`
intersects `platform_functions`, `allowed_tools`, and `tool_specs`. The engine
maps the alias back to the canonical control-plane ID and sends the original
model call ID; missing, duplicate, and invalid mappings fail closed.
Provider-native `web_search` remains the only declaration sent through
`native_tools`, while target and MCP tools retain their existing route.
- Execution-engine never calls target agents, management-console, or external MCP servers directly.
- Cancellation is terminal from the engine's point of view; after cancellation wins, user-visible assistant output stops.
- Approval continuations must not store gateway tokens or credentials. Resume reboots policy through control-plane bootstrap.
Expand Down Expand Up @@ -63,6 +69,10 @@ The execution engine owns run execution and talks only to the control plane and
third-party MCP servers.
- The gateway validates provider, model, tool, native-tool, max-output, and scope claims; execution-engine must not bypass or reinterpret those checks.
- Frozen target skills use the internal model-only `_acornops_load_skill` pseudo-tool. It is intercepted by execution-engine and is not an MCP tool.
- Explicit target-chat skill references are preloaded through the same bounded
skill loader before the first model request. Explicit tool references add
exact runtime aliases to model instructions but never bypass tool authority
or write approval.
- Local smoke tests may set `LLM_ENABLE_DETERMINISTIC_DEV_RESPONSES=true`; this remains a local-only aid after normal JWT and scope validation.
- Reasoning summary events are provider summaries only and must not be merged into final assistant markdown.

Expand Down
42 changes: 17 additions & 25 deletions docs/contracts/manifest.json
Original file line number Diff line number Diff line change
@@ -1,42 +1,31 @@
{
"repo": "execution-engine",
"version": 1,
"executionContractVersion": 2,
"runtimeDependencies": ["control-plane", "llm-gateway"],
"counterparts": {
"control-plane": {
"dispatchPaths": ["POST /api/v1/runs", "POST /api/v1/runs/{run_id}/cancel"],
"dispatchAuth": "Authorization: Bearer <EXECUTION_ENGINE_DISPATCH_TOKEN>",
"internalTransportSecurity": "HTTP by default; HTTPS/mTLS when Helm internalTransport.tls.enabled=true; bearer/JWT tokens remain required",
"controlPlanePaths": [
"POST /internal/v1/runs/{runId}/bootstrap",
"GET /internal/v1/runs/{runId}/skills/{skillRef}",
"POST /internal/v1/runs/{runId}/approvals",
"GET /internal/v1/runs/{runId}/continuation",
"POST /internal/v1/runs/{runId}/approvals/{approvalId}/execution-started",
"POST /internal/v1/runs/{runId}/approvals/{approvalId}/execution-finished",
"DELETE /internal/v1/runs/{runId}/continuation",
"GET /internal/v1/sessions/{sessionId}/context?run_id=<runId>",
"GET /internal/v1/agent-runs/{runId}/context",
"POST /internal/v1/runs/{runId}/events",
"POST /internal/v1/runs/{runId}/tool-result-artifacts",
"GET /internal/v1/runs/{runId}/event-cursor",
"POST /internal/v1/runs/{runId}/commit"
],
"bootstrapFields": [
"contract_version",
"scope.{type,workspace_id,target_id?,target_type?,workflow_id?,workflow_run_id?,workflow_execution_id?,workflow_session_id?,workflow_step_id?,step_index?,attempt_number?,idempotency_key?,agent_id?,agent_version?,trigger_id?,session_id,run_id,user_id}",
"scope.{type,workspace_id,target_id?,target_type?,workflow_id?,workflow_run_id?,workflow_execution_id?,workflow_session_id?,attempt_number?,idempotency_key?,agent_id?,agent_version?,trigger_id?,session_id,run_id,user_id}",
"assistant?.{targetType?,instructions}",
"policy.{max_runtime_ms,max_output_tokens,budget_cents,max_steps,max_tool_calls,max_duplicate_tool_calls}",
"context.{endpoint,max_context_tokens}",
"resources.{prompt_digest,binding_digest,resolved_at,bindings[].{binding_id,type,resource_id,provider,provider_version,workspace_id,label_snapshot,source,operations,context_mode,provider_data?}}",
"llm.{provider,model,temperature,mode,reasoning.{summary_mode,effort},gateway.{url,token,request_timeout_ms}}",
"tools.{tool_registry_version,allowed_tools,native_tools,tool_specs,write_unavailable_reason?,gateway.{url,token},confirmation_required_for_write,approval_timeout_seconds}",
"skills?.{contract_version,entries[].{ref,skill_id,name,description,file_count,total_bytes},load_endpoint}",
"tools.{tool_registry_version,allowed_tools,allowed_tool_refs[].{server_id,tool_name},native_tools,platform_functions[].{id,model_alias},tool_specs[].{server_id?,tool_ref?},referenced_tools[].{name,label,server_id?,tool_name?},write_unavailable_reason?,gateway.{url,token},confirmation_required_for_write,approval_timeout_seconds}",
"skills?.{contract_version,entries[].{ref,skill_id,name,description,file_count,total_bytes},referenced_refs[],load_endpoint}",
"routing",
"tracing"
],
"contextFields": ["messages", "summaries", "attachments", "target_insights.retrieval_status", "target_insights.snippets[]"],
"promptResourceIntegrity": "The execution engine rejects duplicate bindings, oversized or malformed claims, and any binding array whose canonical SHA-256 digest differs from resources.binding_digest.",
"eventFrameFields": ["schema_version", "run_id", "seq", "ts", "type", "payload"],
"approvalRequestFields": ["toolCallId", "toolName", "summary?", "arguments", "continuation?"],
"approvalResponseScopeFields": ["targetId?", "targetType?", "workflowId?", "workflowRunId?", "workflowSessionId?", "workflowStepId?"],
"approvalRequestFields": ["toolCallId", "toolName", "toolRef.{serverId,toolName}", "summary?", "arguments", "continuation?"],
"approvalExecutionStartedResponseFields": ["approval", "approvalReceipt"],
"approvalResponseScopeFields": ["targetId?", "targetType?", "workflowId?", "workflowRunId?", "workflowSessionId?"],
"eventTypes": [
"run_progress",
"run_started",
Expand Down Expand Up @@ -65,6 +54,7 @@
"toolCallCompletedContextMetaFields": ["schema_version", "strategy", "original_bytes", "context_bytes", "truncated", "omissions"],
"toolCallCompletedArtifactFields": ["id", "expires_at", "sha256", "uncompressed_bytes", "compressed_bytes", "content_type"],
"toolCallCompletedResultMaxBytes": 12288,
"platformNativeToolRouting": "A platform function is exposed under its provider-safe model_alias only when that alias intersects bootstrap platform_functions, allowed_tools, and tool_specs. Execution routes the alias back to the canonical control-plane ID with the stable tool-call ID. Missing, duplicate, or invalid mappings fail closed. native_tools and gateway JWT allowed_native_tools contain provider-native tools only; target MCP tools remain gateway/target-adapter owned.",
"commitStatusValues": ["completed", "failed", "cancelled"],
"commitAssistantMessageFields": ["content", "format"],
"commitUsageFields": ["input_tokens", "output_tokens", "tool_calls", "reasoning_tokens?"],
Expand All @@ -74,8 +64,8 @@
"200 terminal_idempotent_replay",
"409 run_id_scope_mismatch",
"429 overloaded",
"workspace scope may identify either a workflow step or a standalone Agent run",
"a selected-target workflow step dispatches with target scope plus target_id and target_type",
"workspace scope may identify either a single-entry workflow or a standalone Agent run",
"a selected-target workflow dispatches with target scope plus target_id and target_type",
"idempotency_key is stable across at-least-once dispatch of one attempt"
]
},
Expand All @@ -92,7 +82,6 @@
"workflow_id",
"workflow_run_id",
"workflow_session_id",
"workflow_step_id",
"agent_id",
"agent_version",
"trigger_id",
Expand All @@ -107,6 +96,7 @@
"native_tools"
],
"internalModelOnlyTools": ["_acornops_load_skill"],
"providerFunctionNamePattern": "^[A-Za-z_][A-Za-z0-9_-]{0,62}$",
"streamResponseTypes": [
"delta",
"tool_call",
Expand All @@ -126,12 +116,14 @@
"workflow_id",
"workflow_run_id",
"workflow_session_id",
"workflow_step_id",
"agent_id",
"agent_version",
"trigger_id",
"tool_call_id",
"tool",
"tool_ref.server_id",
"tool_ref.tool_name",
"approval_receipt?",
"arguments"
],
"toolCallResponseFields": ["full_result", "model_context", "context_meta", "artifact_eligible", "is_error"],
Expand Down
12 changes: 12 additions & 0 deletions docs/exec-plans/active/extensible-catalog-sources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Server-qualified MCP tool references

## Goal

Carry model-facing collision-safe aliases and authoritative MCP tool references
through execution without resolving a runtime tool by its remote name alone.

## Validation

- Snapshot contract parsing, alias-to-reference dispatch, and duplicate remote
name tests.
- `task validate` and platform contract checks.
28 changes: 28 additions & 0 deletions docs/exec-plans/active/target-chat-slash-references.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Target chat slash references

## Goal

Honor control-plane-validated target-chat references deterministically while
preserving run lifecycle and tool safety.

## Runtime boundaries

- Treat referenced tool aliases and skill refs from the bootstrap snapshot as
upstream policy, not user-provided authority.
- Add a compact system instruction identifying exact referenced tools while
keeping the full allowed tool set available for supporting diagnostics.
- Preload referenced skills before the first model request and count them toward
the existing skill count and byte budgets.
- Emit the existing skill load events for preloaded references.
- Deduplicate later model-requested skill loads against preloaded refs.
- A missing referenced skill or exceeded skill budget fails through the existing
bounded skill-load path; it never substitutes another skill.
- Continuations retain loaded skill refs and do not reload them.

## Validation

- Snapshot parsing and backward compatibility tests.
- Referenced-tool instruction tests.
- Preload ordering, event, budget, deduplication, continuation, and cancellation
tests.
- `task validate`, contract checks, and workspace platform contract checks.
32 changes: 32 additions & 0 deletions docs/exec-plans/completed/interactive-pdf-report-artifacts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Interactive PDF report artifacts

## Goal

Route platform-native function tools declared by the control-plane snapshot back
to the control plane while preserving exact-reference enforcement for MCP tools.

## Scope

- Intercept only tool IDs present in both `allowed_tools` and `native_tools`.
- Keep provider-native tools such as `web_search` on the LLM-gateway path.
- Call the service-authenticated control-plane native-tool endpoint with the
stable model tool-call ID.
- Preserve bounded tool-result normalization and existing event behavior.

## Verification

- Unit coverage for authorization intersection, routing, and callback payloads.
- Contract and run-lifecycle validation.

## Delivery

Shared branch: `feat/extensible-catalog-sources`.
Merge order: control-plane, execution-engine, management-console.

## Outcome

- Added a fail-closed intersection across allowed tools, native tool IDs, and
function specs, then routed only that intersection to the control plane.
- Kept `web_search` provider-native and retained exact MCP reference routing.
- Verified Ruff, contract checks, and the full canonical unit-test selection in
the pinned Python 3.12.11 container (174 passed).
62 changes: 62 additions & 0 deletions execution_engine/agent/assistant_reference_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Prompt and skill context derived from explicit chat references."""

from typing import Any, AsyncGenerator, Dict, List

from execution_engine.agent.skill_loading import (
SkillLoader,
SkillLoadState,
load_requested_skill_contexts,
)
from execution_engine.skill_constants import INTERNAL_LOAD_TARGET_SKILL_TOOL


def native_tool_instruction(native_tools: List[Dict[str, Any]] | None) -> str | None:
"""Describes native capabilities that are not regular function tools."""
if not native_tools:
return None
capability_labels = ["Web Search" for tool in native_tools if tool.get("id") == "web_search"]
if not capability_labels:
return None
capabilities = ", ".join(dict.fromkeys(capability_labels))
return (
f"Built-in capabilities enabled for this run: {capabilities}. "
"When the user asks what tools or capabilities are available, include these separately from "
"standard callable function tools. Built-in capabilities may not appear as standard tool-call "
"events in run details."
)


def referenced_tool_instruction(tool_names: List[str]) -> str | None:
"""Explains the semantics of tools explicitly referenced by the operator."""
if not tool_names:
return None
names = ", ".join(f"`{name}`" for name in tool_names)
return (
f"The operator explicitly referenced these exact tools: {names}. "
"Use a referenced tool when it is relevant to the request, and do not substitute a similarly named "
"tool. A reference does not require a tool call when the request can be answered without one."
)


async def preload_referenced_skills(
skill_refs: List[str],
messages: List[Dict[str, str]],
state: SkillLoadState,
skill_loader: SkillLoader | None,
max_skill_loads: int,
max_loaded_skill_bytes: int,
) -> AsyncGenerator[Dict[str, Any], None]:
"""Loads explicitly referenced skills before the first model request."""
calls = [
{"tool": INTERNAL_LOAD_TARGET_SKILL_TOOL, "arguments": {"skill_ref": skill_ref}}
for skill_ref in skill_refs
]
async for event in load_requested_skill_contexts(
calls,
messages,
state,
skill_loader=skill_loader,
max_skill_loads=max_skill_loads,
max_loaded_skill_bytes=max_loaded_skill_bytes,
):
yield event
53 changes: 27 additions & 26 deletions execution_engine/agent/react_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
from contextlib import suppress
from typing import Any, AsyncGenerator, AsyncIterator, Dict, List

from execution_engine.agent.assistant_reference_context import (
native_tool_instruction,
preload_referenced_skills,
referenced_tool_instruction,
)
from execution_engine.agent.engine import AgentEngine
from execution_engine.agent.remediation_verification import (
finalize_remediation_verifications,
Expand Down Expand Up @@ -52,6 +57,7 @@ def __init__(
skill_loader: SkillLoader | None = None,
max_skill_loads: int = 3,
max_loaded_skill_bytes: int = 262144,
referenced_tool_names: List[str] | None = None, referenced_skill_refs: List[str] | None = None,
):
"""
Initializes the ReAct engine.
Expand All @@ -72,6 +78,8 @@ def __init__(
self.skill_loader = skill_loader
self.max_skill_loads = max(max_skill_loads, 0)
self.max_loaded_skill_bytes = max(max_loaded_skill_bytes, 0)
self.referenced_tool_names = list(dict.fromkeys(referenced_tool_names or []))
self.referenced_skill_refs = list(dict.fromkeys(referenced_skill_refs or []))

@staticmethod
async def _iterate_until_cancelled(
Expand Down Expand Up @@ -143,27 +151,6 @@ def _write_unavailable_instruction(reason: str | None) -> str | None:
)
return None

@staticmethod
def _native_tool_instruction(native_tools: List[Dict[str, Any]] | None) -> str | None:
if not native_tools:
return None

capability_labels: List[str] = []
for tool in native_tools:
if tool.get("id") == "web_search":
capability_labels.append("Web Search")

if not capability_labels:
return None

capabilities = ", ".join(dict.fromkeys(capability_labels))
return (
f"Built-in capabilities enabled for this run: {capabilities}. "
"When the user asks what tools or capabilities are available, include these separately from "
"standard callable function tools. Built-in capabilities may not appear as standard tool-call "
"events in run details."
)

async def run(
self,
messages: List[Message],
Expand Down Expand Up @@ -218,9 +205,25 @@ async def run(
write_unavailable_instruction = self._write_unavailable_instruction(self.write_unavailable_reason)
if write_unavailable_instruction:
llm_messages.insert(0, {"role": "system", "content": write_unavailable_instruction})
native_tool_instruction = self._native_tool_instruction(native_tools)
if native_tool_instruction:
llm_messages.insert(0, {"role": "system", "content": native_tool_instruction})
native_instruction = native_tool_instruction(native_tools)
if native_instruction:
llm_messages.insert(0, {"role": "system", "content": native_instruction})
referenced_instruction = referenced_tool_instruction(self.referenced_tool_names)
if referenced_instruction:
llm_messages.insert(0, {"role": "system", "content": referenced_instruction})
if self.referenced_skill_refs:
skill_state = SkillLoadState(loaded_skill_refs, loaded_skill_bytes)
async for event in preload_referenced_skills(
self.referenced_skill_refs,
llm_messages,
skill_state,
self.skill_loader,
self.max_skill_loads,
self.max_loaded_skill_bytes,
):
yield event
loaded_skill_refs = skill_state.loaded_refs
loaded_skill_bytes = skill_state.loaded_bytes
request_preview = self._summarize_user_request(llm_messages)
if request_preview:
yield {
Expand Down Expand Up @@ -487,7 +490,6 @@ async def run(
workflow_id=self.scope.workflow_id,
workflow_run_id=self.scope.workflow_run_id,
workflow_session_id=self.scope.workflow_session_id,
workflow_step_id=self.scope.workflow_step_id,
agent_id=self.scope.agent_id,
agent_version=self.scope.agent_version,
trigger_id=self.scope.trigger_id,
Expand Down Expand Up @@ -623,7 +625,6 @@ async def run(
workflow_id=self.scope.workflow_id,
workflow_run_id=self.scope.workflow_run_id,
workflow_session_id=self.scope.workflow_session_id,
workflow_step_id=self.scope.workflow_step_id,
agent_id=self.scope.agent_id,
agent_version=self.scope.agent_version,
trigger_id=self.scope.trigger_id,
Expand Down
Loading
Loading