@sema-agent/core 2.1.0 → 2.2.0
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/agents/observer.d.ts +14 -0
- package/dist/agents/observer.js +58 -9
- package/dist/agents/send-message-tool.js +55 -10
- package/dist/agents/subagent.d.ts +1 -0
- package/dist/agents/subagent.js +69 -15
- package/dist/core/context-edit.js +16 -3
- package/dist/core/file-snapshot-store.js +10 -1
- package/dist/core/runner/prepare-task.d.ts +1 -0
- package/dist/core/runner/prepare-task.js +29 -5
- package/dist/core/runner/runtask.js +9 -0
- package/dist/core/runner/synthetic-tools.d.ts +1 -0
- package/dist/core/runner/synthetic-tools.js +18 -15
- package/dist/core/runner/turn-attachments.d.ts +12 -2
- package/dist/core/runner/turn-attachments.js +33 -3
- package/dist/core/task-registry-agent.d.ts +9 -1
- package/dist/core/task-registry-agent.js +23 -2
- package/dist/core/task-registry-monitor.js +79 -24
- package/dist/core/task-registry-shared.d.ts +13 -1
- package/dist/core/task-registry-shared.js +21 -0
- package/dist/core/task-registry.d.ts +2 -0
- package/dist/core/task-registry.js +24 -26
- package/dist/core/types.d.ts +3 -1
- package/dist/engine/session/memory-repo.js +5 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/orchestration/workflow-size-guideline.d.ts +6 -1
- package/dist/orchestration/workflow-size-guideline.js +19 -9
- package/dist/orchestration/workflow.d.ts +1 -0
- package/dist/orchestration/workflow.js +18 -2
- package/dist/prompt-assembly/assemble.js +3 -7
- package/dist/prompt-assembly/packs/sema-default.js +8 -5
- package/dist/prompts/coordinator.d.ts +1 -1
- package/dist/prompts/coordinator.js +45 -0
- package/dist/prompts/default.d.ts +4 -5
- package/dist/prompts/default.js +16 -18
- package/dist/prompts/simple-sections.d.ts +3 -1
- package/dist/prompts/simple-sections.js +11 -1
- package/dist/stores/file/workflow-journal-store.d.ts +7 -1
- package/dist/stores/file/workflow-journal-store.js +70 -33
- package/dist/tools/fs/bash-readonly-classifier.js +20 -1
- package/dist/tools/fs/fs-bash.d.ts +1 -0
- package/dist/tools/fs/fs-bash.js +10 -3
- package/dist/tools/fs/fs-read.js +10 -10
- package/dist/tools/fs/fs-search-tools.js +42 -7
- package/dist/tools/fs/fs-write.js +18 -6
- package/dist/tools/fs/index.d.ts +1 -0
- package/dist/tools/fs/index.js +2 -1
- package/dist/tools/fs/safety.d.ts +4 -0
- package/dist/tools/fs/safety.js +102 -6
- package/dist/tools/fs/search.d.ts +1 -0
- package/dist/tools/fs/search.js +23 -3
- package/dist/tools/monitor.js +1 -1
- package/package.json +3 -2
|
@@ -68,6 +68,15 @@ export declare class ObserverDigestTap {
|
|
|
68
68
|
finish(reason: string): void;
|
|
69
69
|
}
|
|
70
70
|
export type ObserverPairingState = "armed" | "denied" | "stopped" | "retired" | "blocked";
|
|
71
|
+
export declare const OBSERVER_STOPPED_BY_USER_ERROR_NAME = "AgentStoppedByUserError";
|
|
72
|
+
export declare const OBSERVER_RESUME_STATE_ERROR_NAME = "ResumeAgentStateError";
|
|
73
|
+
export declare class ObserverStoppedByUserError extends Error {
|
|
74
|
+
constructor(message: string);
|
|
75
|
+
}
|
|
76
|
+
export declare class ObserverResumeStateError extends Error {
|
|
77
|
+
constructor(message: string);
|
|
78
|
+
}
|
|
79
|
+
export declare const OBSERVER_FRESH_START_NOTE = "[Note: your previous observation context was lost; this is a fresh start mid-task.]";
|
|
71
80
|
export interface ObserverSpawner {
|
|
72
81
|
spawnFirstRun(args: {
|
|
73
82
|
framingPrompt: string;
|
|
@@ -76,6 +85,10 @@ export interface ObserverSpawner {
|
|
|
76
85
|
deliver(args: {
|
|
77
86
|
digest: string;
|
|
78
87
|
}): Promise<void>;
|
|
88
|
+
restartFresh?(args: {
|
|
89
|
+
framingPrompt: string;
|
|
90
|
+
digest: string;
|
|
91
|
+
}): Promise<void>;
|
|
79
92
|
}
|
|
80
93
|
export declare class ObserverPairing {
|
|
81
94
|
state: ObserverPairingState;
|
|
@@ -102,6 +115,7 @@ export declare class ObserverPairing {
|
|
|
102
115
|
private safeOnError;
|
|
103
116
|
drain(): Promise<void>;
|
|
104
117
|
private pump;
|
|
118
|
+
private deliverBatch;
|
|
105
119
|
retire(state: Exclude<ObserverPairingState, "armed">): void;
|
|
106
120
|
}
|
|
107
121
|
export declare function markObserverTaskId(taskId: string): void;
|
package/dist/agents/observer.js
CHANGED
|
@@ -209,6 +209,24 @@ export class ObserverDigestTap {
|
|
|
209
209
|
this.safeFlush(slice);
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
|
+
export const OBSERVER_STOPPED_BY_USER_ERROR_NAME = "AgentStoppedByUserError";
|
|
213
|
+
export const OBSERVER_RESUME_STATE_ERROR_NAME = "ResumeAgentStateError";
|
|
214
|
+
export class ObserverStoppedByUserError extends Error {
|
|
215
|
+
constructor(message) {
|
|
216
|
+
super(message);
|
|
217
|
+
this.name = OBSERVER_STOPPED_BY_USER_ERROR_NAME;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
export class ObserverResumeStateError extends Error {
|
|
221
|
+
constructor(message) {
|
|
222
|
+
super(message);
|
|
223
|
+
this.name = OBSERVER_RESUME_STATE_ERROR_NAME;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function errorName(err) {
|
|
227
|
+
return err instanceof Error ? err.name : undefined;
|
|
228
|
+
}
|
|
229
|
+
export const OBSERVER_FRESH_START_NOTE = "[Note: your previous observation context was lost; this is a fresh start mid-task.]";
|
|
212
230
|
export class ObserverPairing {
|
|
213
231
|
state = "armed";
|
|
214
232
|
observedEnvelopeName;
|
|
@@ -278,18 +296,22 @@ export class ObserverPairing {
|
|
|
278
296
|
const batch = this.buffer.splice(0, this.buffer.length);
|
|
279
297
|
try {
|
|
280
298
|
const digest = renderObserverDigestBatch(this, batch);
|
|
281
|
-
|
|
282
|
-
await this.spawner.spawnFirstRun({ framingPrompt: this.framingPrompt, digest });
|
|
283
|
-
this.firstRunDone = true;
|
|
284
|
-
}
|
|
285
|
-
else {
|
|
286
|
-
await this.spawner.deliver({ digest });
|
|
287
|
-
}
|
|
299
|
+
await this.deliverBatch(digest);
|
|
288
300
|
}
|
|
289
301
|
catch (err) {
|
|
290
|
-
|
|
291
|
-
|
|
302
|
+
if (errorName(err) === OBSERVER_STOPPED_BY_USER_ERROR_NAME) {
|
|
303
|
+
this.state = "stopped";
|
|
304
|
+
this.buffer = [];
|
|
305
|
+
this.safeOnError(err);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
292
308
|
this.safeOnError(err);
|
|
309
|
+
if (this.buffer.length > 0) {
|
|
310
|
+
const strandedCount = this.buffer.length;
|
|
311
|
+
this.buffer = [];
|
|
312
|
+
this.safeOnError(new Error(`observer delivery: ${strandedCount} segment(s) enqueued mid-fault were stranded and dropped (never attempted) after: ${err instanceof Error ? err.message : String(err)}`));
|
|
313
|
+
}
|
|
314
|
+
return;
|
|
293
315
|
}
|
|
294
316
|
}
|
|
295
317
|
}
|
|
@@ -297,6 +319,33 @@ export class ObserverPairing {
|
|
|
297
319
|
this.delivering = false;
|
|
298
320
|
}
|
|
299
321
|
}
|
|
322
|
+
async deliverBatch(digest) {
|
|
323
|
+
if (!this.firstRunDone) {
|
|
324
|
+
await this.spawner.spawnFirstRun({ framingPrompt: this.framingPrompt, digest });
|
|
325
|
+
this.firstRunDone = true;
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
try {
|
|
329
|
+
await this.spawner.deliver({ digest });
|
|
330
|
+
}
|
|
331
|
+
catch (err) {
|
|
332
|
+
if (errorName(err) !== OBSERVER_RESUME_STATE_ERROR_NAME)
|
|
333
|
+
throw err;
|
|
334
|
+
const restart = this.spawner.restartFresh?.bind(this.spawner);
|
|
335
|
+
if (restart === undefined)
|
|
336
|
+
throw err;
|
|
337
|
+
try {
|
|
338
|
+
await restart({ framingPrompt: `${this.framingPrompt}\n\n${OBSERVER_FRESH_START_NOTE}`, digest });
|
|
339
|
+
this.firstRunDone = true;
|
|
340
|
+
}
|
|
341
|
+
catch (restartErr) {
|
|
342
|
+
this.firstRunDone = false;
|
|
343
|
+
throw restartErr instanceof Error
|
|
344
|
+
? new Error(`${restartErr.message} (after resume-state loss: ${err instanceof Error ? err.message : String(err)})`, { cause: restartErr })
|
|
345
|
+
: restartErr;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
300
349
|
retire(state) {
|
|
301
350
|
if (this.state !== "armed")
|
|
302
351
|
return;
|
|
@@ -37,6 +37,7 @@ export function createSendMessageTool(opts) {
|
|
|
37
37
|
contract: { contractId: "core.send_message@1", implementationRevision: "1" },
|
|
38
38
|
executionMode: "parallel",
|
|
39
39
|
description: `Send a follow-up message to a previously spawned background agent. ` +
|
|
40
|
+
`Your plain text output is NOT visible to other agents — to communicate, you MUST call this tool. Messages addressed to you are delivered automatically; you don't check an inbox. ` +
|
|
40
41
|
`'to' is the agent's name (set at spawn via Agent({name})) or its task_id (a…). A RUNNING agent receives the ` +
|
|
41
42
|
`message at its next turn (queued — never interrupts its current work); a FINISHED agent resumes as a new ` +
|
|
42
43
|
`background run with its full prior conversation preserved, so don't re-explain what it already knows. Names ` +
|
|
@@ -50,7 +51,7 @@ export function createSendMessageTool(opts) {
|
|
|
50
51
|
parameters: Type.Object({
|
|
51
52
|
to: Type.String({ description: 'Recipient: the agent\'s name, or its task_id (a…) returned by the Agent tool with run_in_background. "main" is reserved for the spawning conversation.' }),
|
|
52
53
|
message: Type.String({ description: "The follow-up request. The agent continues from its full prior context." }),
|
|
53
|
-
summary: Type.Optional(Type.String({ maxLength: 200, description: "A
|
|
54
|
+
summary: Type.Optional(Type.String({ maxLength: 200, description: "A 5-10 word summary shown as a preview in the UI (required when message is a string)" })),
|
|
54
55
|
}),
|
|
55
56
|
execute: async (args, ctx) => {
|
|
56
57
|
const a = args;
|
|
@@ -60,6 +61,28 @@ export function createSendMessageTool(opts) {
|
|
|
60
61
|
return { content: "Message not sent: 'to' was empty. Pass the agent's task_id (a…).", details: { error: "empty to" }, isError: true };
|
|
61
62
|
if (!message)
|
|
62
63
|
return { content: "Message not sent: 'message' was empty.", details: { error: "empty message" }, isError: true };
|
|
64
|
+
if (to === "*") {
|
|
65
|
+
return {
|
|
66
|
+
content: 'Message not sent: broadcast (to: "*") is no longer supported — send a message per recipient.',
|
|
67
|
+
details: { error: "broadcast_unsupported", to },
|
|
68
|
+
isError: true,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (to.includes("@")) {
|
|
72
|
+
return {
|
|
73
|
+
content: "Message not sent: to must be a bare teammate name — there is only one team per session.",
|
|
74
|
+
details: { error: "qualified_name_unsupported", to },
|
|
75
|
+
isError: true,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const summaryArg = typeof a.summary === "string" ? a.summary.trim() : "";
|
|
79
|
+
if (summaryArg === "") {
|
|
80
|
+
return {
|
|
81
|
+
content: "Message not sent: summary is required when message is a string — pass a 5-10 word summary of the follow-up.",
|
|
82
|
+
details: { error: "summary_required", to },
|
|
83
|
+
isError: true,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
63
86
|
const senderId = ctx.taskId ?? opts.owner;
|
|
64
87
|
if (senderId !== undefined && isObserverTaskId(senderId)) {
|
|
65
88
|
return { content: OBSERVER_SENDMESSAGE_SENDER_REFUSAL, details: { error: "observer_sender" }, isError: true };
|
|
@@ -89,6 +112,17 @@ export function createSendMessageTool(opts) {
|
|
|
89
112
|
details: { type: "send-message", status: "uplinked", to: "main", seq: uplinkSeqGlobal },
|
|
90
113
|
};
|
|
91
114
|
}
|
|
115
|
+
const hasParent = ctx.parentTaskId !== undefined ||
|
|
116
|
+
opts.uplink !== undefined ||
|
|
117
|
+
opts.senderName !== undefined ||
|
|
118
|
+
opts.siblingRetain !== undefined;
|
|
119
|
+
if (!hasParent) {
|
|
120
|
+
return {
|
|
121
|
+
content: `You are the main conversation — "main" addresses you. Send to a named agent instead.`,
|
|
122
|
+
details: { error: "main_is_self", to },
|
|
123
|
+
isError: true,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
92
126
|
return {
|
|
93
127
|
content: `Message not sent: "main" (the spawning conversation) is not a deliverable target here. ` +
|
|
94
128
|
`Your completion is reported to it automatically — finish your task and your final report will be relayed.`,
|
|
@@ -126,10 +160,10 @@ export function createSendMessageTool(opts) {
|
|
|
126
160
|
if (row.name === undefined || row.agentType === "fork")
|
|
127
161
|
return undefined;
|
|
128
162
|
const whoT3 = handle === to ? `agent ${handle}` : `agent "${to}" (${handle})`;
|
|
129
|
-
if (row.status === "killed") {
|
|
163
|
+
if (row.status === "killed" && row.stoppedBy === "user") {
|
|
130
164
|
return {
|
|
131
|
-
content: `Message not sent: ${whoT3} was stopped
|
|
132
|
-
details: { error: "killed", to,
|
|
165
|
+
content: `Message not sent: ${whoT3} was stopped by the user and was not resumed. Treat its work as cancelled; only start a new agent for it if the user explicitly asks.`,
|
|
166
|
+
details: { error: "killed", to, stoppedBy: row.stoppedBy },
|
|
133
167
|
isError: true,
|
|
134
168
|
};
|
|
135
169
|
}
|
|
@@ -419,11 +453,13 @@ export function createSendMessageTool(opts) {
|
|
|
419
453
|
}
|
|
420
454
|
if (row.status === "killed") {
|
|
421
455
|
const by = opts.registry.getStopAttribution(targetId);
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
456
|
+
if (by === "user") {
|
|
457
|
+
return {
|
|
458
|
+
content: `Message not sent: ${who} was stopped by the user and was not resumed. Treat its work as cancelled; only start a new agent for it if the user explicitly asks.`,
|
|
459
|
+
details: { error: "killed", to, stoppedBy: by },
|
|
460
|
+
isError: true,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
427
463
|
}
|
|
428
464
|
const runLedger = ctx.subagentRetain ?? opts.retain;
|
|
429
465
|
const smLedgerSessionId = ctx.sessionId ?? opts.sessionId;
|
|
@@ -445,6 +481,13 @@ export function createSendMessageTool(opts) {
|
|
|
445
481
|
isError: true,
|
|
446
482
|
};
|
|
447
483
|
}
|
|
484
|
+
if (ledger.get(resumeToolUseId)?.running === true) {
|
|
485
|
+
return {
|
|
486
|
+
content: `Message not sent: ${who} (or a prior follow-up to it) is still running — wait for its completion notification.`,
|
|
487
|
+
details: { error: "steering.still_running", to },
|
|
488
|
+
isError: true,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
448
491
|
return await withTargetLane(targetLaneKey(access.scope, targetId), async () => {
|
|
449
492
|
const resume = createSubagentResume({
|
|
450
493
|
ledger,
|
|
@@ -470,7 +513,9 @@ export function createSendMessageTool(opts) {
|
|
|
470
513
|
const summary = typeof a.summary === "string" && a.summary.trim() !== "" ? a.summary.trim().slice(0, 200) : undefined;
|
|
471
514
|
const fromPrefix = ctx.parentTaskId !== undefined ? `(message from teammate "${opts.senderName ?? senderId ?? "unknown"}")\n` : "";
|
|
472
515
|
try {
|
|
473
|
-
const
|
|
516
|
+
const safeSummary = summary !== undefined ? escapeEnvelopeTag(TEAMMATE_MESSAGE_TAG, summary) : undefined;
|
|
517
|
+
const safeMessage = escapeEnvelopeTag(TEAMMATE_MESSAGE_TAG, message);
|
|
518
|
+
const marker = await resume(`${fromPrefix}${safeSummary ? `[${safeSummary}] ${safeMessage}` : safeMessage}`);
|
|
474
519
|
return {
|
|
475
520
|
content: `Message sent — ${who} resumed in the background with its prior context intact (correlation marker [${marker}]).\n` +
|
|
476
521
|
`You will be notified automatically when it completes; its reply will carry [${marker}]. Continue with other work — do not poll.`,
|
|
@@ -133,6 +133,7 @@ export declare function asyncLaunchedReceipt(p: {
|
|
|
133
133
|
taskId: string;
|
|
134
134
|
workingLine: string;
|
|
135
135
|
notify: boolean;
|
|
136
|
+
oneShot?: boolean;
|
|
136
137
|
notes?: (string | undefined)[];
|
|
137
138
|
}): string;
|
|
138
139
|
export declare function createSubagentTool(opts: SubagentToolOptions): ToolSpec;
|
package/dist/agents/subagent.js
CHANGED
|
@@ -21,7 +21,7 @@ import { FORK_DEFAULT_MAX_TURNS, SESSION_BG_DEFAULT_TIMEOUT_SEC, RETAIN_DEFAULT_
|
|
|
21
21
|
export { FORK_DEFAULT_MAX_TURNS, SESSION_BG_DEFAULT_TIMEOUT_SEC, RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX };
|
|
22
22
|
import { SUBAGENT_RESUME_CAP, SubagentRetainLedger, getOrCreateSessionRetainLedger, ensureSessionReapHook, createResumePrompt, } from "./retain-ledger.js";
|
|
23
23
|
import { recordRosterSpawn } from "./roster-store.js";
|
|
24
|
-
import { ObserverDigestTap, ObserverPairing, createObserverReportToolSpec, markObserverTaskId, unmarkObserverTaskId, isObserverTaskId, observerFramingPrompt, observerSlug, resolveObserverDeclaration, } from "./observer.js";
|
|
24
|
+
import { ObserverDigestTap, ObserverPairing, createObserverReportToolSpec, markObserverTaskId, unmarkObserverTaskId, isObserverTaskId, ObserverResumeStateError, ObserverStoppedByUserError, observerFramingPrompt, observerSlug, resolveObserverDeclaration, } from "./observer.js";
|
|
25
25
|
import { SubagentStepRecorder } from "./subagent-steps.js";
|
|
26
26
|
const BG_AGENT_RESULT_MAX = 4_000;
|
|
27
27
|
const BG_AGENT_RESULT_FULL_MAX = 200_000;
|
|
@@ -129,6 +129,9 @@ function createToolStatsCounter(delegationToolName) {
|
|
|
129
129
|
snapshot() {
|
|
130
130
|
return t.readCount + t.searchCount + t.bashCount + t.editFileCount + t.otherToolCount > 0 ? { ...t } : undefined;
|
|
131
131
|
},
|
|
132
|
+
total() {
|
|
133
|
+
return t.readCount + t.searchCount + t.bashCount + t.editFileCount + t.otherToolCount;
|
|
134
|
+
},
|
|
132
135
|
};
|
|
133
136
|
}
|
|
134
137
|
export const FORK_SUBAGENT_TYPE = "fork";
|
|
@@ -622,15 +625,20 @@ export function forkWorktreeTranslationNote(parentCwd, worktreeDir) {
|
|
|
622
625
|
}
|
|
623
626
|
export function asyncLaunchedReceipt(p) {
|
|
624
627
|
const noteLines = (p.notes ?? []).filter((n) => n !== undefined && n !== "");
|
|
628
|
+
const oneShotBlocked = p.notify && p.oneShot === true;
|
|
625
629
|
return (`Async agent launched successfully. (This tool result is internal metadata — never quote or paste any part of it, including the task_id below, into a user-facing reply.)\n` +
|
|
626
630
|
`task_id: ${p.taskId} (internal ID - do not mention to user. Use SendMessage with to: '${p.taskId}', summary: '<5-10 word recap>' to continue this agent.)\n` +
|
|
627
|
-
(
|
|
628
|
-
? `${p.workingLine}
|
|
629
|
-
:
|
|
631
|
+
(oneShotBlocked
|
|
632
|
+
? `${p.workingLine} This is a ONE-SHOT submission — there is no later turn for a background notification to land in, so do NOT end your turn expecting one. Actively wait instead: TaskOutput(task_id: "${p.taskId}", block: true). If it is still running after the wait, wait again (bounded) rather than ending the turn, or write out your best available answer now if you are near your own time budget.\n`
|
|
633
|
+
: p.notify
|
|
634
|
+
? `${p.workingLine} You will be notified automatically when it completes. You know nothing about its results until that notification arrives — do not report, assume, or predict them; continue other work or respond to the user in the meantime.\n`
|
|
635
|
+
: `${p.workingLine} Its result is NOT pushed automatically — retrieve progress and results with TaskOutput(task_id) where mounted.\n`) +
|
|
630
636
|
noteLines.map((n) => `${n}\n`).join("") +
|
|
631
|
-
(
|
|
632
|
-
? `In your own words, briefly tell the user what you launched — do not echo this tool result.
|
|
633
|
-
:
|
|
637
|
+
(oneShotBlocked
|
|
638
|
+
? `In your own words, briefly tell the user what you launched — do not echo this tool result. Do not assume a later message will deliver the result: this submission ends after this turn, so retrieve it now with the blocking TaskOutput wait above before writing your final answer.`
|
|
639
|
+
: p.notify
|
|
640
|
+
? `In your own words, briefly tell the user what you launched — do not echo this tool result. Agent results will arrive in a subsequent message. If the user asks for progress, say the agent is still running.`
|
|
641
|
+
: `In your own words, briefly tell the user what you launched — do not echo this tool result. The result will NOT arrive on its own — retrieve it with TaskOutput(task_id) where mounted before relying on it.`));
|
|
634
642
|
}
|
|
635
643
|
export function createSubagentTool(opts) {
|
|
636
644
|
const catalog = opts.runner.agentCatalog;
|
|
@@ -1105,9 +1113,17 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1105
1113
|
let observerTap;
|
|
1106
1114
|
let observerSessionId;
|
|
1107
1115
|
let observerFailure;
|
|
1116
|
+
let observerDegradedCount = 0;
|
|
1117
|
+
let observerDegradedFirst;
|
|
1108
1118
|
const noteObserverFailure = (err) => {
|
|
1109
|
-
|
|
1110
|
-
|
|
1119
|
+
const msg = (err instanceof Error ? err.message : String(err)).slice(0, REPORT_FIELD_MAX);
|
|
1120
|
+
if (observerPairing !== undefined && observerPairing.state === "armed") {
|
|
1121
|
+
observerDegradedCount++;
|
|
1122
|
+
if (observerDegradedFirst === undefined)
|
|
1123
|
+
observerDegradedFirst = msg;
|
|
1124
|
+
}
|
|
1125
|
+
else if (observerFailure === undefined) {
|
|
1126
|
+
observerFailure = msg;
|
|
1111
1127
|
}
|
|
1112
1128
|
try {
|
|
1113
1129
|
opts.onObserverError?.(err, {
|
|
@@ -1139,7 +1155,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1139
1155
|
pendingTrigger.push(prompt);
|
|
1140
1156
|
const observerDef = observerArm.observerDefinition;
|
|
1141
1157
|
const envelopeName = observerSlug(childAgentName ?? def.name);
|
|
1142
|
-
|
|
1158
|
+
let sid = uuidv7();
|
|
1143
1159
|
observerSessionId = sid;
|
|
1144
1160
|
markObserverTaskId(sid);
|
|
1145
1161
|
const reportOpts = {
|
|
@@ -1163,15 +1179,36 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1163
1179
|
});
|
|
1164
1180
|
const runObserverLeg = async (spec) => {
|
|
1165
1181
|
if (!isObserverTaskId(sid))
|
|
1166
|
-
throw new
|
|
1182
|
+
throw new ObserverStoppedByUserError("observer identity revoked — delivery refused");
|
|
1167
1183
|
const r = await opts.runner.runTask(spec);
|
|
1168
1184
|
if (r.status !== "completed") {
|
|
1185
|
+
if (r.errorCode === "resume.session_not_found") {
|
|
1186
|
+
throw new ObserverResumeStateError(`observer session no longer exists${r.errorMessage ? `: ${r.errorMessage}` : ""}`);
|
|
1187
|
+
}
|
|
1169
1188
|
throw new Error(`observer run ended ${r.status}${r.errorMessage ? `: ${r.errorMessage}` : ""}`);
|
|
1170
1189
|
}
|
|
1171
1190
|
};
|
|
1172
1191
|
const spawner = {
|
|
1173
1192
|
spawnFirstRun: ({ framingPrompt, digest }) => runObserverLeg(observerSpec(`${framingPrompt}\n\n${digest}`, false)),
|
|
1174
1193
|
deliver: ({ digest }) => runObserverLeg(observerSpec(digest, true)),
|
|
1194
|
+
restartFresh: async ({ framingPrompt, digest }) => {
|
|
1195
|
+
const dead = sid;
|
|
1196
|
+
const fresh = uuidv7();
|
|
1197
|
+
markObserverTaskId(fresh);
|
|
1198
|
+
sid = fresh;
|
|
1199
|
+
observerSessionId = fresh;
|
|
1200
|
+
try {
|
|
1201
|
+
await runObserverLeg(observerSpec(`${framingPrompt}\n\n${digest}`, false));
|
|
1202
|
+
}
|
|
1203
|
+
finally {
|
|
1204
|
+
unmarkObserverTaskId(dead);
|
|
1205
|
+
try {
|
|
1206
|
+
await opts.runner.sessions.release(dead);
|
|
1207
|
+
}
|
|
1208
|
+
catch {
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
},
|
|
1175
1212
|
};
|
|
1176
1213
|
observerPairing = new ObserverPairing({
|
|
1177
1214
|
observedEnvelopeName: envelopeName,
|
|
@@ -1209,9 +1246,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1209
1246
|
t.unref?.();
|
|
1210
1247
|
});
|
|
1211
1248
|
const outcome = await Promise.race([observerPairing.drain().then(() => "drained"), timeout]);
|
|
1249
|
+
observerPairing.retire(outcome === "timeout" ? "stopped" : "retired");
|
|
1212
1250
|
if (outcome === "timeout")
|
|
1213
1251
|
noteObserverFailure(new Error("observer unresponsive at settle (delivery drain timed out)"));
|
|
1214
|
-
observerPairing.retire(outcome === "timeout" ? "stopped" : "retired");
|
|
1215
1252
|
if (observerSessionId !== undefined) {
|
|
1216
1253
|
try {
|
|
1217
1254
|
await opts.runner.sessions.release(observerSessionId);
|
|
@@ -1240,7 +1277,13 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1240
1277
|
clearTimeout(timer);
|
|
1241
1278
|
return outcome;
|
|
1242
1279
|
};
|
|
1243
|
-
const observerNoteFor = (outcome) => observerFailure !== undefined
|
|
1280
|
+
const observerNoteFor = (outcome) => observerFailure !== undefined
|
|
1281
|
+
? " [observer: failed]"
|
|
1282
|
+
: outcome === "still_draining"
|
|
1283
|
+
? " [observer: still draining]"
|
|
1284
|
+
: observerDegradedCount > 0
|
|
1285
|
+
? ` [observer: degraded, ${observerDegradedCount} digest batch(es) lost]`
|
|
1286
|
+
: "";
|
|
1244
1287
|
const stepRecorder = new SubagentStepRecorder(ctx.toolCallId);
|
|
1245
1288
|
const toolStatsCounter = createToolStatsCounter(opts.name ?? DEFAULT_SUBAGENT_TOOL_NAME);
|
|
1246
1289
|
let assistantTextTail = "";
|
|
@@ -1736,6 +1779,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1736
1779
|
taskId,
|
|
1737
1780
|
workingLine: `The forked agent inherits your full current context and works on its prompt in the background.`,
|
|
1738
1781
|
notify: Boolean(notify),
|
|
1782
|
+
oneShot: ctx.oneShot === true,
|
|
1739
1783
|
notes: [
|
|
1740
1784
|
modelNote,
|
|
1741
1785
|
extraToolsNote,
|
|
@@ -2366,6 +2410,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2366
2410
|
taskId,
|
|
2367
2411
|
workingLine: `The agent is working in the background.`,
|
|
2368
2412
|
notify: Boolean(notify),
|
|
2413
|
+
oneShot: ctx.oneShot === true,
|
|
2369
2414
|
notes: [
|
|
2370
2415
|
modelNote,
|
|
2371
2416
|
extraToolsNote,
|
|
@@ -2381,6 +2426,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2381
2426
|
};
|
|
2382
2427
|
}
|
|
2383
2428
|
let child;
|
|
2429
|
+
const syncStartedAt = Date.now();
|
|
2384
2430
|
let retainEntry;
|
|
2385
2431
|
if (ctx.onSubagentSpawn || observerPairing) {
|
|
2386
2432
|
retainEntry = ctx.onSubagentSpawn ? await tryRetainChild(ctx.subagentRetain) : undefined;
|
|
@@ -2488,9 +2534,13 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2488
2534
|
child.blockedReason ? `blocked_reason: ${inlineUntrusted(child.blockedReason, REPORT_FIELD_MAX)}` : "",
|
|
2489
2535
|
child.errorMessage ? `error: ${inlineUntrusted(child.errorMessage, REPORT_FIELD_MAX)}` : "",
|
|
2490
2536
|
errClass ? `error_kind: ${errClass.errorKind} (retryable: ${errClass.retryable})` : "",
|
|
2491
|
-
observerFailure !== undefined
|
|
2537
|
+
observerFailure !== undefined
|
|
2538
|
+
? `observer: failed (${inlineUntrusted(observerFailure, REPORT_FIELD_MAX)})`
|
|
2539
|
+
: observerDegradedCount > 0
|
|
2540
|
+
? `observer: degraded — ${observerDegradedCount} digest batch(es) never reached it (first: ${inlineUntrusted(observerDegradedFirst ?? "", REPORT_FIELD_MAX)})`
|
|
2541
|
+
: "",
|
|
2492
2542
|
`stats: turns=${child.stats.turns} tokens=${child.stats.tokens}`,
|
|
2493
|
-
retainEntry
|
|
2543
|
+
!retainEntry && failureRetained ? `transcript_id: ${child.sessionId} (internal ID - do not mention to user; inspect via AgentTranscript where mounted)` : "",
|
|
2494
2544
|
retainEntry ? `resumable: true` : "",
|
|
2495
2545
|
failureRetained
|
|
2496
2546
|
? `session_retained: this child's session is kept for a short time (~15 min) so its transcript stays inspectable via transcript_id; it is released automatically afterwards.`
|
|
@@ -2521,6 +2571,10 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2521
2571
|
: "(no text result)",
|
|
2522
2572
|
"",
|
|
2523
2573
|
"Verify this result before concluding. If it leaves more to do, continue or delegate again.",
|
|
2574
|
+
retainEntry
|
|
2575
|
+
? `transcript_id: ${child.sessionId} (internal ID - do not mention to user. Transcript inspectable via AgentTranscript where mounted; this child is continued through the deployment's resume handle, not by SendMessage.)`
|
|
2576
|
+
: "",
|
|
2577
|
+
`<usage>subagent_tokens: ${child.stats.tokens}\ntool_uses: ${toolStatsCounter.total()}\nduration_ms: ${Date.now() - syncStartedAt}</usage>`,
|
|
2524
2578
|
].filter(Boolean);
|
|
2525
2579
|
return {
|
|
2526
2580
|
content: lines.join("\n"),
|
|
@@ -2,7 +2,20 @@ import { DEFAULT_CHARS_PER_TOKEN, estimateContextTokens, estimateTokens } from "
|
|
|
2
2
|
import { isToolResult } from "./message-utils.js";
|
|
3
3
|
const CLEARED_MARKER = "[tool result cleared to save context]";
|
|
4
4
|
const refNote = (ref) => `full text persisted; call ReadToolResult with ref "${ref}" to read it back`;
|
|
5
|
-
const mediaNote = (
|
|
5
|
+
const mediaNote = (blocks) => {
|
|
6
|
+
const byType = new Map();
|
|
7
|
+
for (const b of blocks) {
|
|
8
|
+
const t = b.type ?? "untyped";
|
|
9
|
+
byType.set(t, (byType.get(t) ?? 0) + 1);
|
|
10
|
+
}
|
|
11
|
+
const n = blocks.length;
|
|
12
|
+
if (byType.size === 1) {
|
|
13
|
+
const [t, c] = [...byType][0];
|
|
14
|
+
return `${c} ${t} attachment${c === 1 ? "" : "s"} no longer visible after this clear`;
|
|
15
|
+
}
|
|
16
|
+
const breakdown = [...byType].map(([t, c]) => `${c} ${t}`).join(", ");
|
|
17
|
+
return `${n} attachments (${breakdown}) no longer visible after this clear`;
|
|
18
|
+
};
|
|
6
19
|
const clearedMarker = (notes) => {
|
|
7
20
|
const present = notes.filter((n) => n !== undefined);
|
|
8
21
|
return present.length > 0 ? `[tool result cleared to save context — ${present.join("; ")}]` : CLEARED_MARKER;
|
|
@@ -91,8 +104,8 @@ export function clearStaleToolResults(messages, opts) {
|
|
|
91
104
|
}
|
|
92
105
|
}
|
|
93
106
|
}
|
|
94
|
-
const
|
|
95
|
-
const marker = clearedMarker([ref,
|
|
107
|
+
const mediaBlocks = rawContent.filter((c) => c?.type !== "text");
|
|
108
|
+
const marker = clearedMarker([ref, mediaBlocks.length > 0 ? mediaNote(mediaBlocks) : undefined]);
|
|
96
109
|
const cleared = {
|
|
97
110
|
...out[idx],
|
|
98
111
|
content: [{ type: "text", text: marker }],
|
|
@@ -204,9 +204,18 @@ export class InMemoryFileSnapshotStore {
|
|
|
204
204
|
try {
|
|
205
205
|
for (const hash of new Set(manifest.values())) {
|
|
206
206
|
written.add(hash);
|
|
207
|
+
if (hash === "" || hash === "." || hash === ".." || !/^[A-Za-z0-9_.-]+$/.test(hash)) {
|
|
208
|
+
return { ok: false, error: { code: "read_failed", message: `unsafe blob hash ${JSON.stringify(hash)}` } };
|
|
209
|
+
}
|
|
207
210
|
if (this.blobs.has(hash))
|
|
208
211
|
continue;
|
|
209
|
-
|
|
212
|
+
let bytes;
|
|
213
|
+
try {
|
|
214
|
+
bytes = await srcGetBlob(hash);
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
return { ok: false, error: { code: "read_failed", message: `source blob ${hash} fetch failed: ${err.message}` } };
|
|
218
|
+
}
|
|
210
219
|
if (!bytes)
|
|
211
220
|
return { ok: false, error: { code: "read_failed", message: `missing source blob ${hash}` } };
|
|
212
221
|
if (createHash("sha256").update(bytes).digest("hex") !== hash) {
|
|
@@ -114,6 +114,7 @@ export interface Prepared {
|
|
|
114
114
|
apply: (committedArtifactDigest?: string) => void;
|
|
115
115
|
} | undefined;
|
|
116
116
|
activeTools: Set<string>;
|
|
117
|
+
deferredToolNames?: ReadonlySet<string>;
|
|
117
118
|
memoryEngineSession?: {
|
|
118
119
|
engine: MemoryEngine;
|
|
119
120
|
handle: MemorySessionHandle;
|
|
@@ -93,6 +93,9 @@ export function resolveModelPromptTraits(model, spec, internals) {
|
|
|
93
93
|
fableMitigations: isFableFamilyModelId(model.id),
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
|
+
function isDelegatedNonForkChild(internals) {
|
|
97
|
+
return internals?.isDelegatedChild === true && internals?.insideFork !== true;
|
|
98
|
+
}
|
|
96
99
|
export function batchContextAt(messages, currentId) {
|
|
97
100
|
let batch = [];
|
|
98
101
|
for (const m of messages) {
|
|
@@ -742,6 +745,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
742
745
|
model: harnessRef.current?.getModel(),
|
|
743
746
|
thinkingLevel: harnessRef.current?.getThinkingLevel(),
|
|
744
747
|
principal: spec.principal,
|
|
748
|
+
oneShot: spec.oneShot,
|
|
745
749
|
clientContext: spec.clientContext,
|
|
746
750
|
excludeTools: toolFaceSnapshot.exclude,
|
|
747
751
|
deferTools: toolFaceSnapshot.defer,
|
|
@@ -1164,6 +1168,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1164
1168
|
taskRegistry: defaultTaskRegistry,
|
|
1165
1169
|
taskOwner: hostTaskId,
|
|
1166
1170
|
taskScope,
|
|
1171
|
+
oneShot: spec.oneShot,
|
|
1167
1172
|
...(sessionId !== undefined ? { sessionId } : {}),
|
|
1168
1173
|
...(internals?.onTaskNotification !== undefined ? { taskNotification: internals.onTaskNotification } : {}),
|
|
1169
1174
|
...(internals?.detachHub !== undefined ? { detachHub: internals.detachHub } : {}),
|
|
@@ -1212,7 +1217,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1212
1217
|
if (backgroundTaskToolsActive || workflowToolsActive) {
|
|
1213
1218
|
toolEffects.set("TaskOutput", "read");
|
|
1214
1219
|
toolEffects.set("TaskStop", "write");
|
|
1215
|
-
tools.push(firstPartyOffload(createTaskOutputTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore, notificationWired: internals?.onTaskNotification !== undefined, ...(callCapRef ? { deadlineMs: () => toolCutDeadlineMs(callCapRef, Date.now()) } : {}) })), firstPartyOffload(createTaskStopTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore })));
|
|
1220
|
+
tools.push(firstPartyOffload(createTaskOutputTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore, notificationWired: internals?.onTaskNotification !== undefined, oneShot: spec.oneShot, ...(callCapRef ? { deadlineMs: () => toolCutDeadlineMs(callCapRef, Date.now()) } : {}) })), firstPartyOffload(createTaskStopTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore })));
|
|
1216
1221
|
if (runnerSelf && !(spec.tools ?? []).some((t) => t.name === SEND_MESSAGE_TOOL_NAME)) {
|
|
1217
1222
|
const delegationForRevive = (spec.tools ?? []).find((t) => t.agentListing !== undefined);
|
|
1218
1223
|
const reviveSpawn = delegationForRevive !== undefined && deps.backgroundAgentStore !== undefined && deps.mailboxStore !== undefined
|
|
@@ -1405,7 +1410,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1405
1410
|
loaded = await Promise.resolve(deps.loadProjectMemory({
|
|
1406
1411
|
cwd: taskRootPath,
|
|
1407
1412
|
handsEnabled,
|
|
1408
|
-
isSubagent: internals
|
|
1413
|
+
isSubagent: isDelegatedNonForkChild(internals),
|
|
1409
1414
|
...(internals?.agentName ? { agentName: internals.agentName } : {}),
|
|
1410
1415
|
sessionId,
|
|
1411
1416
|
phase: projectMemoryPhase,
|
|
@@ -1432,6 +1437,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1432
1437
|
memoryBlock = memoryBlock ? `${memoryBlock}\n\n${projectBlock}` : projectBlock;
|
|
1433
1438
|
}
|
|
1434
1439
|
}
|
|
1440
|
+
const declaredSkillRank = new Map();
|
|
1441
|
+
for (const s of spec.skills ?? [])
|
|
1442
|
+
if (!declaredSkillRank.has(s.name))
|
|
1443
|
+
declaredSkillRank.set(s.name, declaredSkillRank.size);
|
|
1435
1444
|
const skillSpecs = normalizeSkills(spec.skills ?? []).filter((s) => {
|
|
1436
1445
|
if (s.content.length <= SKILL_CONTENT_MAX_CHARS)
|
|
1437
1446
|
return true;
|
|
@@ -1457,6 +1466,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1457
1466
|
name: s.name,
|
|
1458
1467
|
description: s.description,
|
|
1459
1468
|
...(s.files !== undefined ? { files: s.files.map((f) => ({ path: f.path })) } : {}),
|
|
1469
|
+
...(declaredSkillRank.get(s.name) !== undefined ? { declaredRank: declaredSkillRank.get(s.name) } : {}),
|
|
1460
1470
|
})),
|
|
1461
1471
|
seedAnnounced: resume !== undefined || spec.sessionId !== undefined,
|
|
1462
1472
|
}
|
|
@@ -1485,7 +1495,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1485
1495
|
awarenessEnabled: thinking !== undefined && ULTRA_REASONING_TIERS.has(thinking),
|
|
1486
1496
|
worktreeIsolated: internals?.isolation === "worktree" && ownedEnv !== undefined,
|
|
1487
1497
|
withinTaskCompactionEnabled: (spec.compaction?.enabled ?? true) && (spec.compaction?.withinTask ?? true),
|
|
1488
|
-
isSubagent: internals
|
|
1498
|
+
isSubagent: isDelegatedNonForkChild(internals),
|
|
1489
1499
|
};
|
|
1490
1500
|
const userSystemPrompt = spec.systemPrompt ?? resolvedRole.systemPrompt ?? internals?.defaultSystemPrompt;
|
|
1491
1501
|
const userAppendSystemPrompt = spec.appendSystemPrompt;
|
|
@@ -2382,7 +2392,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2382
2392
|
: foldedPolicy;
|
|
2383
2393
|
const hooks = spec.hooks ?? deps.hooks;
|
|
2384
2394
|
const onAsk = spec.onAsk ?? deps.onAsk;
|
|
2385
|
-
const handWriteTools = handsEnabled
|
|
2395
|
+
const handWriteTools = handsEnabled && spec.handsReadOnly !== true
|
|
2386
2396
|
? Object.keys(HAND_TOOL_EFFECTS).filter((name) => {
|
|
2387
2397
|
const eff = HAND_TOOL_EFFECTS[name];
|
|
2388
2398
|
return eff === "write" || eff === "idempotent";
|
|
@@ -3167,15 +3177,29 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3167
3177
|
const onCompactionApplied = handsEnabled && readFileStateForCheckpoint
|
|
3168
3178
|
? (attachedComplete, preserveReadState) => applyCompactionToReadFileState(readFileStateForCheckpoint, attachedComplete, preserveReadState)
|
|
3169
3179
|
: undefined;
|
|
3180
|
+
const changedFilesReadDenied = denyNarrowingPolicy === undefined
|
|
3181
|
+
? undefined
|
|
3182
|
+
: async (path) => {
|
|
3183
|
+
try {
|
|
3184
|
+
const d = await denyNarrowingPolicy.check({ toolName: "Read", args: { file_path: path }, toolCallId: "changed-files-visibility-probe" }, abortController.signal);
|
|
3185
|
+
return d.action === "deny";
|
|
3186
|
+
}
|
|
3187
|
+
catch {
|
|
3188
|
+
return false;
|
|
3189
|
+
}
|
|
3190
|
+
};
|
|
3170
3191
|
const detectExternalChanges = handsEnabled && readFileStateForCheckpoint
|
|
3171
3192
|
? async (maxFiles) => {
|
|
3172
3193
|
const candidates = [...readFileStateForCheckpoint.entries()]
|
|
3173
3194
|
.filter(([, e]) => e.lastReadAt !== undefined)
|
|
3195
|
+
.filter(([, e]) => e.truncated !== true)
|
|
3174
3196
|
.sort((a, b) => (b[1].lastReadAt ?? 0) - (a[1].lastReadAt ?? 0))
|
|
3175
3197
|
.slice(0, Math.max(0, maxFiles));
|
|
3176
3198
|
const changed = [];
|
|
3177
3199
|
const evicted = [];
|
|
3178
3200
|
for (const [path, entry] of candidates) {
|
|
3201
|
+
if (changedFilesReadDenied !== undefined && (await changedFilesReadDenied(path)))
|
|
3202
|
+
continue;
|
|
3179
3203
|
try {
|
|
3180
3204
|
const info = await executionEnv.fileInfo(path, abortController.signal);
|
|
3181
3205
|
if (!info.ok) {
|
|
@@ -3249,7 +3273,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3249
3273
|
: undefined;
|
|
3250
3274
|
overheadState.promptChars = systemPrompt.length;
|
|
3251
3275
|
const preparedHolder = {};
|
|
3252
|
-
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, cacheBreakDetector, cacheFingerprint, promptManifest, epochDeclaredSections, activeTools, ownedEnv, suspendRef, reviewRef, reviewRequestRef, suspendLoopRef, suspendForResource, ...(callCapRef ? { callCapRef } : {}), ...(cutKills ? { cutKills } : {}), ...(callCapRef ? { callIssuedAtRef } : {}), suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, promptOverheadTokens, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
3276
|
+
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, cacheBreakDetector, cacheFingerprint, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), ownedEnv, suspendRef, reviewRef, reviewRequestRef, suspendLoopRef, suspendForResource, ...(callCapRef ? { callCapRef } : {}), ...(cutKills ? { cutKills } : {}), ...(callCapRef ? { callIssuedAtRef } : {}), suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, promptOverheadTokens, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
3253
3277
|
const prepared = buildPrepared();
|
|
3254
3278
|
preparedHolder.current = prepared;
|
|
3255
3279
|
return prepared;
|