跳到正文
FunCoding

搜索

搜索文档、文章、Skill 和 MCP

Agent harness attempt runtime

Runtime helpers a selected harness calls during and after an attempt: injection, middleware, outcome classification, usage, and agent-end side effects

The helpers a selected harness calls while an attempt is running and as it finalizes: guarded input injection, tool-result middleware, terminal outcome classification, live token usage, and agent-end side effects. Part of the Agent harness plugins reference.

Guarded active-run injection

Backends that accept source-bound controls advertise messageInjectionV2 on their active-run handle. The capability is contextually typed by setActiveEmbeddedRun from openclaw/plugin-sdk/agent-harness-runtime; its type can also be derived from that function's handle parameter. It requires version: 2, isAvailable(), and queueMessage(text, options, assertCurrent, authorityKind). The required third argument is a host-owned assertion for that individual injection, not a run ID, fingerprint, or diagnostic identity. The required authorityKind is "run" for ordinary input or "source-bound" for input whose source lifetime also constrains dispatch. Both retain the backing-run check; a source-bound input must never be relabeled as ordinary input.

Invoke assertCurrent() alongside the backend's own live-run check after awaited preparation and immediately before queue mutation or provider dispatch. The host normalizes false or throwing source authority into rejection and keeps that injection revoked even if the source later appears current again. Plugins invoke the supplied assertion; they do not reconstruct its authority. Batched backends retain and revalidate each item's assertion, including before retries; omit revoked items without cancelling independently accepted work or poisoning later authorized controls.

When supplied, call options.onQueueSettled() once after that input commits, is canceled, or is terminally rejected. onQueueAccepted(true) only reports admission. A backend that returns early for waitForTranscriptCommit: false retains the settlement callback until its exact input finishes; core uses it to release the selected sender's retained source authority.

Optional V2 claimPendingUserInputAnswer(text, options, assertCurrent, authorityKind) and cancelPendingUserInput(resolvedBy, assertCurrent, authorityKind) methods require the same assertion and authority kind. Carry it through question registration and persistence to the final claim or cancellation boundary. Do not implement V2 by checking only before calling an SDK method that itself awaits before dispatch. If the sink cannot enforce the assertion, leave V2 unsupported.

The V1 messageInjection, queue options, queueAgentHarnessMessage, and setActiveEmbeddedRun signatures shipped in v2026.8.1 remain source-compatible. Pass the resolved agent ID as the fifth setActiveEmbeddedRun argument so raw global and unknown keys retain their owner. Legacy calls inside a matching live host binding inherit its validated agent; an ambient caller alone does not supply ownership. Outside that binding, omitted ownership uses the qualified session key or the configured default agent for session activity. Unscoped V1 injection retains its existing behavior. Source-bound controls require V2 and reject visibly before queue or I/O when only V1 is available; they never fall back to an unchecked V1 callback. Existing deprecation windows are unchanged.

Copilot remains V1-only: @github/copilot-sdk 1.0.11 awaits trace-context and JSON-RPC writer preparation after send entry without a final-dispatch guard. Scoped steering therefore fails before its queue, question claim, or provider I/O; ordinary unscoped injection is unchanged. Check status, cancel the run, or start a new explicit request instead. Update the runtime when guarded injection is supported. Once upstream supplies a final-dispatch assertion, migrate Copilot to V2 and remove this internal V1 reliance; do not add an unchecked fallback or shorten the shipped API's deprecation window.

Tool-result middleware

Bundled plugins and explicitly enabled installed plugins with matching manifest contracts can attach runtime-neutral tool-result middleware through api.registerAgentToolResultMiddleware(...) when their manifest declares the targeted runtime ids in contracts.agentToolResultMiddleware. This trusted seam is for async tool-result transforms that must run before the selected harness feeds tool output back into the model. Supported runtime ids are agentsapi, codex, and openclaw.

Middleware options may combine runtimes with a matcher tool-name list. Each registration keeps that pair intact, so registering the same handler for different runtimes does not broaden either matcher. Matchers use non-empty canonical OpenClaw tool ids; omit matcher to match all tools.

Omitting runtimes uses every supported runtime declared in the plugin's contracts.agentToolResultMiddleware, including agentsapi when declared. Supply runtimes only to select a subset of that declaration. Registration rejects an empty runtime list or any targeted runtime missing from the manifest.

Legacy bundled plugins can still use api.registerCodexAppServerExtensionFactory(...) for Codex app-server-only middleware, but new result transforms should use the runtime-neutral API. The embedded-runner-only api.registerEmbeddedExtensionFactory(...) hook has been removed; embedded tool-result transforms must use runtime-neutral middleware.

Retain details.messageDelivery.sourceReplyDelivered from the host message tool before middleware transforms its result, and carry it into the attempt result. This confirms a final external source reply and does not depend on destination arguments or transcript mirrors.

Use extractMessagingToolSourceReplyPayload(result) from the same runtime subpath to retain internal source-reply payloads through presentation changes. For a confirmed messaging delivery, collectMessagingMediaUrlsFromRecord(args) collects its attachment references for delivery deduplication. Neither helper establishes delivery or grants local-file trust.

Reply attachments from a remote workspace

A harness whose files are remote can call the optional params.hostCapabilities.prepareReplyMedia before closing its file transport. The host applies the existing sender read policy and channel/account byte limit, then saves authorized attachment bytes for delivery.

RequestResultHarness action
kind: "attempt", attemptpreparedMediaSet attempt.preparedReplyMedia on the same result object. Core applies it after final answer selection.
kind: "payload", payloadPrepared payloadDeliver this copy through onBlockReply; keep the persisted message unchanged.

Both requests supply readWorkspaceFile(relativePath, { maxBytes, signal }). The reader must enforce the byte limit and workspace boundary and honor cancellation. It receives only paths authorized under the host's captured policy. Supply workspaceRoot when the remote workspace has a different absolute path; the host maps that alias to the logical workspace before checking policy. Supply assertCurrent when native session or transport ownership can be revoked independently of the host attempt. The host retains this additional check through the final media write and publication. Keep the reader alive until preparation finishes.

Raw file paths in these replies belong to the remote workspace. Paths outside that workspace fail with a labeled remote-file notice, even if a Gateway-local file exists at the same path. HTTP references and securely validated Gateway managed media, including media:// attachments, remain available.

For artifacts whose bytes the provider has already admitted, use kind: "artifact" with buffer, fileName, assertCurrent, and an optional signal. The host stages those exact bytes under the captured channel/account byte limit and returns a prepared payload. This request grants no filesystem reads and applies no image transformation or host-file MIME allowlist. The harness owns validation of the provider artifact's session, turn, environment, and path before downloading it; the host owns the outbound destination and retains live authority through publication.

Missing, denied, and oversized attachments produce the usual delivery failure notice; preparation does not fall back to a stale Gateway workspace file. Prepared facts contain file locations and failures, never a live reader. Do not rewrite assistant text or transcript messages to insert Gateway file paths. When the capability is absent, this remote attachment preparation is unavailable.

Shared attempt mechanics

Official native harnesses use buildCurrentInboundPrompt from the private openclaw/plugin-sdk/agent-harness-attempt-runtime to combine the prepared currentInboundContext with the current prompt using the channel's joiner. Submit this context with each message, including resumed sessions. Steering receives its own options.currentInboundContext; do not reuse the initial turn's context. Keep context out of the original user transcript and pending question answer text. Conversation fields are model context, not tool authority.

resolveAgentHarnessBeforePromptBuildResult from openclaw/plugin-sdk/agent-harness-runtime runs prompt hooks with prepared history and tool authority. Pass the admitted message as currentUserMessage; the helper extracts its text parts and idempotencyKey for ordinary and authorized hooks. String input and a separate currentUserMessageId remain supported. The harness owns the fallback when no admitted message exists.

Supply messages as an array or an async loader, which runs only when a before_prompt_build hook needs history. Heartbeat-only contributions do not read conversation history. The production-private resolveAgentHarnessHistoryLimits helper applies the shared Codex and Agents API transcript read budget.

Agents API retains the first successfully prepared, bounded hook history for retries of the same logical run and native session. Prompt hooks still run on each attempt with the current input and live host authority; their history stays at the original before-turn snapshot. Accepted steering therefore does not force a new transcript read through the original message's now-stale admission. This snapshot is data, not renewed transcript-read permission: later transcript changes are observed by the next logical run, and a changed recorder, session, run identity, or history budget requires a fresh read. Session reset and run cancellation retain their existing authority checks.

The developerInstructions.build callback receives toolsAllow and hasToolRestrictions. Omitted policy or a trimmed * entry is unrestricted; an empty list or a list without * is restrictive. Backends enforcing per-turn restrictions apply or reject them inside that callback, before authorized recall runs. Agents API continues with hook context but does not enforce hook tool lists.

Official harnesses use the JavaScript-only private openclaw/plugin-sdk/agent-harness-attempt-runtime for deadlines, cancellation, and lifecycle/event publication; it is not a third-party Plugin SDK contract. Codex and AgentsAPI also use shouldIncludeAgentHarnessRuntimeContext to exclude runtime prompt additions from lightweight cron inputs, and resolveAgentWorkspaceMemoryRouting to select admitted memory tools and check that they reach the prompt workspace. Backends retain workspace selection, tool name normalization, and native prompt rendering.

createAgentHarnessAttemptDeadlineController takes the original startedAtMs, execution timeoutMs, backend settlementTimeoutMs, abort signal, and timeout callback. The first beginSettlement(receivedAtMs) starts an absolute settlement deadline; repeated calls do not extend it. Abort or dispose() closes it. createAgentHarnessAttemptCancellation retains explicit cancellation reasons and freezes admission at the terminal boundary. emitAgentHarnessAttemptEvent isolates observer failures, and createAgentHarnessAttemptLifecycle gates lifecycle events and deduplicates execution phases. Native interruption, completion decisions, output flushing, and cleanup remain backend-owned.

Assistant event data.itemId identifies the native text item for cumulative snapshots and delta merging. A harness that persists part of an item while it continues streaming also supplies data.occurrenceId, an opaque string identifying the exact live interval. Freeze that occurrence when capturing the transcript candidate, before awaiting persistence, and allocate a new occurrence for later bytes without changing the native item ID or delta text. Successful transcript publication supplies the captured IDs in assistantItemIds; failed writes do not retire them. Retries retain every occurrence actually included in the candidate. Without occurrenceId, retirement uses itemId or the host's source receipt. Text without either identity stays live until terminal settlement; matching durable text alone never proves ownership.

The private openclaw/plugin-sdk/agent-harness-tool-runtime provides correlated execution promises and argument/start snapshots through createAgentHarnessToolExecutionRegistry and createAgentHarnessToolExecutionBoundaryRegistry. Consumed snapshots cannot be republished by late completion. Core tool guards and observeToolTerminal remain authoritative; native decoding and result encoding stay with the harness.

Shared host-tool result facts

Official harnesses use the private JavaScript-only openclaw/plugin-sdk/agent-harness-tool-runtime to execute host tools and record portable tool facts. runAgentHarnessToolInvocation owns argument preparation, validation at the existing execution boundary, monotonic execution snapshots, middleware, and cleanup. Its result and failure callbacks carry those facts to native adapters without taking over their receipt or timeout owner. recordAgentHarnessToolResultTelemetry collects host-tool delivery, media, TTS, cron, and heartbeat facts using the caller's prepared source-reply projection. The invocation preserves execution failures when presentation middleware rewrites a result. recordAgentHarnessMessagingDelivery records an already-confirmed messaging delivery, and recordAgentHarnessToolResultMedia collects and trust-filters presented media. Callers retain their native receipt, routing, cancellation, and result-encoding contracts; these helpers do not establish delivery or grant execution authority.

Workspace-staged attachments

Admitted attachment facts can refer to files staged under the prepared workspace instead of the managed media store. Use root(workspaceDir) and createStagedInputPathMatcher(root) from openclaw/plugin-sdk/file-access-runtime to verify staging ownership before a bounded root.read(relativePath, { maxBytes }). Match the fact's workspace to the attempt's prepared workspace, retain the host's current-run assertion through awaited reads, and use admitted media facts rather than paths extracted from user or model text. The matcher shares the staging owner's exact marker contract and caches results only for that capture.

Final tool-argument validation

Official native harness adapters can call runWithToolExecutionValidation(callId, validate, execute) from the private openclaw/plugin-sdk/agent-harness-tool-runtime around the host-bound tool's execute call. The validator receives the final arguments after policy and before-call hooks have adjusted them, at the existing tool execution boundary. Use the shared schema validation helpers for the declared tool schema. Do not copy private validation markers or run a second before-call hook. Validation is scoped to the tool call and remains isolated from concurrent calls.

Retain accepted background work before middleware: isAsyncStartedToolResult and readAsyncStartedTaskIds from openclaw/plugin-sdk/agent-harness-tool-runtime expose its task metadata. normalizeAcceptedSessionSpawnResult from openclaw/plugin-sdk/agent-harness-tool-runtime preserves a child session's completion ownership. Carry their recorded facts into the attempt result so recovery cannot replay accepted work.

Terminal outcome classification

Native harnesses that own their own protocol projection can use classifyAgentHarnessTerminalOutcome(...) from openclaw/plugin-sdk/agent-harness-runtime when a completed turn produced no visible assistant text. The helper returns empty, reasoning-only, or planning-only so OpenClaw's fallback policy can decide whether to retry on a different model. planning-only requires the harness's explicit planText field; OpenClaw does not infer it from assistant prose. The helper intentionally leaves prompt errors, in-flight turns, and intentional silent replies such as NO_REPLY unclassified.

Live output-token usage

Call params.hostCapabilities.reportOutputTokens?.(outputTokens) once per completed model response. Pass that response's output tokens, not a thread-lifetime or cumulative attempt total. Deduplicate native response notifications before calling it.

The host binds this callback to the admitted run, adds the response to its lifecycle-scoped total, and publishes the cumulative usage event globally and through params.onAgentEvent. Do not emit a second usage event. Retries share the same run total; run cleanup releases it. A closed or superseded capability rejects reporting. Invalid or nonpositive counts do not emit an event.

The capability is optional for compatibility with older hosts; when absent, live output-token reporting is unavailable. Keep last-response context snapshots and persisted billing usage separate from this live counter.

Agent-end side effects

Native harnesses must await runAgentEndSideEffectsAsync(...) from openclaw/plugin-sdk/agent-harness-runtime after they finalize an attempt. It prepares transcript anchors through the history reader before releasing the turn lease, then starts the portable agent_end hook without waiting for it. Use awaitAgentEndSideEffects(...) for local, non-interactive runs that must also wait for plugin hooks. The synchronous runAgentEndSideEffects(...) remains deprecated until the next Plugin SDK major. These helpers accept the same { event, ctx } payload as runAgentHarnessAgentEndHook(...); their failures do not alter the completed attempt result.

Pass ctx.foregroundPromptContext built with buildEmbeddedForegroundPromptContext(params, agentDir) from the same EmbeddedRunAttemptParams the attempt ran with. The detached Skill Workshop experience review rebuilds its system prompt and tool catalog from that context, so the review shares the foreground turn's prompt-cache prefix. Omit it only for runs that have no foreground prompt, such as CLI hook contexts; the review is skipped for those.