Skip to content

DSL invocation identity

The evaluator passes the current ExecutionContext.agent_id to each async reasoning, communication and pattern builtin. Each invocation clones the configured builtin context and binds that caller locally. Nested user functions retain the same principal, and overlapping calls do not share mutable sender identity.

Creating or declaring an agent produces a new agent instance. It does not enter that agent's execution scope or change the creator's principal. Trusted callers enter an agent scope through the execution API; ordinary expressions outside an agent have no attributed agent in their evaluator context.

The receiving builtins use this identity for reasoning authorization and communication policy checks. An unscoped caller retains the existing anonymous builtin behavior; it does not inherit the last declared agent's privileges. Direct SDK use of ReasoningBuiltinContext remains explicit caller configuration.

The REPL retains successful function and behavior declarations between inputs. A module publishes its definitions and agents together only after every agent passes declaration validation. A rejected module leaves existing definitions unchanged. Each behavior invocation captures its entry point and helper functions in one snapshot; later redefinitions affect subsequent invocations. Arguments, local variables and caller identity remain private to the invocation.

Start the declared agent before calling :agent execute <id> <behavior> [args]. The optional argument text is exposed as the behavior's args variable; this command does not bind typed behavior input parameters. The interactive client forwards agent and monitoring commands to the evaluator and displays its outputs and errors. DSL print() writes diagnostics to stderr so stdout remains a framed RPC response stream. The execution monitor records returned failures as completed failed attempts; it is separate from the protected action journal.

RuntimeBridge captures a canonical project directory at construction. Each builtin context keeps that same directory even if the process later changes its working directory. SDK callers can select it with with_project_root before sharing the bridge. A failed initial capture remains a configuration error. Default reason() and tool_call() discover tools from this project's absolute tools/ path and open protected per-invocation journals before inference or effects.

Both builtins return an audit map containing run_id, path and public_key. The principal is chosen once per invocation, including anonymous calls. Explicit tool calls require a start record and a terminal record after worker cleanup; denials, tool errors and cleanup failures have distinct terminal reasons. A failed terminal write returns an error even if an effect already happened. Cancellation can leave an incomplete signed prefix, which must not be treated as completion. SDK callers can deliberately inject their own executor or journal; they then own those implementations' guarantees and injected journals have no default audit map. See protected run audit for verification and key custody.

Direct llm_call, all provider calls inside chain, debate, map_reduce and director, and registered-agent composition also require a journal per provider call. Their existing string/list results are preserved. Use :audit to display the latest 256 public references and a count of older references omitted from the display; the older journal files remain available. SDK callers can read the same display through RuntimeBridge::audit_references().

Communication calls require a configured policy gate and retain the exact registered recipient definition used during authorization. Replacing a name cannot retarget an already authorized call. delegate requires a registered agent. send_to returns null after durable acceptance, before background completion; its journal records the later outcome. race waits for the first successful call, and cancelling parallel or losing a race cancels the remaining provider futures. A retained owner completes required journal writes after caller cancellation, including cancellation during startup. Abrupt process loss can still leave an incomplete signed prefix.

The audit binds canonical typed request and response hashes, the caller, and any recipient snapshot. It does not attest to arbitrary provider-internal effects or HTTP wire bytes. A configured communication bus must accept its request/response enqueue; enqueue acceptance does not prove recipient consumption. The explicit threaded conversation API does not copy conversation history into those queues. Each call has a deadline, request/response size limits and a requested output token cap. These are not aggregate budgets or a terminal record for an entire pattern.

The captured project supplies the default sandbox and tool limits. Registration rejects explicit legacy security.tier, security.sandbox, resource fields and execution-policy fields because this route cannot enforce them per agent. Empty blocks and capability-only declarations retain their existing checks. A rejected module publishes no agents or replacement helpers. Repeated constraint blocks and repeated capability lists are rejected instead of overwriting requirements. Configure the project boundary for supported legacy behavior, or use a supported canonical route when per-agent settings are required; do not remove a required constraint merely to make a registration succeed.

The legacy Tier1–Tier4 and strict/moderate/permissive values do not map to the canonical Docker/gVisor/Firecracker settings. AgentPin domain identity and full canonical per-agent source selection are separate integration work. Declaring or starting an agent does not itself run a behavior. Local shell orchestrator and fleet turns have protected per-turn audit; see protected run audit.