@sema-agent/core 5.8.0 → 5.10.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/CHANGELOG.md +82 -0
- package/dist/agents/cascade.js +24 -0
- package/dist/agents/roster-store.d.ts +1 -0
- package/dist/agents/send-message-tool.js +6 -0
- package/dist/agents/subagent.d.ts +33 -0
- package/dist/agents/subagent.js +125 -33
- package/dist/agents/teacher.js +15 -3
- package/dist/agents/team.js +10 -0
- package/dist/agents/verify.js +7 -0
- package/dist/brain/anthropic.js +27 -10
- package/dist/brain/open-responses.d.ts +11 -0
- package/dist/brain/open-responses.js +736 -0
- package/dist/brain/openai.js +32 -5
- package/dist/brain/request-params.d.ts +1 -0
- package/dist/brain/request-params.js +16 -0
- package/dist/core/a2a.js +1 -1
- package/dist/core/fs-write-gate-policy.js +2 -2
- package/dist/core/lsp-diagnostics.d.ts +3 -2
- package/dist/core/lsp-diagnostics.js +20 -7
- package/dist/core/memory-recall.js +8 -3
- package/dist/core/memory.d.ts +5 -0
- package/dist/core/memory.js +6 -4
- package/dist/core/runner/assemble-result.d.ts +1 -0
- package/dist/core/runner/assemble-result.js +14 -7
- package/dist/core/runner/prepare-task.d.ts +9 -1
- package/dist/core/runner/prepare-task.js +51 -14
- package/dist/core/runner/runtask.d.ts +12 -0
- package/dist/core/runner/runtask.js +153 -42
- package/dist/core/runner/session-file-state-replay.d.ts +7 -0
- package/dist/core/runner/session-file-state-replay.js +56 -0
- package/dist/core/runner/session-rule-policy.d.ts +1 -0
- package/dist/core/runner/session-rule-policy.js +4 -3
- package/dist/core/runner/synthetic-tools.js +1 -1
- package/dist/core/runner/tool-output-projection.js +5 -4
- package/dist/core/session-reconcile.d.ts +7 -3
- package/dist/core/session-reconcile.js +3 -2
- package/dist/core/strategy-store.d.ts +1 -1
- package/dist/core/strategy-store.js +27 -4
- package/dist/core/task-registry-shared.d.ts +0 -1
- package/dist/core/tool-policy.d.ts +8 -0
- package/dist/core/tool-policy.js +11 -0
- package/dist/core/tools.js +9 -1
- package/dist/core/trace.d.ts +0 -2
- package/dist/core/types.d.ts +8 -1
- package/dist/engine/harness/agent-harness.d.ts +1 -0
- package/dist/engine/harness/agent-harness.js +3 -0
- package/dist/engine/harness/types.d.ts +1 -0
- package/dist/engine/llm/types.d.ts +2 -73
- package/dist/engine/loop/agent-loop.js +168 -22
- package/dist/engine/loop/types.d.ts +1 -0
- package/dist/engine/session/repo-utils.d.ts +1 -2
- package/dist/engine/session/repo-utils.js +0 -7
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/internal/llm.d.ts +1 -1
- package/dist/orchestration/run-workflow-tool.js +1 -1
- package/dist/orchestration/workflow-governance.js +19 -0
- package/dist/orchestration/workflow-primitives.d.ts +1 -1
- package/dist/orchestration/workflow-primitives.js +4 -1
- package/dist/orchestration/workflow.js +15 -6
- package/dist/prompts/coordinator.d.ts +1 -1
- package/dist/prompts/coordinator.js +1 -1
- package/dist/stores/file/memory-store.js +3 -7
- package/dist/tools/fs/fs-bash.js +3 -3
- package/dist/tools/fs/fs-shared.d.ts +1 -0
- package/dist/tools/fs/fs-shared.js +4 -0
- package/dist/tools/web.js +20 -20
- package/package.json +5 -3
package/dist/brain/openai.js
CHANGED
|
@@ -481,9 +481,17 @@ export function createOpenAIBrain(config = {}) {
|
|
|
481
481
|
finalContent.push({ type: "text", text: textFace });
|
|
482
482
|
const toolCalls = [];
|
|
483
483
|
const malformed = [];
|
|
484
|
+
const unnamed = [];
|
|
485
|
+
let emptyPlaceholders = 0;
|
|
484
486
|
for (const acc of [...toolAccum.entries()].sort((a, b) => a[0] - b[0]).map((e) => e[1])) {
|
|
485
|
-
if (!acc.name)
|
|
487
|
+
if (!acc.name) {
|
|
488
|
+
if (!acc.id && acc.args === "") {
|
|
489
|
+
emptyPlaceholders++;
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
unnamed.push(`id="${acc.id || "?"}"(${acc.args.slice(0, 200)})`);
|
|
486
493
|
continue;
|
|
494
|
+
}
|
|
487
495
|
const tc = closeToolCallAccum(acc);
|
|
488
496
|
if (tc) {
|
|
489
497
|
toolCalls.push(tc);
|
|
@@ -508,16 +516,35 @@ export function createOpenAIBrain(config = {}) {
|
|
|
508
516
|
}
|
|
509
517
|
}
|
|
510
518
|
}
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
519
|
+
const emptySlots = `<empty slot>${emptyPlaceholders > 1 ? ` ×${emptyPlaceholders}` : ""}`;
|
|
520
|
+
const sawRealAction = toolCalls.length > 0 || malformed.length > 0 || unnamed.length > 0;
|
|
521
|
+
if (emptyPlaceholders > 0 && sawRealAction) {
|
|
522
|
+
unnamed.push(emptySlots);
|
|
523
|
+
}
|
|
524
|
+
else if (!sawRealAction && finishReason === "tool_calls") {
|
|
525
|
+
unnamed.push(emptyPlaceholders > 0 ? emptySlots : "<no tool_call delta arrived on the stream>");
|
|
526
|
+
}
|
|
527
|
+
const toolErrorParts = [];
|
|
528
|
+
if (malformed.length > 0) {
|
|
529
|
+
toolErrorParts.push(`tool call argument(s) not valid JSON (likely truncated, finish_reason="${finishReason ?? "?"}"): ${malformed.join("; ")}`);
|
|
530
|
+
}
|
|
531
|
+
if (unnamed.length > 0) {
|
|
532
|
+
toolErrorParts.push(`tool call(s) arrived with no tool name and cannot be executed (finish_reason="${finishReason ?? "?"}"): ${unnamed.join("; ")}`);
|
|
533
|
+
}
|
|
534
|
+
const toolError = toolErrorParts.length > 0 ? toolErrorParts.join(" | ") : undefined;
|
|
514
535
|
const noUsableContent = toolCalls.length === 0 && !accumText.trim();
|
|
515
|
-
if (
|
|
536
|
+
if (malformed.length > 0) {
|
|
516
537
|
finalContent.push({
|
|
517
538
|
type: "text",
|
|
518
539
|
text: `\n[note: ${malformed.length} tool call(s) were truncated (finish_reason="${finishReason ?? "?"}") and dropped — re-issue them next turn: ${malformed.join("; ")}]`,
|
|
519
540
|
});
|
|
520
541
|
}
|
|
542
|
+
if (unnamed.length > 0) {
|
|
543
|
+
finalContent.push({
|
|
544
|
+
type: "text",
|
|
545
|
+
text: `\n[note: ${unnamed.length} tool call(s) arrived with no tool name and were dropped — re-issue them next turn with an explicit tool name: ${unnamed.join("; ")}]`,
|
|
546
|
+
});
|
|
547
|
+
}
|
|
521
548
|
if (malformedFrames > 0) {
|
|
522
549
|
finalContent.push({
|
|
523
550
|
type: "text",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const OPENAI_RESERVED: ReadonlySet<string>;
|
|
2
2
|
export declare const ANTHROPIC_RESERVED: ReadonlySet<string>;
|
|
3
|
+
export declare const RESPONSES_RESERVED: ReadonlySet<string>;
|
|
3
4
|
export declare function reservedFor(api: string): ReadonlySet<string>;
|
|
4
5
|
export declare function applyExtraBody(body: Record<string, unknown>, extraBody: Record<string, unknown> | undefined, reserved: ReadonlySet<string>): Record<string, unknown>;
|
|
5
6
|
export declare function stripAuthHeaders(headers: Record<string, string>): void;
|
|
@@ -25,7 +25,23 @@ export const ANTHROPIC_RESERVED = new Set([
|
|
|
25
25
|
"output_config",
|
|
26
26
|
"context_management",
|
|
27
27
|
]);
|
|
28
|
+
export const RESPONSES_RESERVED = new Set([
|
|
29
|
+
"model",
|
|
30
|
+
"input",
|
|
31
|
+
"stream",
|
|
32
|
+
"instructions",
|
|
33
|
+
"tools",
|
|
34
|
+
"temperature",
|
|
35
|
+
"max_output_tokens",
|
|
36
|
+
"reasoning",
|
|
37
|
+
"store",
|
|
38
|
+
"previous_response_id",
|
|
39
|
+
"conversation",
|
|
40
|
+
]);
|
|
41
|
+
const RESPONSES_APIS = new Set(["openai-responses", "azure-openai-responses", "openai-chatgpt-responses"]);
|
|
28
42
|
export function reservedFor(api) {
|
|
43
|
+
if (RESPONSES_APIS.has(api))
|
|
44
|
+
return RESPONSES_RESERVED;
|
|
29
45
|
return api === "anthropic-messages" ? ANTHROPIC_RESERVED : OPENAI_RESERVED;
|
|
30
46
|
}
|
|
31
47
|
export function applyExtraBody(body, extraBody, reserved) {
|
package/dist/core/a2a.js
CHANGED
|
@@ -23,7 +23,7 @@ const A2A_CARD_DESCRIPTION_MAX_CHARS = 240;
|
|
|
23
23
|
const A2A_ID_MAX_CHARS = 160;
|
|
24
24
|
const A2A_RESULT_BODY_MAX_CHARS = 100_000;
|
|
25
25
|
const A2A_ERROR_TEXT_MAX_CHARS = 240;
|
|
26
|
-
const A2A_RESULT_FENCE_REASON = "the peer is an
|
|
26
|
+
const A2A_RESULT_FENCE_REASON = "the peer is an agent acting on its own, so its output is worker text, not tool data";
|
|
27
27
|
const A2A_CARD_PATHS = ["/.well-known/agent-card.json", "/.well-known/agent.json"];
|
|
28
28
|
const A2A_PROTOCOL_VERSION = "1.0";
|
|
29
29
|
const A2A_JSONRPC_TRANSPORT = "JSONRPC";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { canonicalizeTarget, writeTargetPath } from "../tools/fs/safety.js";
|
|
2
|
-
import {
|
|
2
|
+
import { PATH_CONFINABLE_WRITE_TOOLS, isWithin } from "./runner/session-rule-policy.js";
|
|
3
3
|
const ask = (message) => ({ action: "ask", message, decisionReason: "rule" });
|
|
4
4
|
export function createFsWriteGatePolicy(opts) {
|
|
5
5
|
const { env, rootPath, defaultWrite } = opts;
|
|
6
|
-
const gated =
|
|
6
|
+
const gated = PATH_CONFINABLE_WRITE_TOOLS;
|
|
7
7
|
const acceptDirs = opts.acceptDirs && opts.acceptDirs.length > 0 ? opts.acceptDirs : undefined;
|
|
8
8
|
const exemptDirs = opts.exemptDirs && opts.exemptDirs.length > 0 ? opts.exemptDirs : undefined;
|
|
9
9
|
return {
|
|
@@ -22,9 +22,10 @@ export declare class LspDiagnosticsRegistry {
|
|
|
22
22
|
private readonly pending;
|
|
23
23
|
private readonly delivered;
|
|
24
24
|
publish(uri: string, diagnostics: LspDiagnostic[]): void;
|
|
25
|
-
fileEdited(uri: string): void;
|
|
25
|
+
fileEdited(runIdent: string, uri: string): void;
|
|
26
|
+
releaseRun(runIdent: string): void;
|
|
26
27
|
isEmpty(): boolean;
|
|
27
|
-
drain(): LspFileDiagnostics[];
|
|
28
|
+
drain(runIdent: string): LspFileDiagnostics[];
|
|
28
29
|
}
|
|
29
30
|
export declare function formatDiagnosticsSummary(files: LspFileDiagnostics[]): string;
|
|
30
31
|
export declare function formatDiagnosticsBlock(files: LspFileDiagnostics[]): string;
|
|
@@ -16,30 +16,39 @@ function diagnosticKey(uri, d) {
|
|
|
16
16
|
}
|
|
17
17
|
export class LspDiagnosticsRegistry {
|
|
18
18
|
pending = new Map();
|
|
19
|
-
delivered = new
|
|
19
|
+
delivered = new Map();
|
|
20
20
|
publish(uri, diagnostics) {
|
|
21
21
|
if (diagnostics.length === 0)
|
|
22
22
|
this.pending.delete(uri);
|
|
23
23
|
else
|
|
24
24
|
this.pending.set(uri, diagnostics);
|
|
25
25
|
}
|
|
26
|
-
fileEdited(uri) {
|
|
26
|
+
fileEdited(runIdent, uri) {
|
|
27
|
+
const forRun = this.delivered.get(runIdent);
|
|
28
|
+
if (forRun === undefined)
|
|
29
|
+
return;
|
|
27
30
|
const prefix = `${uri}|`;
|
|
28
|
-
for (const key of
|
|
31
|
+
for (const key of forRun) {
|
|
29
32
|
if (key.startsWith(prefix))
|
|
30
|
-
|
|
33
|
+
forRun.delete(key);
|
|
31
34
|
}
|
|
35
|
+
if (forRun.size === 0)
|
|
36
|
+
this.delivered.delete(runIdent);
|
|
37
|
+
}
|
|
38
|
+
releaseRun(runIdent) {
|
|
39
|
+
this.delivered.delete(runIdent);
|
|
32
40
|
}
|
|
33
41
|
isEmpty() {
|
|
34
42
|
return this.pending.size === 0;
|
|
35
43
|
}
|
|
36
|
-
drain() {
|
|
44
|
+
drain(runIdent) {
|
|
37
45
|
const out = [];
|
|
38
46
|
const requeued = new Map();
|
|
47
|
+
let delivered = this.delivered.get(runIdent);
|
|
39
48
|
let total = 0;
|
|
40
49
|
for (const [uri, all] of this.pending) {
|
|
41
50
|
const fresh = all
|
|
42
|
-
.filter((d) =>
|
|
51
|
+
.filter((d) => delivered?.has(diagnosticKey(uri, d)) !== true)
|
|
43
52
|
.sort((a, b) => (a.severity ?? 99) - (b.severity ?? 99));
|
|
44
53
|
if (fresh.length === 0)
|
|
45
54
|
continue;
|
|
@@ -49,8 +58,12 @@ export class LspDiagnosticsRegistry {
|
|
|
49
58
|
requeued.set(uri, fresh.slice(take.length));
|
|
50
59
|
if (take.length === 0)
|
|
51
60
|
continue;
|
|
61
|
+
if (delivered === undefined) {
|
|
62
|
+
delivered = new Set();
|
|
63
|
+
this.delivered.set(runIdent, delivered);
|
|
64
|
+
}
|
|
52
65
|
for (const d of take)
|
|
53
|
-
|
|
66
|
+
delivered.add(diagnosticKey(uri, d));
|
|
54
67
|
total += take.length;
|
|
55
68
|
out.push({ uri, diagnostics: take });
|
|
56
69
|
}
|
|
@@ -30,6 +30,11 @@ export const RECALL_CAVEAT = "These notes were recalled as relevant, but they ar
|
|
|
30
30
|
"- If the user is about to act on your recommendation (not just asking about history), verify first.\n" +
|
|
31
31
|
"- If a note names a file, check the file exists; if it names a function or symbol, grep for it; if it names a flag or value, read the current source.\n" +
|
|
32
32
|
"- Memory is a snapshot from when it was written, not a live view — prefer `git log` or reading the code over recalling the snapshot.";
|
|
33
|
+
function isNewerNote(candidate, incumbent) {
|
|
34
|
+
if (candidate.timestampMissing || incumbent.timestampMissing)
|
|
35
|
+
return false;
|
|
36
|
+
return candidate.mtimeMs > incumbent.mtimeMs;
|
|
37
|
+
}
|
|
33
38
|
export function resolveLinkedIds(headers, selected, max) {
|
|
34
39
|
if (max <= 0 || selected.length === 0)
|
|
35
40
|
return [];
|
|
@@ -38,7 +43,7 @@ export function resolveLinkedIds(headers, selected, max) {
|
|
|
38
43
|
if (!h.name)
|
|
39
44
|
continue;
|
|
40
45
|
const prev = byName.get(h.name);
|
|
41
|
-
if (!prev || h
|
|
46
|
+
if (!prev || isNewerNote(h, prev))
|
|
42
47
|
byName.set(h.name, h);
|
|
43
48
|
}
|
|
44
49
|
const selectedIds = new Set(selected.map((r) => r.id));
|
|
@@ -100,8 +105,8 @@ export function validateSelectedIds(headers, ids, max) {
|
|
|
100
105
|
export function composeSelectiveBody(manifestText, selected, nowMs, linked = [], recallable = true) {
|
|
101
106
|
const renderNote = (r, label) => {
|
|
102
107
|
const ageMs = nowMs - r.mtimeMs;
|
|
103
|
-
const verify = ageMs > ONE_DAY_MS ? " — verify it's still current" : "";
|
|
104
|
-
const stale = ` (written ${formatMemoryAge(ageMs)}${verify})`;
|
|
108
|
+
const verify = r.timestampMissing || ageMs > ONE_DAY_MS ? " — verify it's still current" : "";
|
|
109
|
+
const stale = r.timestampMissing ? ` (write time unknown${verify})` : ` (written ${formatMemoryAge(ageMs)}${verify})`;
|
|
105
110
|
const prefix = label ? `${sanitizeUntrustedText(label, ["user_memory"])} ` : "";
|
|
106
111
|
return `- ${prefix}${sanitizeUntrustedText(r.text, ["user_memory"])}${stale}`;
|
|
107
112
|
};
|
package/dist/core/memory.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ export interface MemoryNoteHeader {
|
|
|
40
40
|
id: string;
|
|
41
41
|
description: string;
|
|
42
42
|
mtimeMs: number;
|
|
43
|
+
timestampMissing?: true;
|
|
43
44
|
name?: string;
|
|
44
45
|
type?: string;
|
|
45
46
|
consolidationGenerated?: boolean;
|
|
@@ -84,6 +85,10 @@ export declare class InMemoryMemoryStore implements MemoryStore {
|
|
|
84
85
|
export declare function expandLexicalTerms(term: string): string[];
|
|
85
86
|
export declare function lexicalSearchMatch(query: string, text: string): boolean;
|
|
86
87
|
export declare function firstSentence(text: string): string;
|
|
88
|
+
export declare function parseNoteTimestamp(ts: unknown): {
|
|
89
|
+
mtimeMs: number;
|
|
90
|
+
timestampMissing?: true;
|
|
91
|
+
};
|
|
87
92
|
export interface NormalizedMemorySpec {
|
|
88
93
|
scopes: string[];
|
|
89
94
|
writeScope: string | null;
|
package/dist/core/memory.js
CHANGED
|
@@ -268,7 +268,7 @@ export class InMemoryMemoryStore {
|
|
|
268
268
|
return entries.map((e) => ({
|
|
269
269
|
id: e.id,
|
|
270
270
|
description: e.description ?? firstSentence(e.text),
|
|
271
|
-
|
|
271
|
+
...parseNoteTimestamp(e.ts),
|
|
272
272
|
...(e.name ? { name: e.name } : {}),
|
|
273
273
|
...(e.type ? { type: e.type } : {}),
|
|
274
274
|
...(e.consolidationGenerated ? { consolidationGenerated: true } : {}),
|
|
@@ -291,7 +291,7 @@ export class InMemoryMemoryStore {
|
|
|
291
291
|
id: e.id,
|
|
292
292
|
text: e.text,
|
|
293
293
|
description: e.description ?? firstSentence(e.text),
|
|
294
|
-
|
|
294
|
+
...parseNoteTimestamp(e.ts),
|
|
295
295
|
...(e.name ? { name: e.name } : {}),
|
|
296
296
|
...(e.type ? { type: e.type } : {}),
|
|
297
297
|
...(e.consolidationGenerated ? { consolidationGenerated: true } : {}),
|
|
@@ -347,9 +347,11 @@ export function firstSentence(text) {
|
|
|
347
347
|
const cut = dot >= 0 && dot < 160 ? dot + 1 : Math.min(t.length, 120);
|
|
348
348
|
return t.slice(0, cut).trim();
|
|
349
349
|
}
|
|
350
|
-
function
|
|
350
|
+
export function parseNoteTimestamp(ts) {
|
|
351
|
+
if (typeof ts !== "string" || ts.trim() === "")
|
|
352
|
+
return { mtimeMs: 0, timestampMissing: true };
|
|
351
353
|
const ms = Date.parse(`${ts.replace(" ", "T")}:00Z`);
|
|
352
|
-
return Number.isFinite(ms) ? ms : 0;
|
|
354
|
+
return Number.isFinite(ms) ? { mtimeMs: ms } : { mtimeMs: 0, timestampMissing: true };
|
|
353
355
|
}
|
|
354
356
|
export function normalizeMemorySpec(input) {
|
|
355
357
|
if (!input)
|
|
@@ -59,6 +59,7 @@ export interface ResultFlags {
|
|
|
59
59
|
unpricedSpend?: boolean;
|
|
60
60
|
rewindNotes?: TaskResult["rewindNotes"];
|
|
61
61
|
remoteEnvFailures?: TaskResult["remoteEnvFailures"];
|
|
62
|
+
retryAfterMs?: number;
|
|
62
63
|
abortedForTimeout: boolean;
|
|
63
64
|
abortedForTurns: boolean;
|
|
64
65
|
abortedLive?: boolean;
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
2
2
|
import { extractErrorCode, stripErrorCodePrefix } from "../../brain/errors.js";
|
|
3
|
+
const SALVAGE_ELIGIBLE_TERMINALS = new Set([
|
|
4
|
+
"output.degenerate",
|
|
5
|
+
"limits.max_tokens_exceeded",
|
|
6
|
+
"limits.max_cost_exceeded",
|
|
7
|
+
"limits.max_turns_exceeded",
|
|
8
|
+
"limits.max_walltime_exceeded",
|
|
9
|
+
"env.lifetime_expired",
|
|
10
|
+
"usage.window_exhausted",
|
|
11
|
+
]);
|
|
3
12
|
export function errorCodeOf(err) {
|
|
4
13
|
let cur = err;
|
|
5
14
|
let fallback;
|
|
@@ -63,7 +72,6 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
63
72
|
status = "failed";
|
|
64
73
|
errorCode = "output.degenerate";
|
|
65
74
|
errorMessage = final?.errorMessage;
|
|
66
|
-
salvagedOutput = text.trim() || undefined;
|
|
67
75
|
}
|
|
68
76
|
else if (flags.budgetHit) {
|
|
69
77
|
status = "failed";
|
|
@@ -73,7 +81,6 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
73
81
|
flags.budgetHit === "precall"
|
|
74
82
|
? `the estimated cost of the first call already exceeds ${axisName}; the task was not started`
|
|
75
83
|
: `cumulative usage exceeded ${axisName}`;
|
|
76
|
-
salvagedOutput = text.trim() || undefined;
|
|
77
84
|
}
|
|
78
85
|
else if (flags.suspendLoop) {
|
|
79
86
|
status = "failed";
|
|
@@ -90,8 +97,6 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
90
97
|
: flags.conflict
|
|
91
98
|
? "conflict"
|
|
92
99
|
: errorCodeOf(flags.threw);
|
|
93
|
-
if (flags.abortedForTimeout || flags.abortedForTurns)
|
|
94
|
-
salvagedOutput = text.trim() || undefined;
|
|
95
100
|
}
|
|
96
101
|
else if (flags.blockedReason) {
|
|
97
102
|
status = "blocked";
|
|
@@ -114,8 +119,6 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
114
119
|
status = "failed";
|
|
115
120
|
errorCode = flags.abortedForTimeout ? "limits.max_walltime_exceeded" : flags.abortedForTurns ? "limits.max_turns_exceeded" : undefined;
|
|
116
121
|
errorMessage = final?.errorMessage ?? (flags.abortedForTurns ? "max turns exceeded" : "run aborted");
|
|
117
|
-
if (flags.abortedForTimeout || flags.abortedForTurns)
|
|
118
|
-
salvagedOutput = text.trim() || undefined;
|
|
119
122
|
}
|
|
120
123
|
else if (!final) {
|
|
121
124
|
status = "failed";
|
|
@@ -139,9 +142,13 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
139
142
|
else {
|
|
140
143
|
status = "completed";
|
|
141
144
|
}
|
|
145
|
+
if (errorCode !== undefined && SALVAGE_ELIGIBLE_TERMINALS.has(errorCode)) {
|
|
146
|
+
salvagedOutput = text.trim() || undefined;
|
|
147
|
+
}
|
|
148
|
+
const retryAfterMs = errorCode === "usage.window_exhausted" ? flags.retryAfterMs : undefined;
|
|
142
149
|
const { compactionMicroUsd: _internalCompaction, ...publicStats } = stats;
|
|
143
150
|
void _internalCompaction;
|
|
144
151
|
if (flags.unpricedSpend)
|
|
145
152
|
delete publicStats.costMicroUsd;
|
|
146
|
-
return { taskId, sessionId, status, ...(flags.model !== undefined ? { model: flags.model } : {}), result: result.trim(), salvagedOutput, blockedReason, errorMessage, errorCode, checkpointToken, checkpointGate, ...(workspaceRestoreMode !== undefined ? { workspaceRestoreMode } : {}), ...(flags.rewindNotes !== undefined && flags.rewindNotes.length > 0 ? { rewindNotes: flags.rewindNotes } : {}), ...(flags.remoteEnvFailures !== undefined && flags.remoteEnvFailures.length > 0 ? { remoteEnvFailures: flags.remoteEnvFailures } : {}), stats: publicStats };
|
|
153
|
+
return { taskId, sessionId, status, ...(flags.model !== undefined ? { model: flags.model } : {}), result: result.trim(), salvagedOutput, blockedReason, errorMessage, errorCode, ...(retryAfterMs !== undefined ? { retryAfterMs } : {}), checkpointToken, checkpointGate, ...(workspaceRestoreMode !== undefined ? { workspaceRestoreMode } : {}), ...(flags.rewindNotes !== undefined && flags.rewindNotes.length > 0 ? { rewindNotes: flags.rewindNotes } : {}), ...(flags.remoteEnvFailures !== undefined && flags.remoteEnvFailures.length > 0 ? { remoteEnvFailures: flags.remoteEnvFailures } : {}), stats: publicStats };
|
|
147
154
|
}
|
|
@@ -9,6 +9,7 @@ import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
|
9
9
|
import type { OnAsk, ToolPolicy } from "../tool-policy.js";
|
|
10
10
|
import { type ActiveSkillFrame } from "./active-skill-scope.js";
|
|
11
11
|
import type { SessionPermissionRules } from "../session-policy-store.js";
|
|
12
|
+
import { type RecoveredOrphan } from "../session-reconcile.js";
|
|
12
13
|
import { CacheBreakDetector, type ToolFingerprintInput } from "../cache-break-detector.js";
|
|
13
14
|
import { type BrainCallGuardrailRef } from "../../brain/timeout.js";
|
|
14
15
|
import { type OutputRef, type BlockedRef, type SkillListingEntry } from "./synthetic-tools.js";
|
|
@@ -18,7 +19,7 @@ import type { TaskNotificationPayload } from "../task-notification.js";
|
|
|
18
19
|
import { type CwdRef } from "../../tools/fs/index.js";
|
|
19
20
|
import { type WorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
20
21
|
import type { Runner } from "./runtask.js";
|
|
21
|
-
import { type CheckpointGate, type CheckpointState, type CheckpointToken, type ResourceLedger, type PlatformLimitReason, type ResourceLimitReason } from "../checkpoint-store.js";
|
|
22
|
+
import { type CheckpointGate, type CheckpointState, type CheckpointStore, type CheckpointToken, type ResourceLedger, type PlatformLimitReason, type ResourceLimitReason } from "../checkpoint-store.js";
|
|
22
23
|
import type { ActiveWorktreeSession, AgentMessage, AgentTool, ExecutionEnv } from "../../internal/harness.js";
|
|
23
24
|
import type { NestedUsageAccum, RunnerDeps, TaskEvent, TaskLimits, TaskResult, TaskSpec, ToolActivity, ToolEffect } from "../types.js";
|
|
24
25
|
import type { RepairBundle } from "../../agents/repair-loop.js";
|
|
@@ -42,6 +43,11 @@ export declare function checkpointScopeOf(spec: {
|
|
|
42
43
|
};
|
|
43
44
|
principal?: string;
|
|
44
45
|
}): string;
|
|
46
|
+
export declare function resolveCheckpointStore(spec: {
|
|
47
|
+
checkpointStore?: CheckpointStore | null;
|
|
48
|
+
}, deps: {
|
|
49
|
+
checkpointStore?: CheckpointStore;
|
|
50
|
+
}): CheckpointStore | undefined;
|
|
45
51
|
export interface Prepared {
|
|
46
52
|
harness: AgentHarness;
|
|
47
53
|
session: StoredSession;
|
|
@@ -187,6 +193,7 @@ export interface Prepared {
|
|
|
187
193
|
now: () => number;
|
|
188
194
|
tools: AgentTool[];
|
|
189
195
|
toolEffects: Map<string, ToolEffect>;
|
|
196
|
+
wakeRecovered: RecoveredOrphan[];
|
|
190
197
|
promptOverheadTokens: number;
|
|
191
198
|
readTaskFile?: (path: string) => Promise<string | null>;
|
|
192
199
|
recentlyReadFiles?: () => string[];
|
|
@@ -199,6 +206,7 @@ export interface Prepared {
|
|
|
199
206
|
lspDiagnostics?: {
|
|
200
207
|
registry: import("../lsp-diagnostics.js").LspDiagnosticsRegistry;
|
|
201
208
|
nudge: (rawPath: string) => void;
|
|
209
|
+
runIdent: string;
|
|
202
210
|
};
|
|
203
211
|
planModeRef: {
|
|
204
212
|
active: boolean;
|
|
@@ -17,7 +17,7 @@ import { createSubagentWorktreeHelper, forkGovernanceDenial } from "../../agents
|
|
|
17
17
|
import { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME } from "../../agents/agent-transcript-tool.js";
|
|
18
18
|
import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
19
19
|
import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
20
|
-
import { combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets } from "../tool-policy.js";
|
|
20
|
+
import { askApproverIdentity, combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets } from "../tool-policy.js";
|
|
21
21
|
import { ActiveSkillScope, createActiveSkillScopePolicy } from "./active-skill-scope.js";
|
|
22
22
|
import { CHANGED_FILES_MTIME_EPS_MS, fenceMcpServerInstructions, renderAgentListingDelta } from "./turn-attachments.js";
|
|
23
23
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
@@ -57,7 +57,7 @@ import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.j
|
|
|
57
57
|
import { createTaskOutputTool, createTaskStopTool, defaultTaskRegistry } from "../task-registry.js";
|
|
58
58
|
import { createMonitorTool } from "../../tools/monitor.js";
|
|
59
59
|
import { createWorktreeTools } from "../../tools/worktree.js";
|
|
60
|
-
import { applyCompactionToReadFileState, bashReversibilityProbe, createHandsToolkit, isReadDedupStubResult, seedReadFileStateFromContext, HAND_TOOL_EFFECTS, pdfModelCapabilitiesOf } from "../../tools/fs/index.js";
|
|
60
|
+
import { applyCompactionToReadFileState, bashReversibilityProbe, createHandsToolkit, isReadDedupStubResult, seedReadFileStateFromContext, seedReadFileStateFromTranscript, HAND_TOOL_EFFECTS, pdfModelCapabilitiesOf } from "../../tools/fs/index.js";
|
|
61
61
|
import { decodeTextBytes } from "../../tools/fs/encoding.js";
|
|
62
62
|
import { ASK_USER_QUESTION_TOOL_NAME, createAskUserQuestionTool } from "../ask-question.js";
|
|
63
63
|
import { createSchedulerTools } from "../../tools/scheduler-tools.js";
|
|
@@ -67,6 +67,7 @@ import { createRunWorkflowTool, RUN_WORKFLOW_TOOL_NAME } from "../../orchestrati
|
|
|
67
67
|
import { resolveWorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
68
68
|
import { createLspTool, gitCheckIgnoreFilter, resolveLspPath } from "../lsp.js";
|
|
69
69
|
import { resolveKey } from "../../tools/fs/safety.js";
|
|
70
|
+
import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
|
|
70
71
|
import { BINDING_CHECKPOINT_VERSION, mintCheckpointToken, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, buildRiskDescriptor, debitLedger, remainingBudgetMicroUsd, } from "../checkpoint-store.js";
|
|
71
72
|
import { boundInputHashOf } from "../canonical-json.js";
|
|
72
73
|
import { GLOBAL_USAGE_KEY, resolveUsageWindows, usageRetryAfterMs } from "../usage-window-store.js";
|
|
@@ -171,6 +172,11 @@ export const DEFAULT_IRREVERSIBLE_SCOPE = "irreversible";
|
|
|
171
172
|
export function checkpointScopeOf(spec) {
|
|
172
173
|
return spec.durableApproval?.scope || spec.principal || DEFAULT_IRREVERSIBLE_SCOPE;
|
|
173
174
|
}
|
|
175
|
+
export function resolveCheckpointStore(spec, deps) {
|
|
176
|
+
if (spec.checkpointStore === null)
|
|
177
|
+
return undefined;
|
|
178
|
+
return spec.checkpointStore ?? deps.checkpointStore;
|
|
179
|
+
}
|
|
174
180
|
export function isFableFamilyModelId(id) {
|
|
175
181
|
const tail = id.toLowerCase().split("/").pop() ?? "";
|
|
176
182
|
return /^claude-fable-\d/.test(tail) || /^claude-mythos-5(?!\d)/.test(tail);
|
|
@@ -319,6 +325,20 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
319
325
|
throw e;
|
|
320
326
|
}
|
|
321
327
|
resolveTaskLimits(spec.limits);
|
|
328
|
+
if (spec.resourceSuspend !== undefined) {
|
|
329
|
+
const rsus = spec.resourceSuspend;
|
|
330
|
+
if (typeof rsus.scope !== "string" || rsus.scope === "") {
|
|
331
|
+
throw limitConfigError("config.limit_invalid", `TaskSpec.resourceSuspend.scope must be a non-empty string (got ${String(rsus.scope)}) — it is the multi-tenant isolation key every resource checkpoint is filed under.`);
|
|
332
|
+
}
|
|
333
|
+
for (const key of ["totalBudgetUsd", "totalTokens", "maxSlices", "ttlMs"]) {
|
|
334
|
+
const value = rsus[key];
|
|
335
|
+
if (value === undefined)
|
|
336
|
+
continue;
|
|
337
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
338
|
+
throw limitConfigError("config.limit_invalid", `TaskSpec.resourceSuspend.${key} must be a finite, non-negative number (got ${String(value)}) — an unevaluable allocation is not an allocation, and a NaN here blinds even the validated per-slice window.`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
322
342
|
const usageWindows = resolveUsageWindows(deps.usageWindows);
|
|
323
343
|
const brainCallGuardrailRef = {};
|
|
324
344
|
const brainCallGuardrailMs = resolveBrainCallGuardrailMs(spec.limits?.brainCallGuardrailMs ?? deps.brainCallGuardrailMs);
|
|
@@ -411,6 +431,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
411
431
|
let acquired;
|
|
412
432
|
let session;
|
|
413
433
|
let conflictRef;
|
|
434
|
+
let wakeRecovered = [];
|
|
414
435
|
let resumeAtBeforeParentId = null;
|
|
415
436
|
for (let attempt = 0;; attempt++) {
|
|
416
437
|
try {
|
|
@@ -487,7 +508,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
487
508
|
}
|
|
488
509
|
await session.getStorage().setLeafId(spec.resumeAtMode === "before" ? entry.parentId : spec.resumeAt);
|
|
489
510
|
}
|
|
490
|
-
await reconcileInterruptedSession(session, toolEffects, resume?.suspendedBatch);
|
|
511
|
+
wakeRecovered = (await reconcileInterruptedSession(session, toolEffects, resume?.suspendedBatch)).recovered;
|
|
491
512
|
break;
|
|
492
513
|
}
|
|
493
514
|
catch (err) {
|
|
@@ -910,6 +931,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
910
931
|
const effectiveShellGate = inheritedShellGate !== undefined && shellGateRank[inheritedShellGate] > shellGateRank[specShellGate] ? inheritedShellGate : specShellGate;
|
|
911
932
|
const ownSessionRulesRef = {};
|
|
912
933
|
const frozenOnAsk = spec.onAsk ?? deps.onAsk;
|
|
934
|
+
const frozenOnQuestion = spec.onQuestion ?? deps.onQuestion;
|
|
913
935
|
const inheritedGateForChildren = () => {
|
|
914
936
|
const ancestorRules = [
|
|
915
937
|
...(inheritedAncestorRules ?? []),
|
|
@@ -944,6 +966,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
944
966
|
model: harnessRef.current?.getModel(),
|
|
945
967
|
thinkingLevel: harnessRef.current?.getThinkingLevel(),
|
|
946
968
|
principal: spec.principal,
|
|
969
|
+
...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
|
|
970
|
+
...(frozenOnQuestion !== undefined ? { onQuestion: frozenOnQuestion } : {}),
|
|
971
|
+
...(spec.handsReadOnly === true ? { handsReadOnly: true } : {}),
|
|
972
|
+
...(spec.interactiveTools === false ? { interactiveTools: false } : {}),
|
|
947
973
|
oneShot: spec.oneShot,
|
|
948
974
|
clientContext: spec.clientContext,
|
|
949
975
|
excludeTools: toolFaceSnapshot.exclude,
|
|
@@ -960,6 +986,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
960
986
|
inheritedGateForChildren,
|
|
961
987
|
...(autoModeDecider ? { autoModeReview: { decider: autoModeDecider } } : {}),
|
|
962
988
|
...(spec.durableApproval !== undefined ? { durableApprovalForChildren: { ...spec.durableApproval } } : {}),
|
|
989
|
+
...(spec.checkpointStore === null ? { checkpointStoreDisabledForChildren: true } : {}),
|
|
963
990
|
taskId: hostTaskId,
|
|
964
991
|
...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
|
|
965
992
|
...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}),
|
|
@@ -1013,7 +1040,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1013
1040
|
tools.push(createReportFindingsTool());
|
|
1014
1041
|
}
|
|
1015
1042
|
if (spec.enablePlanMode === true && spec.interactiveTools !== false) {
|
|
1016
|
-
const planReviewFace = (spec
|
|
1043
|
+
const planReviewFace = resolveCheckpointStore(spec, deps) !== undefined;
|
|
1017
1044
|
if (spec.interactiveTools === true || planReviewFace) {
|
|
1018
1045
|
tools.push(defineTool(createPresentPlanTool(requestReview)));
|
|
1019
1046
|
if (planReviewFace) {
|
|
@@ -1351,6 +1378,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1351
1378
|
}
|
|
1352
1379
|
const readFileState = new Map((resume?.seed.readFileState ?? []).map(([k, v]) => [rebaseRestoredPath(k), v]));
|
|
1353
1380
|
readFileStateForCheckpoint = readFileState;
|
|
1381
|
+
if (resume === undefined && spec.sessionId !== undefined) {
|
|
1382
|
+
const prior = await session.buildContext().catch(() => undefined);
|
|
1383
|
+
for (const rec of wholeFileRecordsFromTranscript(prior?.messages ?? [])) {
|
|
1384
|
+
const rk = await resolveKey(executionEnv, rootCanonical, rec.path, undefined, undefined, additionalRootsCanonical);
|
|
1385
|
+
if (rk.ok)
|
|
1386
|
+
seedReadFileStateFromTranscript(readFileState, rk.key, rec.content, rec.at);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1354
1389
|
seedContextFiles = async (files) => {
|
|
1355
1390
|
for (const f of files) {
|
|
1356
1391
|
const rk = await resolveKey(executionEnv, rootCanonical, f.path, undefined, undefined, additionalRootsCanonical);
|
|
@@ -1532,8 +1567,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1532
1567
|
}
|
|
1533
1568
|
if (offloadStore)
|
|
1534
1569
|
tools.push(createReadToolResultTool(offloadStore));
|
|
1535
|
-
const onQuestion =
|
|
1536
|
-
const durableQuestionFace = (spec
|
|
1570
|
+
const onQuestion = frozenOnQuestion;
|
|
1571
|
+
const durableQuestionFace = resolveCheckpointStore(spec, deps) !== undefined &&
|
|
1537
1572
|
(spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true);
|
|
1538
1573
|
if (spec.interactiveTools === true || (spec.interactiveTools !== false && (onQuestion !== undefined || durableQuestionFace)))
|
|
1539
1574
|
tools.push(createAskUserQuestionTool(onQuestion, { principal: spec.principal, sourceTaskId: sessionId }));
|
|
@@ -1562,11 +1597,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1562
1597
|
const lspDiagnostics = spec.lspDiagnostics !== false && lspManager?.diagnostics !== undefined && handsEnabled && spec.handsReadOnly !== true
|
|
1563
1598
|
? lspManager.diagnostics
|
|
1564
1599
|
: undefined;
|
|
1600
|
+
const lspRunIdent = sessionId;
|
|
1565
1601
|
const nudgeLspOnEdit = lspDiagnostics
|
|
1566
1602
|
? (rawPath) => {
|
|
1567
1603
|
const baseDir = handsCwdRef?.current ?? taskRootPath;
|
|
1568
1604
|
const filePath = resolveLspPath(rawPath, baseDir);
|
|
1569
|
-
lspDiagnostics.fileEdited(pathToUri(filePath));
|
|
1605
|
+
lspDiagnostics.fileEdited(lspRunIdent, pathToUri(filePath));
|
|
1570
1606
|
void lspManager
|
|
1571
1607
|
.sessionFor(filePath, undefined, executionEnv)
|
|
1572
1608
|
.then((session) => session?.notifyFileChanged?.(filePath))
|
|
@@ -2203,6 +2239,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2203
2239
|
? { legDate: envFacts.date, today: () => formatLocalDate(new Date(), tzValid ? userTz : undefined) }
|
|
2204
2240
|
: undefined;
|
|
2205
2241
|
const harness = new AgentHarness({
|
|
2242
|
+
abortResultDetails: () => suspendRef.token !== undefined || reviewRef.token !== undefined ? { code: "gate.parked" } : undefined,
|
|
2206
2243
|
...(spec.limits?.maxOutputTokens !== undefined && spec.limits.maxOutputTokens > 0
|
|
2207
2244
|
? { maxOutputTokens: spec.limits.maxOutputTokens }
|
|
2208
2245
|
: {}),
|
|
@@ -2459,7 +2496,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2459
2496
|
if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME)
|
|
2460
2497
|
return first;
|
|
2461
2498
|
if (pc.durableMandate === true) {
|
|
2462
|
-
if ((spec
|
|
2499
|
+
if (resolveCheckpointStore(spec, deps) !== undefined &&
|
|
2463
2500
|
(spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true) &&
|
|
2464
2501
|
markInheritedUnavailable(creq.toolCallId)) {
|
|
2465
2502
|
return first;
|
|
@@ -2515,7 +2552,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2515
2552
|
if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME)
|
|
2516
2553
|
return decision;
|
|
2517
2554
|
if (pc.durableMandate === true) {
|
|
2518
|
-
if ((spec
|
|
2555
|
+
if (resolveCheckpointStore(spec, deps) !== undefined &&
|
|
2519
2556
|
(spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true) &&
|
|
2520
2557
|
markInheritedUnavailable(creq.toolCallId)) {
|
|
2521
2558
|
return decision;
|
|
@@ -2675,13 +2712,13 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2675
2712
|
const blockedTracked = Boolean(hooks?.postToolUse || hooks?.preToolUse || hooks?.postToolUseFailure || hooks?.postToolBatch);
|
|
2676
2713
|
const restoreSurfaceGap = ownedEnv !== undefined && isRemoteExecutionEnv(ownedEnv) && ownedEnv.capabilities.suspendable ? missingRestoreSurface(ownedEnv) : [];
|
|
2677
2714
|
const incompleteSuspendAdapter = restoreSurfaceGap.length > 0 ? restoreSurfaceGap : undefined;
|
|
2678
|
-
const durableSuspendInfraReady = (spec
|
|
2715
|
+
const durableSuspendInfraReady = resolveCheckpointStore(spec, deps) !== undefined &&
|
|
2679
2716
|
!(offloadStore !== undefined && isVolatileOffloadStore(offloadStore)) &&
|
|
2680
2717
|
(ownedEnv === undefined || isRemoteExecutionEnv(ownedEnv)) &&
|
|
2681
2718
|
incompleteSuspendAdapter === undefined;
|
|
2682
2719
|
const resourceSuspendEligible = spec.resourceSuspend !== undefined && durableSuspendInfraReady;
|
|
2683
2720
|
if (spec.resourceSuspend !== undefined && !resourceSuspendEligible) {
|
|
2684
|
-
const why = (spec
|
|
2721
|
+
const why = resolveCheckpointStore(spec, deps) === undefined
|
|
2685
2722
|
? "no CheckpointStore is wired"
|
|
2686
2723
|
: offloadStore !== undefined && isVolatileOffloadStore(offloadStore)
|
|
2687
2724
|
? "tool-result offload uses the in-memory store (a resume needs durable results)"
|
|
@@ -2774,7 +2811,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2774
2811
|
}
|
|
2775
2812
|
if (decision.decisionReason === undefined || decision.decisionReason === "rule") {
|
|
2776
2813
|
const grant = inheritedAskGrants.get(req.toolCallId);
|
|
2777
|
-
if (grant !== undefined && grant.approver === onAsk && grant.argsJson === askGrantShapeOf(req.args)) {
|
|
2814
|
+
if (grant !== undefined && askApproverIdentity(grant.approver) === askApproverIdentity(onAsk) && grant.argsJson === askGrantShapeOf(req.args)) {
|
|
2778
2815
|
inheritedAskGrants.delete(req.toolCallId);
|
|
2779
2816
|
humanReviewRef.count += 1;
|
|
2780
2817
|
humanReviewRef.totalWaitMs += grant.waitMs;
|
|
@@ -2839,7 +2876,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2839
2876
|
catch {
|
|
2840
2877
|
}
|
|
2841
2878
|
};
|
|
2842
|
-
const checkpointStore = spec
|
|
2879
|
+
const checkpointStore = resolveCheckpointStore(spec, deps);
|
|
2843
2880
|
const durableApproval = spec.durableApproval ??
|
|
2844
2881
|
(runtimeCaps?.forceDurableGate ? { scope: spec.principal || DEFAULT_IRREVERSIBLE_SCOPE } : undefined);
|
|
2845
2882
|
const inFlightSpendMicroUsd = () => {
|
|
@@ -3599,7 +3636,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3599
3636
|
: undefined;
|
|
3600
3637
|
overheadState.promptChars = systemPrompt.length;
|
|
3601
3638
|
const preparedHolder = {};
|
|
3602
|
-
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), 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, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, 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 } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
3639
|
+
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), 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, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
3603
3640
|
const prepared = buildPrepared();
|
|
3604
3641
|
preparedHolder.current = prepared;
|
|
3605
3642
|
return prepared;
|