@agent-compose/sdk 0.8.3 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/agent-loop.d.ts +5 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +21 -4
- package/dist/runtimes/_cli-agent.d.ts +12 -0
- package/dist/runtimes/openai-desktop.js +19 -4
- package/dist/sandbox.d.ts +1 -1
- package/dist/types/protocol.d.ts +13 -1
- package/package.json +1 -1
- package/src/agent/agent-loop.ts +8 -3
- package/src/index.ts +3 -2
- package/src/runtimes/_cli-agent.ts +21 -1
- package/src/runtimes/claude-code.ts +11 -2
- package/src/sandbox/providers/e2b.ts +29 -1
- package/src/sandbox.ts +1 -1
- package/src/types/protocol.ts +15 -1
|
@@ -80,13 +80,17 @@ export type AgentMessageSummary = {
|
|
|
80
80
|
* an agent.message event (the terminating `text` carries the whole block) —
|
|
81
81
|
* the loop filters it before summarizing. `task_notification` is filtered
|
|
82
82
|
* too: it is session-transport metadata (a parent harness's background-task
|
|
83
|
-
* completion echo), not the agent's own output.
|
|
83
|
+
* completion echo), not the agent's own output. `harness_notice` likewise:
|
|
84
|
+
* harness-composed advisory text (synthetic assistant messages), never the
|
|
85
|
+
* agent speaking. */
|
|
84
86
|
type DurableAgentMessage = Exclude<AgentMessage, {
|
|
85
87
|
type: "text_delta";
|
|
86
88
|
} | {
|
|
87
89
|
type: "usage_delta";
|
|
88
90
|
} | {
|
|
89
91
|
type: "task_notification";
|
|
92
|
+
} | {
|
|
93
|
+
type: "harness_notice";
|
|
90
94
|
}>;
|
|
91
95
|
export declare function summarizeAgentMessage(msg: DurableAgentMessage): AgentMessageSummary;
|
|
92
96
|
export type AgentLifecycleEvent = {
|
package/dist/index.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export { RequestContext, ReservedKeyError, NonSerialisableValueError, AC_RESERVE
|
|
|
24
24
|
export type { RequestContextReserved, RequestContextWire, } from "./request-context/index.js";
|
|
25
25
|
export { Verdict, runProcessorChain, denyTools, humanApproval, requireScope, redactPattern, createGatePauseProcessor, createAskHumanProcessor, ASK_USER_QUESTION_TOOL, } from "./processors/index.js";
|
|
26
26
|
export type { Processor, ProcessorContext, ProcessorVerdict, ToolCall, GatePausePolicy, GatePauseApproval, GatePauseConnection, } from "./processors/index.js";
|
|
27
|
-
export type { AgentMessage, AgentMessageInit, AgentMessageText, AgentMessageThinking, AgentMessageToolUse, AgentMessageToolResult, AgentMessageDone, AgentMessageError, AgentMessageUsage, AgentMessageTaskNotification, AgentStatus, } from "./types/protocol.js";
|
|
27
|
+
export type { AgentMessage, AgentMessageInit, AgentMessageText, AgentMessageThinking, AgentMessageToolUse, AgentMessageToolResult, AgentMessageDone, AgentMessageError, AgentMessageUsage, AgentMessageTaskNotification, AgentMessageHarnessNotice, AgentStatus, } from "./types/protocol.js";
|
|
28
28
|
export type { SandboxProvider, DesktopSandboxProvider, SandboxPtyOpts, SandboxPtyHandle, } from "./types/sandbox.js";
|
|
29
29
|
export { AgentComposeClient } from "./client.js";
|
|
30
30
|
export type { RegisterResult, RegisterWorkflowInput, RuntimeSourceInput, TemplateSourceRef, InvokeWorkflowOptions, InvokeAndWaitOptions, InvokeResult, FundingChoice, InlineWorkflowPayload, InvokeInlineOptions, InvokeInlineAndWaitOptions, ListSnapshotsOptions, TemplateRow, ListTemplatesOptions, CreateFactoryInput, UpdateFactoryInput, SecretOptions, SetSecretResult, SecretListEntry, SessionSecretEntry, SessionSecretInput, SessionSecretRequestCreated, SessionSecretRequestStatus, CreateApiKeyInput, StreamRunLogsOptions, TeamMember, Mention, CreateMentionsInput, EventSubjectType, EventRow, ReportEventInput, ListEventsOptions, ListEventsResult, RunLogLine, ListRunLogsOptions, RunListEntry, ListRunsOptions, RunDetail, FundingLane, RunFundingStamp, RunFundingUsageRow, RunFundingResponse, RunStepUsage, RunStepUsageResponse, RegisteredRuntime, RunState, RunStatus, FactoryRow, SnapshotListEntry, SnapshotListResponse, ApiKey, ApiKeyCreated, ListApiKeysOptions, ApiKeyPage, UsageRollupRow, UsageResponse, CancelRunResponse, RequestAgentPauseOptions, RequestAgentPauseResponse, SendAgentMessageOptions, SendAgentMessageResponse, AnswerSteerOptions, ResumePauseOptions, ResumePauseResponse, ResumePauseSuccess, ResumePausePending, ResumePauseActor, ConversationMessagePart, ConversationRow, ConversationMessageRow, ConversationsPage, ConversationDetail, ConversationThread, ConversationPageContext, SendConversationMessageInput, SendConversationMessageResult, UnnotifiedMention, ConversationTurnState, ConversationPresenceSnapshot, StreamConversationOptions, AgentListRow, CreateCloudSessionInput, CloudSessionCreated, SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked, BackgroundWorkHeld, SessionFileChange, SessionChangeSet, SessionChangeStats, SessionDiffReviewBound, ReviewSuggestion, ReviewSuggestionInput, ReviewNotesPublished, ReviewGitCredential, SessionReviewDocument, ReviewSuggestionDecision, ReviewSuggestionDecisions, SessionMergeReportDetail, SessionMergeReport, SessionMergeGated, SessionDiscardReport, SessionPreflightFileRow, SessionChangePreflight, SessionRebaseReport, DriveMountSession, CreateDriveMountSessionInput, ChannelSessionStatus, ChannelSessionRow, ChannelSessionsResponse, SessionChannelMessagePosted, SessionDirectMessageSent, BranchClaimInfo, BranchClaimGranted, BranchClaimsReleased, BranchClaimState, SessionPerfBucket, SessionPerfHistoryPage, FactoryPerfSummary, FactoryFileSearchRow, FactoryFolderSearchRow, SearchFactoryFilesOptions, FactoryFileSearchResult, PublicFileLinkState, FactoryFileListRow, ListFactoryFilesOptions, FactoryFileListPage, ConversationMemberRole, ConversationMember, DocumentCapability, TemplateCapability, ScopeGrant, ArtifactScope, RunContext, SetScopeGrantsInput, SetTemplateScopeInput, TemplateDetail, Project, ProjectsPage, ProjectRole, ProjectMember, ProjectObject, ProjectObjectsPage, ProjectSkippedFile, ProjectPreviewFile, ProjectAddPreview, AddProjectObjectInput, AddProjectObjectResult, RefreshProjectObjectResult, ComplianceScopeKind, ComplianceStatus, ComplianceSession, ComplianceAccess, RequestComplianceSessionInput, ListComplianceSessionsOptions, ComplianceSessionsPage, ListComplianceAccessesOptions, ComplianceAccessesPage, } from "./client.js";
|
|
@@ -51,7 +51,7 @@ export { createCodexRuntime, codexSpec } from "./runtimes/codex.js";
|
|
|
51
51
|
export type { CodexRuntimeConfig, CodexReasoningEffort } from "./runtimes/codex.js";
|
|
52
52
|
export { default as codexRuntime } from "./runtimes/codex.js";
|
|
53
53
|
export type { CliReasoningEffort } from "./runtimes/_cli-agent.js";
|
|
54
|
-
export { shellQuote, uniquePromptPath, turnLivenessProbeCommand, parseTurnProbeOutput, livenessVerdictFromReading, runnerBusySentinelFragment, exitEventPushCommand, injectAppendAndAckCommand, INJECT_ACK_EXEC_TIMEOUT_MS, streamInputFeederFragment, sessionEnvSourceFragment, credEnvSourceFragment, residentFeederFragment, residentIdleCheckFragment, residentIdleWatchdogFragment, residentResultCountCommand, deferredExitEventPushCommand, RESIDENT_IDLE_TTL_S, RESIDENT_WATCHDOG_POLL_S, RESIDENT_RESULT_LINE_PREFIX, SESSION_ENV_FILE_RELPATH, LAUNCH_EXEC_TIMEOUT_MS, TURN_PROBE_TIMEOUT_MS, INSTALL_PROBE_TIMEOUT_MS, CLI_INSTALL_TIMEOUT_MS, RUNNER_HEARTBEAT_INTERVAL_SECONDS, RUNNER_HEARTBEAT_STALE_MS, } from "./runtimes/_cli-agent.js";
|
|
54
|
+
export { shellQuote, uniquePromptPath, turnLivenessProbeCommand, parseTurnProbeOutput, livenessVerdictFromReading, runnerBusySentinelFragment, exitEventPushCommand, injectAppendAndAckCommand, INJECT_ACK_EXEC_TIMEOUT_MS, wrapMidTurnUserMessage, streamInputFeederFragment, sessionEnvSourceFragment, credEnvSourceFragment, residentFeederFragment, residentIdleCheckFragment, residentIdleWatchdogFragment, residentResultCountCommand, deferredExitEventPushCommand, RESIDENT_IDLE_TTL_S, RESIDENT_WATCHDOG_POLL_S, RESIDENT_RESULT_LINE_PREFIX, SESSION_ENV_FILE_RELPATH, LAUNCH_EXEC_TIMEOUT_MS, TURN_PROBE_TIMEOUT_MS, INSTALL_PROBE_TIMEOUT_MS, CLI_INSTALL_TIMEOUT_MS, RUNNER_HEARTBEAT_INTERVAL_SECONDS, RUNNER_HEARTBEAT_STALE_MS, } from "./runtimes/_cli-agent.js";
|
|
55
55
|
export type { TurnProbeReading, CliAgentSpec } from "./runtimes/_cli-agent.js";
|
|
56
56
|
export { perfSamplerFunctionFragment, standalonePerfProbeCommand, parsePerfToken, parsePerfProbeOutput, PERF_SAMPLE_EVERY_BEATS, PERF_PROBE_WINDOW_SECONDS, PERF_TOKEN_MAX_CHARS, } from "./agent/perf-sampler.js";
|
|
57
57
|
export type { GuestPerfSample } from "./agent/perf-sampler.js";
|
|
@@ -73,7 +73,7 @@ export { default as claudeCodeRuntime } from "./runtimes/claude-code.js";
|
|
|
73
73
|
export { bashTool, codingTools, editTool, readTool, writeTool } from "./tools/index.js";
|
|
74
74
|
export type { CodingTool } from "./tools/index.js";
|
|
75
75
|
export type { RunEvent } from "./types/events.js";
|
|
76
|
-
export { createSandbox, reconnectSandbox, killAllSandboxes, killSandboxById, getSandboxQuotas, listOwnedSandboxes, deleteSandboxSnapshot, snapshotResolves, makeSandboxProvider, makeDesktopSandboxProvider, parseSseExecStream, AGENT_COMPOSE_TAG, SANDBOX_SIZES, SANDBOX_MACHINES, SANDBOX_VCPUS, DEFAULT_SANDBOX_SIZE, SESSION_DEFAULT_SANDBOX_SIZE, E2B_TEMPLATE_SIZES, E2B_MAX_VCPUS, E2B_MAX_MEMORY_MB, VERCEL_MEMORY_MB_PER_VCPU, isE2bSupportedSize, isVercelSupportedSize, sandboxSizeLabel, e2bMachineSpec, e2bBaseTemplate, e2bAgentEnvTemplate, isPlatformE2bTemplateAlias, E2B_DEVBOX_TEMPLATE, E2B_DEVBOX_SPEC, E2B_DEVBOX_RECIPE_VERSION, e2bDevboxTemplateRef } from "./sandbox.js";
|
|
76
|
+
export { createSandbox, reconnectSandbox, killAllSandboxes, killSandboxById, getSandboxQuotas, listOwnedSandboxes, deleteSandboxSnapshot, snapshotResolves, makeSandboxProvider, makeDesktopSandboxProvider, parseSseExecStream, AGENT_COMPOSE_TAG, SANDBOX_SIZES, SANDBOX_MACHINES, SANDBOX_VCPUS, DEFAULT_SANDBOX_SIZE, SESSION_DEFAULT_SANDBOX_SIZE, E2B_TEMPLATE_SIZES, E2B_MAX_VCPUS, E2B_MAX_MEMORY_MB, VERCEL_MEMORY_MB_PER_VCPU, isE2bSupportedSize, isVercelSupportedSize, sandboxSizeLabel, e2bMachineSpec, e2bBaseTemplate, e2bAgentEnvTemplate, e2bMaxSandboxMs, isPlatformE2bTemplateAlias, E2B_DEVBOX_TEMPLATE, E2B_DEVBOX_SPEC, E2B_DEVBOX_RECIPE_VERSION, e2bDevboxTemplateRef } from "./sandbox.js";
|
|
77
77
|
export { SandboxUnavailableError, SANDBOX_UNAVAILABLE_PREFIX } from "./sandbox-errors.js";
|
|
78
78
|
export type { SandboxCreateOpts, SandboxNetworkPolicy, SandboxNetworkHeaderTransform, SandboxNetworkAllowRule, SandboxNetworkSubnetPolicy, SandboxProviderName, SandboxQuotaResult, OwnedSandboxResult, OwnedSandbox, SandboxSize, ParseSseExecStreamOptions, SandboxCommandRunOptions, SandboxCommandResult, } from "./sandbox.js";
|
|
79
79
|
export type { SandboxResources, DriveMergePolicy } from "./types/workflow-metadata.js";
|
package/dist/index.js
CHANGED
|
@@ -3413,7 +3413,7 @@ Re-emit the COMPLETE corrected <response> JSON now: every required field present
|
|
|
3413
3413
|
continue;
|
|
3414
3414
|
}
|
|
3415
3415
|
const msg = outputVerdict.value;
|
|
3416
|
-
if (msg.type === "text_delta" || msg.type === "usage_delta" || msg.type === "task_notification")
|
|
3416
|
+
if (msg.type === "text_delta" || msg.type === "usage_delta" || msg.type === "task_notification" || msg.type === "harness_notice")
|
|
3417
3417
|
continue;
|
|
3418
3418
|
opts.onAgentEvent?.(iteration, msg);
|
|
3419
3419
|
const summary = summarizeAgentMessage(msg);
|
|
@@ -4038,6 +4038,13 @@ function streamInputFeederFragment(paths) {
|
|
|
4038
4038
|
const q2 = shellQuote;
|
|
4039
4039
|
return `rm -f ${q2(paths.fifoPath)}; mkfifo ${q2(paths.fifoPath)}; ` + `: > ${q2(paths.inboxPath)}; echo 0 > ${q2(paths.deliveredPath)}; ` + `( cat ${q2(paths.promptPath)}; ac_fed=0; while kill -0 "$$" 2>/dev/null; do ` + `if tail -c ${INJECT_FEEDER_RESULT_TAIL_BYTES} ${q2(paths.outPath)} 2>/dev/null | grep -q '"type":"result"'; then break; fi; ` + `ac_lines=$(wc -l < ${q2(paths.inboxPath)} 2>/dev/null || echo 0); ac_lines=\${ac_lines:-0}; ` + `if [ "$ac_lines" -gt "$ac_fed" ]; then ` + `sed -n "$((ac_fed+1)),$((ac_lines))p" ${q2(paths.inboxPath)}; ac_fed=$ac_lines; ` + `echo "$ac_fed" > ${q2(paths.deliveredPath)}; fi; ` + `sleep ${INJECT_FEEDER_POLL_SECONDS}; done ) > ${q2(paths.fifoPath)} 2>/dev/null </dev/null & `;
|
|
4040
4040
|
}
|
|
4041
|
+
function wrapMidTurnUserMessage(text) {
|
|
4042
|
+
return `The user sent a new message while you were working:
|
|
4043
|
+
|
|
4044
|
+
` + text + `
|
|
4045
|
+
|
|
4046
|
+
Address the message above as you continue this turn.`;
|
|
4047
|
+
}
|
|
4041
4048
|
function injectAppendAndAckCommand(args) {
|
|
4042
4049
|
const q2 = shellQuote;
|
|
4043
4050
|
return `printf '%s\\n' ${q2(args.line)} >> ${q2(args.inboxPath)}; i=0; ` + `while [ "$i" -lt ${INJECT_ACK_POLL_ATTEMPTS} ]; do ` + `d=$(cat ${q2(args.deliveredPath)} 2>/dev/null || echo 0); ` + `[ "\${d:-0}" -ge ${args.target} ] && exit 0; ` + `kill -0 ${args.pid} 2>/dev/null || exit 4; ` + `sleep ${INJECT_ACK_POLL_SECONDS}; i=$((i+1)); done; exit 5`;
|
|
@@ -4172,7 +4179,7 @@ class CliAgentRunner {
|
|
|
4172
4179
|
const streamInput = this.spec.streamInput;
|
|
4173
4180
|
if (!inject || !streamInput || this.guestDetached)
|
|
4174
4181
|
return "unsupported";
|
|
4175
|
-
const line = streamInput.messageLine(text);
|
|
4182
|
+
const line = streamInput.messageLine(wrapMidTurnUserMessage(text));
|
|
4176
4183
|
const attempt = inject.chain.then(async () => {
|
|
4177
4184
|
if (this.turnInject !== inject)
|
|
4178
4185
|
return "closed";
|
|
@@ -5115,10 +5122,11 @@ var claudeCodeSpec = {
|
|
|
5115
5122
|
switch (p.type) {
|
|
5116
5123
|
case "assistant": {
|
|
5117
5124
|
const message = p.message;
|
|
5125
|
+
const synthetic = message?.model === "<synthetic>";
|
|
5118
5126
|
const blocks2 = Array.isArray(message?.content) ? message.content : [];
|
|
5119
5127
|
return blocks2.flatMap((b) => {
|
|
5120
5128
|
if (b.type === "text" && typeof b.text === "string" && b.text.length > 0) {
|
|
5121
|
-
return [{ type: "text", text: b.text, timestamp: ts }];
|
|
5129
|
+
return [{ type: synthetic ? "harness_notice" : "text", text: b.text, timestamp: ts }];
|
|
5122
5130
|
}
|
|
5123
5131
|
if (b.type === "thinking" && typeof b.thinking === "string" && b.thinking.length > 0) {
|
|
5124
5132
|
return [{ type: "thinking", text: b.thinking, timestamp: ts }];
|
|
@@ -5434,6 +5442,7 @@ function makeSandboxProvider(sb) {
|
|
|
5434
5442
|
function e2bMaxSandboxMs() {
|
|
5435
5443
|
return Number(process.env.E2B_MAX_SANDBOX_MS) || 60 * 60 * 1000;
|
|
5436
5444
|
}
|
|
5445
|
+
var e2bExtendClampWarned = false;
|
|
5437
5446
|
function wrapE2bBackgroundProcess(handle) {
|
|
5438
5447
|
return {
|
|
5439
5448
|
pid: handle.pid,
|
|
@@ -5527,7 +5536,12 @@ function makeE2bSandboxProvider(sb) {
|
|
|
5527
5536
|
return { resumeHandle: sb.sandboxId };
|
|
5528
5537
|
},
|
|
5529
5538
|
async extendLifetime(ms) {
|
|
5530
|
-
|
|
5539
|
+
const maxMs = e2bMaxSandboxMs();
|
|
5540
|
+
if (ms > maxMs && !e2bExtendClampWarned) {
|
|
5541
|
+
e2bExtendClampWarned = true;
|
|
5542
|
+
console.warn(`[sandbox] e2b lifetime extension clamped: requested ${ms}ms exceeds the plan cap ${maxMs}ms — ` + `the kill-clock backstop fires at the cap, not the requested horizon. Raise E2B_MAX_SANDBOX_MS on plans that allow more.`);
|
|
5543
|
+
}
|
|
5544
|
+
await sb.setTimeout(Math.min(ms, maxMs));
|
|
5531
5545
|
},
|
|
5532
5546
|
async updateNetworkPolicy(policy) {
|
|
5533
5547
|
await sb.updateNetwork(toE2bNetwork(policy));
|
|
@@ -5559,6 +5573,7 @@ var e2bProviderDef = {
|
|
|
5559
5573
|
const sandboxOpts = {
|
|
5560
5574
|
...rest,
|
|
5561
5575
|
timeoutMs: Math.min(timeoutMs, maxMs),
|
|
5576
|
+
lifecycle: { onTimeout: "pause", autoResume: true },
|
|
5562
5577
|
...networkPolicy ? { network: toE2bNetwork(networkPolicy) } : {}
|
|
5563
5578
|
};
|
|
5564
5579
|
const resolvedSize = size ?? DEFAULT_SANDBOX_SIZE;
|
|
@@ -8976,6 +8991,7 @@ function createDisplayPromoter(ctx = {}) {
|
|
|
8976
8991
|
export {
|
|
8977
8992
|
writeTool,
|
|
8978
8993
|
writeAgentContext,
|
|
8994
|
+
wrapMidTurnUserMessage,
|
|
8979
8995
|
uniquePromptPath,
|
|
8980
8996
|
turnLivenessProbeCommand,
|
|
8981
8997
|
streamInputFeederFragment,
|
|
@@ -9053,6 +9069,7 @@ export {
|
|
|
9053
9069
|
explainBundleFailure,
|
|
9054
9070
|
exitEventPushCommand,
|
|
9055
9071
|
editTool,
|
|
9072
|
+
e2bMaxSandboxMs,
|
|
9056
9073
|
e2bMachineSpec,
|
|
9057
9074
|
e2bDevboxTemplateRef,
|
|
9058
9075
|
e2bBaseTemplate,
|
|
@@ -149,6 +149,18 @@ export declare function streamInputFeederFragment(paths: {
|
|
|
149
149
|
deliveredPath: string;
|
|
150
150
|
outPath: string;
|
|
151
151
|
}): string;
|
|
152
|
+
/** The instructional envelope around a user message delivered INTO a
|
|
153
|
+
* running turn. Claude Code's own interactive harness wraps queued
|
|
154
|
+
* mid-turn input with an explicit "address this as you continue"
|
|
155
|
+
* instruction; a bare `--input-format stream-json` line carries no such
|
|
156
|
+
* framing, so an injected message reads like any other transcript text
|
|
157
|
+
* and the model tends to continue its work narration without
|
|
158
|
+
* acknowledging it. Every mid-turn inject producer (the runner's
|
|
159
|
+
* `injectUserMessage` and the workflow lane's `deliverInjectedMessage`
|
|
160
|
+
* activity) MUST wrap through here — the wrapper rides only the wire to
|
|
161
|
+
* the model; the stored conversation row keeps the user's raw text.
|
|
162
|
+
* The text is pinned by tests: change it deliberately or not at all. */
|
|
163
|
+
export declare function wrapMidTurnUserMessage(text: string): string;
|
|
152
164
|
/** The one delivery-ack exec `injectUserMessage` runs: append the message
|
|
153
165
|
* line to the durable inbox, then wait for the feeder's delivered counter
|
|
154
166
|
* to cover it. Exit 0 = delivered into the CLI's stdin pre-result; 4 = the
|
|
@@ -3413,7 +3413,7 @@ Re-emit the COMPLETE corrected <response> JSON now: every required field present
|
|
|
3413
3413
|
continue;
|
|
3414
3414
|
}
|
|
3415
3415
|
const msg = outputVerdict.value;
|
|
3416
|
-
if (msg.type === "text_delta" || msg.type === "usage_delta" || msg.type === "task_notification")
|
|
3416
|
+
if (msg.type === "text_delta" || msg.type === "usage_delta" || msg.type === "task_notification" || msg.type === "harness_notice")
|
|
3417
3417
|
continue;
|
|
3418
3418
|
opts.onAgentEvent?.(iteration, msg);
|
|
3419
3419
|
const summary = summarizeAgentMessage(msg);
|
|
@@ -4038,6 +4038,13 @@ function streamInputFeederFragment(paths) {
|
|
|
4038
4038
|
const q2 = shellQuote;
|
|
4039
4039
|
return `rm -f ${q2(paths.fifoPath)}; mkfifo ${q2(paths.fifoPath)}; ` + `: > ${q2(paths.inboxPath)}; echo 0 > ${q2(paths.deliveredPath)}; ` + `( cat ${q2(paths.promptPath)}; ac_fed=0; while kill -0 "$$" 2>/dev/null; do ` + `if tail -c ${INJECT_FEEDER_RESULT_TAIL_BYTES} ${q2(paths.outPath)} 2>/dev/null | grep -q '"type":"result"'; then break; fi; ` + `ac_lines=$(wc -l < ${q2(paths.inboxPath)} 2>/dev/null || echo 0); ac_lines=\${ac_lines:-0}; ` + `if [ "$ac_lines" -gt "$ac_fed" ]; then ` + `sed -n "$((ac_fed+1)),$((ac_lines))p" ${q2(paths.inboxPath)}; ac_fed=$ac_lines; ` + `echo "$ac_fed" > ${q2(paths.deliveredPath)}; fi; ` + `sleep ${INJECT_FEEDER_POLL_SECONDS}; done ) > ${q2(paths.fifoPath)} 2>/dev/null </dev/null & `;
|
|
4040
4040
|
}
|
|
4041
|
+
function wrapMidTurnUserMessage(text) {
|
|
4042
|
+
return `The user sent a new message while you were working:
|
|
4043
|
+
|
|
4044
|
+
` + text + `
|
|
4045
|
+
|
|
4046
|
+
Address the message above as you continue this turn.`;
|
|
4047
|
+
}
|
|
4041
4048
|
function injectAppendAndAckCommand(args) {
|
|
4042
4049
|
const q2 = shellQuote;
|
|
4043
4050
|
return `printf '%s\\n' ${q2(args.line)} >> ${q2(args.inboxPath)}; i=0; ` + `while [ "$i" -lt ${INJECT_ACK_POLL_ATTEMPTS} ]; do ` + `d=$(cat ${q2(args.deliveredPath)} 2>/dev/null || echo 0); ` + `[ "\${d:-0}" -ge ${args.target} ] && exit 0; ` + `kill -0 ${args.pid} 2>/dev/null || exit 4; ` + `sleep ${INJECT_ACK_POLL_SECONDS}; i=$((i+1)); done; exit 5`;
|
|
@@ -4172,7 +4179,7 @@ class CliAgentRunner {
|
|
|
4172
4179
|
const streamInput = this.spec.streamInput;
|
|
4173
4180
|
if (!inject || !streamInput || this.guestDetached)
|
|
4174
4181
|
return "unsupported";
|
|
4175
|
-
const line = streamInput.messageLine(text);
|
|
4182
|
+
const line = streamInput.messageLine(wrapMidTurnUserMessage(text));
|
|
4176
4183
|
const attempt = inject.chain.then(async () => {
|
|
4177
4184
|
if (this.turnInject !== inject)
|
|
4178
4185
|
return "closed";
|
|
@@ -5115,10 +5122,11 @@ var claudeCodeSpec = {
|
|
|
5115
5122
|
switch (p.type) {
|
|
5116
5123
|
case "assistant": {
|
|
5117
5124
|
const message = p.message;
|
|
5125
|
+
const synthetic = message?.model === "<synthetic>";
|
|
5118
5126
|
const blocks2 = Array.isArray(message?.content) ? message.content : [];
|
|
5119
5127
|
return blocks2.flatMap((b) => {
|
|
5120
5128
|
if (b.type === "text" && typeof b.text === "string" && b.text.length > 0) {
|
|
5121
|
-
return [{ type: "text", text: b.text, timestamp: ts }];
|
|
5129
|
+
return [{ type: synthetic ? "harness_notice" : "text", text: b.text, timestamp: ts }];
|
|
5122
5130
|
}
|
|
5123
5131
|
if (b.type === "thinking" && typeof b.thinking === "string" && b.thinking.length > 0) {
|
|
5124
5132
|
return [{ type: "thinking", text: b.thinking, timestamp: ts }];
|
|
@@ -5434,6 +5442,7 @@ function makeSandboxProvider(sb) {
|
|
|
5434
5442
|
function e2bMaxSandboxMs() {
|
|
5435
5443
|
return Number(process.env.E2B_MAX_SANDBOX_MS) || 60 * 60 * 1000;
|
|
5436
5444
|
}
|
|
5445
|
+
var e2bExtendClampWarned = false;
|
|
5437
5446
|
function wrapE2bBackgroundProcess(handle) {
|
|
5438
5447
|
return {
|
|
5439
5448
|
pid: handle.pid,
|
|
@@ -5527,7 +5536,12 @@ function makeE2bSandboxProvider(sb) {
|
|
|
5527
5536
|
return { resumeHandle: sb.sandboxId };
|
|
5528
5537
|
},
|
|
5529
5538
|
async extendLifetime(ms) {
|
|
5530
|
-
|
|
5539
|
+
const maxMs = e2bMaxSandboxMs();
|
|
5540
|
+
if (ms > maxMs && !e2bExtendClampWarned) {
|
|
5541
|
+
e2bExtendClampWarned = true;
|
|
5542
|
+
console.warn(`[sandbox] e2b lifetime extension clamped: requested ${ms}ms exceeds the plan cap ${maxMs}ms — ` + `the kill-clock backstop fires at the cap, not the requested horizon. Raise E2B_MAX_SANDBOX_MS on plans that allow more.`);
|
|
5543
|
+
}
|
|
5544
|
+
await sb.setTimeout(Math.min(ms, maxMs));
|
|
5531
5545
|
},
|
|
5532
5546
|
async updateNetworkPolicy(policy) {
|
|
5533
5547
|
await sb.updateNetwork(toE2bNetwork(policy));
|
|
@@ -5559,6 +5573,7 @@ var e2bProviderDef = {
|
|
|
5559
5573
|
const sandboxOpts = {
|
|
5560
5574
|
...rest,
|
|
5561
5575
|
timeoutMs: Math.min(timeoutMs, maxMs),
|
|
5576
|
+
lifecycle: { onTimeout: "pause", autoResume: true },
|
|
5562
5577
|
...networkPolicy ? { network: toE2bNetwork(networkPolicy) } : {}
|
|
5563
5578
|
};
|
|
5564
5579
|
const resolvedSize = size ?? DEFAULT_SANDBOX_SIZE;
|
package/dist/sandbox.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export { AGENT_COMPOSE_TAG } from "./sandbox/provider-def.js";
|
|
|
20
20
|
export type { SandboxCreateOpts, OwnedSandbox } from "./sandbox/provider-def.js";
|
|
21
21
|
export { parseSseExecStream } from "./sandbox/exec-stream.js";
|
|
22
22
|
export type { ParseSseExecStreamOptions } from "./sandbox/exec-stream.js";
|
|
23
|
-
export { makeSandboxProvider } from "./sandbox/providers/e2b.js";
|
|
23
|
+
export { makeSandboxProvider, e2bMaxSandboxMs } from "./sandbox/providers/e2b.js";
|
|
24
24
|
export { makeDesktopSandboxProvider } from "./sandbox/providers/desktop.js";
|
|
25
25
|
export { VERCEL_MAX_TAGS, buildVercelTags } from "./sandbox/providers/vercel.js";
|
|
26
26
|
export { makeLocalSandboxProvider } from "./sandbox/providers/local.js";
|
package/dist/types/protocol.d.ts
CHANGED
|
@@ -144,7 +144,19 @@ export interface AgentMessageTaskNotification extends AgentMessageBase {
|
|
|
144
144
|
durationMs?: number;
|
|
145
145
|
};
|
|
146
146
|
}
|
|
147
|
-
|
|
147
|
+
/** HARNESS-authored notice text — content the CLI composed itself rather
|
|
148
|
+
* than the model speaking: slash-command stdout, model/skills advisories,
|
|
149
|
+
* queued-input notes. claude-code marks these structurally (assistant
|
|
150
|
+
* events whose `message.model` is `"<synthetic>"`), and the normaliser
|
|
151
|
+
* forwards them under this kind so downstream persists them as collapsed
|
|
152
|
+
* system notices instead of agent prose (the 2026-08-22 "(session model)"
|
|
153
|
+
* / skills-reminder leak: harness plumbing rendered as message content).
|
|
154
|
+
* Additive kind: existing producers never emit it. */
|
|
155
|
+
export interface AgentMessageHarnessNotice extends AgentMessageBase {
|
|
156
|
+
type: "harness_notice";
|
|
157
|
+
text: string;
|
|
158
|
+
}
|
|
159
|
+
export type AgentMessage = AgentMessageInit | AgentMessageText | AgentMessageTextDelta | AgentMessageThinking | AgentMessageToolUse | AgentMessageToolResult | AgentMessageDone | AgentMessageError | AgentMessageUsage | AgentMessageUsageDelta | AgentMessagePlan | AgentMessageTaskNotification | AgentMessageHarnessNotice;
|
|
148
160
|
/** Status block the agent emits to signal iteration completion or blockers. */
|
|
149
161
|
export interface AgentStatus {
|
|
150
162
|
summary: string;
|
package/package.json
CHANGED
package/src/agent/agent-loop.ts
CHANGED
|
@@ -93,10 +93,13 @@ function preview(value: unknown): string {
|
|
|
93
93
|
* an agent.message event (the terminating `text` carries the whole block) —
|
|
94
94
|
* the loop filters it before summarizing. `task_notification` is filtered
|
|
95
95
|
* too: it is session-transport metadata (a parent harness's background-task
|
|
96
|
-
* completion echo), not the agent's own output.
|
|
96
|
+
* completion echo), not the agent's own output. `harness_notice` likewise:
|
|
97
|
+
* harness-composed advisory text (synthetic assistant messages), never the
|
|
98
|
+
* agent speaking. */
|
|
97
99
|
type DurableAgentMessage = Exclude<
|
|
98
100
|
AgentMessage,
|
|
99
101
|
{ type: "text_delta" } | { type: "usage_delta" } | { type: "task_notification" }
|
|
102
|
+
| { type: "harness_notice" }
|
|
100
103
|
>;
|
|
101
104
|
|
|
102
105
|
export function summarizeAgentMessage(msg: DurableAgentMessage): AgentMessageSummary {
|
|
@@ -484,8 +487,10 @@ export async function agentLoop<TResponse = unknown>(opts: AgentLoopOpts<TRespon
|
|
|
484
487
|
}
|
|
485
488
|
const msg = outputVerdict.value;
|
|
486
489
|
// A processor cannot re-introduce a live-only chunk; task
|
|
487
|
-
// notifications are transport metadata, never
|
|
488
|
-
|
|
490
|
+
// notifications and harness notices are transport metadata, never
|
|
491
|
+
// loop output.
|
|
492
|
+
if (msg.type === "text_delta" || msg.type === "usage_delta"
|
|
493
|
+
|| msg.type === "task_notification" || msg.type === "harness_notice") continue;
|
|
489
494
|
opts.onAgentEvent?.(iteration, msg);
|
|
490
495
|
// Usage summaries carry the resolved model so the server can price
|
|
491
496
|
// token rows per model without correlating back to agent.spawned.
|
package/src/index.ts
CHANGED
|
@@ -101,6 +101,7 @@ export type {
|
|
|
101
101
|
AgentMessageError,
|
|
102
102
|
AgentMessageUsage,
|
|
103
103
|
AgentMessageTaskNotification,
|
|
104
|
+
AgentMessageHarnessNotice,
|
|
104
105
|
AgentStatus,
|
|
105
106
|
} from "./types/protocol.js";
|
|
106
107
|
|
|
@@ -266,7 +267,7 @@ export {
|
|
|
266
267
|
shellQuote, uniquePromptPath,
|
|
267
268
|
turnLivenessProbeCommand, parseTurnProbeOutput, livenessVerdictFromReading,
|
|
268
269
|
runnerBusySentinelFragment, exitEventPushCommand,
|
|
269
|
-
injectAppendAndAckCommand, INJECT_ACK_EXEC_TIMEOUT_MS,
|
|
270
|
+
injectAppendAndAckCommand, INJECT_ACK_EXEC_TIMEOUT_MS, wrapMidTurnUserMessage,
|
|
270
271
|
streamInputFeederFragment, sessionEnvSourceFragment, credEnvSourceFragment,
|
|
271
272
|
residentFeederFragment, residentIdleCheckFragment, residentIdleWatchdogFragment,
|
|
272
273
|
residentResultCountCommand, deferredExitEventPushCommand,
|
|
@@ -323,7 +324,7 @@ export { createSandbox, reconnectSandbox, killAllSandboxes, killSandboxById,
|
|
|
323
324
|
E2B_TEMPLATE_SIZES, E2B_MAX_VCPUS, E2B_MAX_MEMORY_MB,
|
|
324
325
|
VERCEL_MEMORY_MB_PER_VCPU,
|
|
325
326
|
isE2bSupportedSize, isVercelSupportedSize, sandboxSizeLabel,
|
|
326
|
-
e2bMachineSpec, e2bBaseTemplate, e2bAgentEnvTemplate,
|
|
327
|
+
e2bMachineSpec, e2bBaseTemplate, e2bAgentEnvTemplate, e2bMaxSandboxMs,
|
|
327
328
|
isPlatformE2bTemplateAlias,
|
|
328
329
|
// ADR-0038 "Computer" — the per-member persistent desktop machine template.
|
|
329
330
|
E2B_DEVBOX_TEMPLATE, E2B_DEVBOX_SPEC, E2B_DEVBOX_RECIPE_VERSION,
|
|
@@ -252,6 +252,23 @@ export function streamInputFeederFragment(paths: {
|
|
|
252
252
|
+ `sleep ${INJECT_FEEDER_POLL_SECONDS}; done ) > ${q(paths.fifoPath)} 2>/dev/null </dev/null & `;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
+
/** The instructional envelope around a user message delivered INTO a
|
|
256
|
+
* running turn. Claude Code's own interactive harness wraps queued
|
|
257
|
+
* mid-turn input with an explicit "address this as you continue"
|
|
258
|
+
* instruction; a bare `--input-format stream-json` line carries no such
|
|
259
|
+
* framing, so an injected message reads like any other transcript text
|
|
260
|
+
* and the model tends to continue its work narration without
|
|
261
|
+
* acknowledging it. Every mid-turn inject producer (the runner's
|
|
262
|
+
* `injectUserMessage` and the workflow lane's `deliverInjectedMessage`
|
|
263
|
+
* activity) MUST wrap through here — the wrapper rides only the wire to
|
|
264
|
+
* the model; the stored conversation row keeps the user's raw text.
|
|
265
|
+
* The text is pinned by tests: change it deliberately or not at all. */
|
|
266
|
+
export function wrapMidTurnUserMessage(text: string): string {
|
|
267
|
+
return "The user sent a new message while you were working:\n\n"
|
|
268
|
+
+ text
|
|
269
|
+
+ "\n\nAddress the message above as you continue this turn.";
|
|
270
|
+
}
|
|
271
|
+
|
|
255
272
|
/** The one delivery-ack exec `injectUserMessage` runs: append the message
|
|
256
273
|
* line to the durable inbox, then wait for the feeder's delivered counter
|
|
257
274
|
* to cover it. Exit 0 = delivered into the CLI's stdin pre-result; 4 = the
|
|
@@ -823,7 +840,10 @@ export class CliAgentRunner implements ModelExecutionContract {
|
|
|
823
840
|
const inject = this.turnInject;
|
|
824
841
|
const streamInput = this.spec.streamInput;
|
|
825
842
|
if (!inject || !streamInput || this.guestDetached) return "unsupported";
|
|
826
|
-
|
|
843
|
+
// The address-this envelope (wrapMidTurnUserMessage): a raw injected
|
|
844
|
+
// line carries no framing, and the model continues its narration
|
|
845
|
+
// without acknowledging the message.
|
|
846
|
+
const line = streamInput.messageLine(wrapMidTurnUserMessage(text));
|
|
827
847
|
const attempt = inject.chain.then(async (): Promise<"delivered" | "pending" | "closed"> => {
|
|
828
848
|
if (this.turnInject !== inject) return "closed";
|
|
829
849
|
// The append lands whether or not the ack times out, so the target
|
|
@@ -291,11 +291,20 @@ export const claudeCodeSpec: CliAgentSpec = {
|
|
|
291
291
|
switch (p.type) {
|
|
292
292
|
// Assistant API message: content blocks → text / thinking / tool_use.
|
|
293
293
|
case "assistant": {
|
|
294
|
-
const message = p.message as { content?: ClaudeContentBlock[] } | undefined;
|
|
294
|
+
const message = p.message as { content?: ClaudeContentBlock[]; model?: unknown } | undefined;
|
|
295
|
+
// HARNESS-synthesized assistant messages: the CLI stamps
|
|
296
|
+
// `message.model: "<synthetic>"` on content it composed itself —
|
|
297
|
+
// slash-command stdout, model/skills advisories — never the model
|
|
298
|
+
// speaking (verified live on claude 2.1.212 and 2.1.236: /model and
|
|
299
|
+
// /context output both arrive this way). Their text forwards as
|
|
300
|
+
// `harness_notice` so downstream renders it as a quiet system note
|
|
301
|
+
// instead of agent prose (the 2026-08-22 plumbing-as-content leak).
|
|
302
|
+
// Structural marker only — no content sniffing.
|
|
303
|
+
const synthetic = message?.model === "<synthetic>";
|
|
295
304
|
const blocks = Array.isArray(message?.content) ? message.content : [];
|
|
296
305
|
return blocks.flatMap((b): AgentMessage[] => {
|
|
297
306
|
if (b.type === "text" && typeof b.text === "string" && b.text.length > 0) {
|
|
298
|
-
return [{ type: "text", text: b.text, timestamp: ts }];
|
|
307
|
+
return [{ type: synthetic ? "harness_notice" : "text", text: b.text, timestamp: ts }];
|
|
299
308
|
}
|
|
300
309
|
if (b.type === "thinking" && typeof b.thinking === "string" && b.thinking.length > 0) {
|
|
301
310
|
return [{ type: "thinking", text: b.thinking, timestamp: ts }];
|
|
@@ -104,6 +104,9 @@ export function e2bMaxSandboxMs(): number {
|
|
|
104
104
|
return Number(process.env.E2B_MAX_SANDBOX_MS) || 60 * 60 * 1000;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/** Once-per-process latch for the extendLifetime clamp warning below. */
|
|
108
|
+
let e2bExtendClampWarned = false;
|
|
109
|
+
|
|
107
110
|
/** Wrap an E2B `CommandHandle` as a provider-agnostic background process.
|
|
108
111
|
* `wait()` is normalised NOT to throw on a non-zero exit (mirroring the
|
|
109
112
|
* `commands.run` contract in `makeSandboxProvider`) so callers branch on
|
|
@@ -245,7 +248,20 @@ function makeE2bSandboxProvider(sb: Sandbox): SandboxProvider {
|
|
|
245
248
|
// the create timeout — an over-cap push would 400 and leave the OLD
|
|
246
249
|
// (possibly short) deadline standing.
|
|
247
250
|
async extendLifetime(ms) {
|
|
248
|
-
|
|
251
|
+
const maxMs = e2bMaxSandboxMs();
|
|
252
|
+
if (ms > maxMs && !e2bExtendClampWarned) {
|
|
253
|
+
// Once per process, not per extension: every far-horizon push clamps
|
|
254
|
+
// identically, and the fact worth surfacing is the CONFIGURATION —
|
|
255
|
+
// the server believes it bought `ms` of kill-clock slack but the
|
|
256
|
+
// plan cap silently shortens it, so the "orphan backstop" can reap
|
|
257
|
+
// a machine the platform still considers covered.
|
|
258
|
+
e2bExtendClampWarned = true;
|
|
259
|
+
console.warn(
|
|
260
|
+
`[sandbox] e2b lifetime extension clamped: requested ${ms}ms exceeds the plan cap ${maxMs}ms — ` +
|
|
261
|
+
`the kill-clock backstop fires at the cap, not the requested horizon. Raise E2B_MAX_SANDBOX_MS on plans that allow more.`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
await sb.setTimeout(Math.min(ms, maxMs));
|
|
249
265
|
},
|
|
250
266
|
// Push a freshly-resolved egress policy onto the live sandbox via E2B's
|
|
251
267
|
// native `updateNetwork` — the E2B analogue of Vercel's `update({
|
|
@@ -317,6 +333,15 @@ export const e2bProviderDef: SandboxProviderDef = {
|
|
|
317
333
|
const sandboxOpts = {
|
|
318
334
|
...rest,
|
|
319
335
|
timeoutMs: Math.min(timeoutMs, maxMs),
|
|
336
|
+
// Lifetime-deadline expiry PAUSES instead of killing, and the next
|
|
337
|
+
// incoming connection auto-resumes (recycle forensics 2026-08-23:
|
|
338
|
+
// uncontrolled kill-clock loss was the dominant machine-end cause
|
|
339
|
+
// ~60:1 — under this policy a missed extension becomes a resumable
|
|
340
|
+
// park our redial paths revive). CREATE-time policy, persistent for
|
|
341
|
+
// the sandbox's life — connect() takes no lifecycle. NOTE: this is
|
|
342
|
+
// e2b's `lifecycle` contract, NOT the deprecated `autoPause` flag
|
|
343
|
+
// (which the create body would silently drop).
|
|
344
|
+
lifecycle: { onTimeout: "pause" as const, autoResume: true },
|
|
320
345
|
...(networkPolicy ? { network: toE2bNetwork(networkPolicy) } : {}),
|
|
321
346
|
};
|
|
322
347
|
// Boot template resolution, in priority order:
|
|
@@ -339,6 +364,9 @@ export const e2bProviderDef: SandboxProviderDef = {
|
|
|
339
364
|
);
|
|
340
365
|
},
|
|
341
366
|
reconnect: async (sandboxId) => makeE2bSandboxProvider(
|
|
367
|
+
// The pause-on-timeout policy is create-time and persistent — connect
|
|
368
|
+
// takes no lifecycle options (the old per-connect autoPause flag is
|
|
369
|
+
// deprecated API-side).
|
|
342
370
|
await Sandbox.connect(sandboxId, { apiKey: process.env.E2B_API_KEY ?? "", timeoutMs: 60 * 60 * 1000 }),
|
|
343
371
|
),
|
|
344
372
|
killAll: async () => {
|
package/src/sandbox.ts
CHANGED
|
@@ -54,7 +54,7 @@ export type { SandboxCreateOpts, OwnedSandbox } from "./sandbox/provider-def.js"
|
|
|
54
54
|
export { parseSseExecStream } from "./sandbox/exec-stream.js";
|
|
55
55
|
export type { ParseSseExecStreamOptions } from "./sandbox/exec-stream.js";
|
|
56
56
|
|
|
57
|
-
export { makeSandboxProvider } from "./sandbox/providers/e2b.js";
|
|
57
|
+
export { makeSandboxProvider, e2bMaxSandboxMs } from "./sandbox/providers/e2b.js";
|
|
58
58
|
export { makeDesktopSandboxProvider } from "./sandbox/providers/desktop.js";
|
|
59
59
|
export { VERCEL_MAX_TAGS, buildVercelTags } from "./sandbox/providers/vercel.js";
|
|
60
60
|
export { makeLocalSandboxProvider } from "./sandbox/providers/local.js";
|
package/src/types/protocol.ts
CHANGED
|
@@ -147,6 +147,19 @@ export interface AgentMessageTaskNotification extends AgentMessageBase {
|
|
|
147
147
|
usage?: { tokens?: number; toolUses?: number; durationMs?: number };
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
/** HARNESS-authored notice text — content the CLI composed itself rather
|
|
151
|
+
* than the model speaking: slash-command stdout, model/skills advisories,
|
|
152
|
+
* queued-input notes. claude-code marks these structurally (assistant
|
|
153
|
+
* events whose `message.model` is `"<synthetic>"`), and the normaliser
|
|
154
|
+
* forwards them under this kind so downstream persists them as collapsed
|
|
155
|
+
* system notices instead of agent prose (the 2026-08-22 "(session model)"
|
|
156
|
+
* / skills-reminder leak: harness plumbing rendered as message content).
|
|
157
|
+
* Additive kind: existing producers never emit it. */
|
|
158
|
+
export interface AgentMessageHarnessNotice extends AgentMessageBase {
|
|
159
|
+
type: "harness_notice";
|
|
160
|
+
text: string;
|
|
161
|
+
}
|
|
162
|
+
|
|
150
163
|
export type AgentMessage =
|
|
151
164
|
| AgentMessageInit
|
|
152
165
|
| AgentMessageText
|
|
@@ -159,7 +172,8 @@ export type AgentMessage =
|
|
|
159
172
|
| AgentMessageUsage
|
|
160
173
|
| AgentMessageUsageDelta
|
|
161
174
|
| AgentMessagePlan
|
|
162
|
-
| AgentMessageTaskNotification
|
|
175
|
+
| AgentMessageTaskNotification
|
|
176
|
+
| AgentMessageHarnessNotice;
|
|
163
177
|
|
|
164
178
|
/** Status block the agent emits to signal iteration completion or blockers. */
|
|
165
179
|
export interface AgentStatus {
|