@sema-agent/core 5.34.0 → 5.36.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 +104 -0
- package/dist/agents/subagent.js +29 -2
- package/dist/core/auto-compaction.d.ts +23 -0
- package/dist/core/auto-compaction.js +8 -0
- package/dist/core/checkpoint-store.d.ts +49 -4
- package/dist/core/context-guard.d.ts +41 -0
- package/dist/core/context-guard.js +76 -0
- package/dist/core/hooks.d.ts +98 -3
- package/dist/core/hooks.js +146 -8
- package/dist/core/memory-engine/engine.js +1 -1
- package/dist/core/park-selfcheck.d.ts +161 -0
- package/dist/core/park-selfcheck.js +251 -0
- package/dist/core/runner/assemble-result.d.ts +3 -0
- package/dist/core/runner/assemble-result.js +3 -0
- package/dist/core/runner/git-status-frame.d.ts +219 -0
- package/dist/core/runner/git-status-frame.js +212 -0
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +6 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +2 -1
- package/dist/core/runner/prepare-task.d.ts +28 -4
- package/dist/core/runner/prepare-task.js +86 -52
- package/dist/core/runner/runtask.d.ts +6 -1
- package/dist/core/runner/runtask.js +330 -19
- package/dist/core/task-registry-agent.d.ts +15 -0
- package/dist/core/task-registry-agent.js +9 -0
- package/dist/core/task-registry.d.ts +3 -0
- package/dist/core/task-registry.js +4 -1
- package/dist/core/tool-errors.d.ts +2 -2
- package/dist/core/tool-policy.d.ts +125 -0
- package/dist/core/tool-policy.js +35 -2
- package/dist/core/types.d.ts +98 -9
- package/dist/engine/harness/types.d.ts +65 -1
- package/dist/engine/harness/types.js +20 -0
- package/dist/engine/session/import-validate.js +10 -1
- package/dist/engine/session/session.d.ts +37 -1
- package/dist/engine/session/session.js +56 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/internal/harness-types.d.ts +1 -0
- package/dist/internal/harness.d.ts +2 -0
- package/dist/internal/harness.js +2 -0
- package/dist/orchestration/workflow.d.ts +1 -1
- package/dist/prompt-assembly/epoch.js +1 -1
- package/dist/prompt-assembly/event-registry.js +1 -0
- package/dist/prompts/default.d.ts +20 -7
- package/dist/prompts/default.js +2 -7
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +17 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { GIT_SNAPSHOT_CC_PREAMBLE, buildGitSnapshot } from "../../prompts/default.js";
|
|
3
|
+
import { inlineUntrusted } from "../untrusted-text.js";
|
|
4
|
+
import { engineRegionCovers, protectGitFrame } from "../context-guard.js";
|
|
5
|
+
import { gitFrameContextVisible } from "../../internal/harness.js";
|
|
6
|
+
export const GIT_STATUS_FRAME_FORMAT_VERSION = 1;
|
|
7
|
+
export const GIT_STATUS_FRAME_PREAMBLE = "This is the git status observed while preparing this request. When the visible snapshot changes it will be re-sent in a later message — the most recent git status frame supersedes earlier ones.";
|
|
8
|
+
export const GIT_STATUS_UNAVAILABLE_BODY = "Git status is currently unavailable; the most recent git status frame above may be stale.";
|
|
9
|
+
export const GIT_STATUS_NON_REPO_BODY = "The working directory is no longer a git repository; earlier git status frames no longer apply.";
|
|
10
|
+
export const GIT_STATUS_ECHO_PREVIEW = {
|
|
11
|
+
full: "git status updated",
|
|
12
|
+
degraded: "git status updated (degraded: branch and dirtiness only)",
|
|
13
|
+
unavailable: "git status unavailable",
|
|
14
|
+
"non-repo": "git status: no longer a git repository",
|
|
15
|
+
};
|
|
16
|
+
function toWellFormedText(s) {
|
|
17
|
+
const native = s.toWellFormed;
|
|
18
|
+
if (typeof native === "function")
|
|
19
|
+
return native.call(s);
|
|
20
|
+
return s.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "�");
|
|
21
|
+
}
|
|
22
|
+
export function renderGitStatusFrameBody(outcome) {
|
|
23
|
+
switch (outcome.kind) {
|
|
24
|
+
case "full": {
|
|
25
|
+
const body = outcome.snapshot.startsWith(GIT_SNAPSHOT_CC_PREAMBLE)
|
|
26
|
+
? GIT_STATUS_FRAME_PREAMBLE + outcome.snapshot.slice(GIT_SNAPSHOT_CC_PREAMBLE.length)
|
|
27
|
+
: `${GIT_STATUS_FRAME_PREAMBLE}\n\n${outcome.snapshot}`;
|
|
28
|
+
return toWellFormedText(body);
|
|
29
|
+
}
|
|
30
|
+
case "degraded": {
|
|
31
|
+
const lines = [GIT_STATUS_FRAME_PREAMBLE];
|
|
32
|
+
lines.push(`Current branch: ${inlineUntrusted(outcome.branch ?? "HEAD")}`);
|
|
33
|
+
if (outcome.dirty !== undefined) {
|
|
34
|
+
lines.push(`Git working tree: ${outcome.dirty ? "has uncommitted changes" : "clean"}`);
|
|
35
|
+
}
|
|
36
|
+
return toWellFormedText(lines.join("\n\n"));
|
|
37
|
+
}
|
|
38
|
+
case "unavailable":
|
|
39
|
+
return GIT_STATUS_UNAVAILABLE_BODY;
|
|
40
|
+
case "non-repo":
|
|
41
|
+
return GIT_STATUS_NON_REPO_BODY;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function hashGitStatusFrame(kind, body, canonicalRoot) {
|
|
45
|
+
return `sha256:${createHash("sha256")
|
|
46
|
+
.update(`${GIT_STATUS_FRAME_FORMAT_VERSION}\u0000${canonicalRoot}\u0000${kind}\u0000${body}`)
|
|
47
|
+
.digest("hex")}`;
|
|
48
|
+
}
|
|
49
|
+
export function resolveGitStatusFrame(outcome, canonicalRoot, degradedMaterial) {
|
|
50
|
+
const body = renderGitStatusFrameBody(outcome);
|
|
51
|
+
const resolved = {
|
|
52
|
+
kind: outcome.kind,
|
|
53
|
+
body,
|
|
54
|
+
hash: hashGitStatusFrame(outcome.kind, body, canonicalRoot),
|
|
55
|
+
};
|
|
56
|
+
if (outcome.kind === "full") {
|
|
57
|
+
const shrunkBody = renderGitStatusFrameBody({ kind: "degraded", ...(degradedMaterial ?? {}) });
|
|
58
|
+
resolved.shrunk = { body: shrunkBody, hash: hashGitStatusFrame("degraded", shrunkBody, canonicalRoot) };
|
|
59
|
+
}
|
|
60
|
+
return resolved;
|
|
61
|
+
}
|
|
62
|
+
export async function probeGitStatusLane(args) {
|
|
63
|
+
const { executionEnv, envFacts, handsEnabled, taskRoot, onDegrade } = args;
|
|
64
|
+
const ref = {};
|
|
65
|
+
if (!handsEnabled)
|
|
66
|
+
return ref;
|
|
67
|
+
let outcome;
|
|
68
|
+
if (envFacts.isGitRepo === true) {
|
|
69
|
+
const SEP = "@@SEMA_ENV_GIT_SPLIT@@";
|
|
70
|
+
outcome = {
|
|
71
|
+
kind: "degraded",
|
|
72
|
+
...(envFacts.gitBranch !== undefined ? { branch: envFacts.gitBranch } : {}),
|
|
73
|
+
...(envFacts.gitDirty !== undefined ? { dirty: envFacts.gitDirty } : {}),
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
const snap = await executionEnv.exec(`(m=$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null); m=\${m#origin/}; for s in "$m" main master; do if [ -n "$s" ] && git show-ref --verify --quiet "refs/remotes/origin/$s"; then echo "$s"; break; fi; done) || true; echo "${SEP}"; (git config user.name 2>/dev/null || true); echo "${SEP}"; gs=$(git --no-optional-locks status --short) || exit 41; printf '%s\\n' "$gs"; echo "${SEP}"; gl=$(git --no-optional-locks log --oneline -n 5) || exit 42; printf '%s\\n' "$gl"`, { cwd: envFacts.cwd, timeout: 10 });
|
|
77
|
+
if (snap.ok && snap.value.exitCode === 0) {
|
|
78
|
+
const parts = snap.value.stdout.split(`${SEP}\n`);
|
|
79
|
+
if (parts.length === 4) {
|
|
80
|
+
outcome = {
|
|
81
|
+
kind: "full",
|
|
82
|
+
snapshot: buildGitSnapshot({
|
|
83
|
+
branch: envFacts.gitBranch ?? "HEAD",
|
|
84
|
+
mainBranch: parts[0].trim() || "main",
|
|
85
|
+
...(parts[1].trim() ? { userName: parts[1].trim() } : {}),
|
|
86
|
+
status: parts[2],
|
|
87
|
+
log: parts[3],
|
|
88
|
+
}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
onDegrade(`sentinel mis-split (${parts.length} sections, expected 4)`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
onDegrade(snap.ok
|
|
97
|
+
? snap.value.exitCode === 41
|
|
98
|
+
? "git status failed (exit 41)"
|
|
99
|
+
: snap.value.exitCode === 42
|
|
100
|
+
? "git log failed (exit 42)"
|
|
101
|
+
: `git exited ${snap.value.exitCode}`
|
|
102
|
+
: `exec failed: ${snap.error.message}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
onDegrade(err instanceof Error ? err.message : String(err));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else if (envFacts.isGitRepo === false) {
|
|
110
|
+
outcome = { kind: "non-repo" };
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
outcome = { kind: "unavailable" };
|
|
114
|
+
}
|
|
115
|
+
const rootRaw = envFacts.gitWorktreeRoot ?? envFacts.cwd ?? taskRoot;
|
|
116
|
+
let canonicalRoot = rootRaw;
|
|
117
|
+
try {
|
|
118
|
+
const c = await executionEnv.canonicalPath(rootRaw);
|
|
119
|
+
if (c.ok)
|
|
120
|
+
canonicalRoot = c.value;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
}
|
|
124
|
+
ref.canonicalRoot = canonicalRoot;
|
|
125
|
+
ref.frame = resolveGitStatusFrame(outcome, canonicalRoot, {
|
|
126
|
+
...(envFacts.gitBranch !== undefined ? { branch: envFacts.gitBranch } : {}),
|
|
127
|
+
...(envFacts.gitDirty !== undefined ? { dirty: envFacts.gitDirty } : {}),
|
|
128
|
+
});
|
|
129
|
+
return ref;
|
|
130
|
+
}
|
|
131
|
+
export function applyGitFrameGuard(args) {
|
|
132
|
+
const { before, trimmed, budgetTokens, ref, charsPerToken, onDegrade } = args;
|
|
133
|
+
if (ref.protectedText === undefined)
|
|
134
|
+
return trimmed;
|
|
135
|
+
const guarded = protectGitFrame(before, trimmed, budgetTokens, {
|
|
136
|
+
protectedText: ref.protectedText,
|
|
137
|
+
...(ref.frame?.kind === "full" && ref.frame.shrunk !== undefined && ref.wrappedShrink !== undefined ? { substitute: ref.wrappedShrink } : {}),
|
|
138
|
+
}, charsPerToken);
|
|
139
|
+
if (guarded.action === "over_budget") {
|
|
140
|
+
ref.terminalCode = "irreducible_core_over_budget";
|
|
141
|
+
throw Object.assign(new Error("irreducible core over budget: the compaction summary plus the git status frame alone exceed the request budget — no trim can produce an honest request"), { code: "irreducible_core_over_budget" });
|
|
142
|
+
}
|
|
143
|
+
if (guarded.action === "shrunk" && !ref.overBudgetShrunk) {
|
|
144
|
+
ref.overBudgetShrunk = true;
|
|
145
|
+
if (ref.announced !== undefined)
|
|
146
|
+
ref.announced = { ...ref.announced, pending: true };
|
|
147
|
+
onDegrade("git status frame degraded under budget pressure: the full snapshot no longer fits the request budget — requests carry the branch+dirty residual; the degraded view will be re-announced");
|
|
148
|
+
}
|
|
149
|
+
return guarded.messages;
|
|
150
|
+
}
|
|
151
|
+
export function newestEngineGitFrame(branch) {
|
|
152
|
+
const positiveHead = `<system-reminder>\n${GIT_STATUS_FRAME_PREAMBLE}`;
|
|
153
|
+
const tombUnits = [`<system-reminder>\n${GIT_STATUS_UNAVAILABLE_BODY}\n</system-reminder>`, `<system-reminder>\n${GIT_STATUS_NON_REPO_BODY}\n</system-reminder>`];
|
|
154
|
+
const topLevel = (text, at) => {
|
|
155
|
+
if (at === 0)
|
|
156
|
+
return true;
|
|
157
|
+
const before = text.slice(0, at).replace(/\n+$/, "");
|
|
158
|
+
return before === "" || before.endsWith("</system-reminder>");
|
|
159
|
+
};
|
|
160
|
+
const allTopLevel = (text, needle, m) => {
|
|
161
|
+
const out = [];
|
|
162
|
+
for (let at = text.indexOf(needle); at !== -1; at = text.indexOf(needle, at + 1)) {
|
|
163
|
+
if (topLevel(text, at) && engineRegionCovers(m, at, needle.length))
|
|
164
|
+
out.push(at);
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
167
|
+
};
|
|
168
|
+
for (let i = branch.length - 1; i >= 0; i--) {
|
|
169
|
+
const entry = branch[i];
|
|
170
|
+
if (entry === undefined || entry.type !== "message")
|
|
171
|
+
continue;
|
|
172
|
+
const m = entry.message;
|
|
173
|
+
if (m.role !== "user")
|
|
174
|
+
continue;
|
|
175
|
+
const c = m.content;
|
|
176
|
+
const text = typeof c === "string" ? c : Array.isArray(c) && c.length >= 1 && c[0].type === "text" ? (c[0].text ?? "") : undefined;
|
|
177
|
+
if (text === undefined)
|
|
178
|
+
continue;
|
|
179
|
+
let best;
|
|
180
|
+
for (const at of allTopLevel(text, positiveHead, m)) {
|
|
181
|
+
if (best === undefined || at > best.at)
|
|
182
|
+
best = { at, positive: true };
|
|
183
|
+
}
|
|
184
|
+
for (const unit of tombUnits) {
|
|
185
|
+
for (const at of allTopLevel(text, unit, m)) {
|
|
186
|
+
if (best === undefined || at > best.at)
|
|
187
|
+
best = { at, positive: false };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (best !== undefined)
|
|
191
|
+
return { entryId: entry.id, positive: best.positive };
|
|
192
|
+
}
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
export function stripGitStatusUnits(text) {
|
|
196
|
+
const head = `<system-reminder>\n${GIT_STATUS_FRAME_PREAMBLE}`;
|
|
197
|
+
const close = "</system-reminder>";
|
|
198
|
+
let out = text;
|
|
199
|
+
for (let at = out.indexOf(head); at !== -1; at = out.indexOf(head)) {
|
|
200
|
+
const end = out.indexOf(close, at);
|
|
201
|
+
if (end === -1)
|
|
202
|
+
break;
|
|
203
|
+
if (out.slice(at + head.length, end).includes("<system-reminder>"))
|
|
204
|
+
break;
|
|
205
|
+
out = out.slice(0, at) + out.slice(end + close.length);
|
|
206
|
+
}
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
export function branchCarriesVisiblePositiveGitFrame(branch) {
|
|
210
|
+
const newest = newestEngineGitFrame(branch);
|
|
211
|
+
return newest !== undefined && newest.positive && gitFrameContextVisible(branch, newest.entryId);
|
|
212
|
+
}
|
|
@@ -40,6 +40,12 @@ export interface PrepareAcquireReconcileInput {
|
|
|
40
40
|
* {@link import("./prepare-safety-scan.js").PrepareSafetyScanResult}); reconcile classifies an
|
|
41
41
|
* orphaned call's retry-safety by it. */
|
|
42
42
|
toolEffects: Map<string, ToolEffect>;
|
|
43
|
+
/** borrowed-readonly — design/252 G-6 sibling: the durable-park TOPOLOGY gap sentence for this
|
|
44
|
+
* deployment (`durableParkGapFor`), or `undefined` when the topology is whole / does not apply.
|
|
45
|
+
* Read ONLY on the fail-loud missing-session path below, where it turns a symptom into a named
|
|
46
|
+
* absent seat; nothing in the phase's control flow depends on it. Computed by the driver (which
|
|
47
|
+
* holds `deps`) rather than here, keeping this slice's input the narrow Pick its contract says. */
|
|
48
|
+
durableParkGap?: string;
|
|
43
49
|
}
|
|
44
50
|
/** The phase's outputs (design/238 相 API 规则件 four-class form). All five are fresh bindings —
|
|
45
51
|
* the driver destructures them into consts, so a consumer moved ahead of this call is a lexical
|
|
@@ -39,7 +39,8 @@ export async function prepareAcquireReconcile(input) {
|
|
|
39
39
|
}
|
|
40
40
|
catch (err) {
|
|
41
41
|
if (spec.requireExistingSession && err?.code === "not_found") {
|
|
42
|
-
const e = new Error(`requireExistingSession: session "${spec.sessionId}" does not exist — refusing a silent fresh run (design/114 Phase3)`
|
|
42
|
+
const e = new Error(`requireExistingSession: session "${spec.sessionId}" does not exist — refusing a silent fresh run (design/114 Phase3)` +
|
|
43
|
+
(input.durableParkGap !== undefined ? `. Note the assembly: ${input.durableParkGap}` : ""));
|
|
43
44
|
e.code = "resume.session_not_found";
|
|
44
45
|
throw e;
|
|
45
46
|
}
|
|
@@ -17,6 +17,7 @@ import { CacheBreakDetector, type ToolFingerprintInput } from "../cache-break-de
|
|
|
17
17
|
import { type BrainCallGuardrailRef } from "../../brain/timeout.js";
|
|
18
18
|
import { type OutputRef, type BlockedRef, type SkillListingEntry } from "./synthetic-tools.js";
|
|
19
19
|
import type { MemoryEngine } from "../memory-engine/engine.js";
|
|
20
|
+
import { type GitStatusLaneRef } from "./git-status-frame.js";
|
|
20
21
|
import { type ToolManifestRow } from "../../prompt-assembly/tool-catalog.js";
|
|
21
22
|
import type { ToolDisclosureManifest } from "../trace.js";
|
|
22
23
|
import type { TaskNotificationPayload } from "../task-notification.js";
|
|
@@ -169,9 +170,14 @@ export interface Prepared {
|
|
|
169
170
|
* emits; the tool_result-side delete in prepare-task never fires for immediate results). */
|
|
170
171
|
blockedToolCalls: Set<string>;
|
|
171
172
|
/**
|
|
172
|
-
*
|
|
173
|
-
* tool gate, at the one exit where an ask resolved,
|
|
174
|
-
* is minted (the reader deletes on read; a call the
|
|
173
|
+
* WHAT ended the approval a gated call was waiting on and — design/252 G-7 — WHOSE settlement it
|
|
174
|
+
* was, keyed by tool-call id: written ONLY by the tool gate, at the one exit where an ask resolved,
|
|
175
|
+
* and read once when that call's `tool_end` frame is minted (the reader deletes on read; a call the
|
|
176
|
+
* gate never settled has no entry, and an entry never names neither fact).
|
|
177
|
+
*
|
|
178
|
+
* ONE record rather than two parallel maps because they are one observation: an attribution without
|
|
179
|
+
* the settlement kind beside it is unreadable ("alice" — approved? her window elapsed?), and two maps
|
|
180
|
+
* keyed alike are two chances to drain one and leak the other.
|
|
175
181
|
*
|
|
176
182
|
* It is a sideband and not a field on the tool RESULT because a result is not a trustworthy carrier
|
|
177
183
|
* for this: `details` is arbitrary tool-authored data that post-tool hooks may also replace, so a
|
|
@@ -180,7 +186,10 @@ export interface Prepared {
|
|
|
180
186
|
* adjudicating layer can write. Same reason the entries are keyed by CALL id: the gate adjudicated
|
|
181
187
|
* that exact call, and the frame that reads it is that call's own.
|
|
182
188
|
*/
|
|
183
|
-
|
|
189
|
+
approvalSettlement: Map<string, {
|
|
190
|
+
settledBy?: import("../tool-policy.js").ApprovalSettledBy;
|
|
191
|
+
approver?: string;
|
|
192
|
+
}>;
|
|
184
193
|
/** Summed usage of nested sub-runs (sub-agents) spawned by this task's tools. */
|
|
185
194
|
nestedStats: NestedUsageAccum;
|
|
186
195
|
/** RB-430-a: prepare-time rewind disclosures (conversation-only branch / no snapshot backend / no file
|
|
@@ -739,6 +748,12 @@ export interface Prepared {
|
|
|
739
748
|
skills?: readonly string[];
|
|
740
749
|
models?: readonly string[];
|
|
741
750
|
};
|
|
751
|
+
/** env-tail migration (#254 shape) — the git-status frame lane's run-local state: this leg's
|
|
752
|
+
* resolved frame (probe outcome rendered + hashed at prepare), the announced `(kind, hash)`
|
|
753
|
+
* mirror the checkpoint serializer reads, the trim-protection slot the request-build context
|
|
754
|
+
* handler matches on, and the re-assert closure the compaction landing + boundary retry call.
|
|
755
|
+
* Always present (empty object on a hands-less leg — the lane is then out of scope). */
|
|
756
|
+
gitStatusRef: GitStatusLaneRef;
|
|
742
757
|
/** G1 通告层 — narrow post-compact getter over the process task registry: THIS run's visible
|
|
743
758
|
* pending/running background tasks (same owner/scope/session identity the TaskOutput/TaskStop tools
|
|
744
759
|
* use), as a bounded display projection (id/description/status — never handles/env/abort). Called by
|
|
@@ -1236,6 +1251,15 @@ export interface RunInternals {
|
|
|
1236
1251
|
* stamping. TRUSTED run-scoped channel (never a {@link TaskSpec} field).
|
|
1237
1252
|
*/
|
|
1238
1253
|
delegationTaskType?: import("../types.js").DelegationTaskType;
|
|
1254
|
+
/**
|
|
1255
|
+
* #258 — the registry row's stop-cycle generation this run executes as (fresh spawn = 1, a
|
|
1256
|
+
* revival's bumped counter), threaded by the BACKGROUND delegation lanes from the registry's own
|
|
1257
|
+
* `cycleSeq` so every `task_progress` tick the run mints carries it as `seq` (same axis as
|
|
1258
|
+
* `TaskNotificationPayload.seq` / `BackgroundChildEvent.seq`). Absent for runs with no `a*` row
|
|
1259
|
+
* (sync children, workflow agents, top-level) — same absence-is-a-fact posture as
|
|
1260
|
+
* {@link delegationTaskType} above. TRUSTED run-scoped channel (never a TaskSpec field).
|
|
1261
|
+
*/
|
|
1262
|
+
cycleSeq?: number;
|
|
1239
1263
|
/** δ 批 [1498]⑦/A-3 — the ROOT host session of the whole delegation tree (fixed point: the
|
|
1240
1264
|
* spawner passes its own `ctx.rootSessionId ?? ctx.sessionId`, so depth 1 gets the host session
|
|
1241
1265
|
* and every deeper level inherits it verbatim). `parentSessionId` is the IMMEDIATE spawner —
|
|
@@ -20,7 +20,7 @@ import { createSubagentWorktreeHelper, forkGovernanceDenial } from "../../agents
|
|
|
20
20
|
import { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME } from "../../agents/agent-transcript-tool.js";
|
|
21
21
|
import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
22
22
|
import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
23
|
-
import { askApproverIdentity, checkToolPolicyProjection, combinePolicies, constraintChainDigest, constraintChainEntryOf, isApprovalSettledBy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
|
|
23
|
+
import { askApproverIdentity, checkToolPolicyProjection, combinePolicies, constraintChainDigest, constraintChainEntryOf, isApprovalSettledBy, screenApproverAttribution, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
|
|
24
24
|
const PERSISTED_RULE_TOOL = "Bash";
|
|
25
25
|
import { findAdmittingRule, suggestRulesForCommand } from "../permission-rule-model.js";
|
|
26
26
|
import { ActiveSkillScope, createActiveSkillScopePolicy } from "./active-skill-scope.js";
|
|
@@ -56,7 +56,8 @@ import { limitConfigError, prepareConfigDoors } from "./prepare-config-doors.js"
|
|
|
56
56
|
import { NAMESPACED_NAME_SHAPES, prepareSafetyScan } from "./prepare-safety-scan.js";
|
|
57
57
|
import { prepareAcquireReconcile } from "./prepare-acquire-reconcile.js";
|
|
58
58
|
import { prepareWorkspaceRestore, rebaseWorkspacePath, remoteEnvFailureNote, restoreWorkspaceWithRetry } from "./prepare-workspace-restore.js";
|
|
59
|
-
import { defaultPromptProvider, buildEnvironmentContext,
|
|
59
|
+
import { defaultPromptProvider, buildEnvironmentContext, formatLocalDate, isValidTimeZone, PROJECT_CONTEXT_FRAMING } from "../../prompts/default.js";
|
|
60
|
+
import { applyGitFrameGuard, probeGitStatusLane } from "./git-status-frame.js";
|
|
60
61
|
import { assemblePrompt } from "../../prompt-assembly/assemble.js";
|
|
61
62
|
import { auditToolCollisions, getToolContract, projectToolManifest } from "../../prompt-assembly/tool-catalog.js";
|
|
62
63
|
import { resolveEpochAgainstBundled } from "../../prompt-assembly/epoch.js";
|
|
@@ -88,6 +89,7 @@ import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
|
|
|
88
89
|
import { BINDING_CHECKPOINT_VERSION, mintCheckpointToken, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, FACE_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, buildRiskDescriptor, debitLedger, encodeAtFidelity, remainingBudgetMicroUsd, resolveCheckpointStore, resolveDeclaredFidelity, samePlainValue, } from "../checkpoint-store.js";
|
|
89
90
|
import { boundInputHashOf } from "../canonical-json.js";
|
|
90
91
|
import { countElicitOptIns, deriveWiringManifest, resolveAskSeamForm, resolveDeclaredDurability, resolveElicitSeam, resolveQuestionSeam } from "../wiring-manifest.js";
|
|
92
|
+
import { durableParkGapFor } from "../park-selfcheck.js";
|
|
91
93
|
import { GLOBAL_USAGE_KEY, usageRetryAfterMs } from "../usage-window-store.js";
|
|
92
94
|
import { deliverEngineNotice } from "../types.js";
|
|
93
95
|
const announcedMaterializeEnv = new Set();
|
|
@@ -192,6 +194,46 @@ async function forgetQuietly(sessions, sessionId) {
|
|
|
192
194
|
catch {
|
|
193
195
|
}
|
|
194
196
|
}
|
|
197
|
+
function screenGateSettlement(result, settling) {
|
|
198
|
+
const defects = [];
|
|
199
|
+
const reportedBy = result.settledBy;
|
|
200
|
+
let settledBy;
|
|
201
|
+
if (reportedBy !== undefined) {
|
|
202
|
+
if (!isApprovalSettledBy(reportedBy) || (!settling && reportedBy !== "human")) {
|
|
203
|
+
defects.push(`a tool-gate settlement reported settledBy "${String(reportedBy)}" on ${settling ? "a blocked" : "an executing"} call — ` +
|
|
204
|
+
`it is one of "human" / "timeout" / "aborted", and only "human" can be the source of a call that runs; the frame carries no source`);
|
|
205
|
+
}
|
|
206
|
+
else
|
|
207
|
+
settledBy = reportedBy;
|
|
208
|
+
}
|
|
209
|
+
const attribution = screenApproverAttribution(result.approver);
|
|
210
|
+
if (attribution.defect !== undefined) {
|
|
211
|
+
defects.push(`a tool-gate settlement reported an attribution this engine refuses: ${attribution.defect}; the frame carries no approver`);
|
|
212
|
+
}
|
|
213
|
+
const record = {
|
|
214
|
+
...(settledBy !== undefined ? { settledBy } : {}),
|
|
215
|
+
...(attribution.approver !== undefined ? { approver: attribution.approver } : {}),
|
|
216
|
+
};
|
|
217
|
+
return { ...(settledBy !== undefined || attribution.approver !== undefined ? { record } : {}), defects };
|
|
218
|
+
}
|
|
219
|
+
function inheritedAskRuleEvidence(deps) {
|
|
220
|
+
const org = deps.permissionRuleOrg === undefined ? "not_wired" : "not_adjudicated";
|
|
221
|
+
const personal = deps.permissionRuleStore === undefined ? "not_wired" : "not_adjudicated";
|
|
222
|
+
return Object.freeze({ orgRevisionAbsent: org, orgRuleAbsent: org, personalRuleDotsAbsent: personal });
|
|
223
|
+
}
|
|
224
|
+
function orgRevisionEvidenceOf(resolution, onDefect) {
|
|
225
|
+
const reported = resolution.revision;
|
|
226
|
+
if (reported === undefined)
|
|
227
|
+
return {};
|
|
228
|
+
if (typeof reported === "number" && Number.isFinite(reported))
|
|
229
|
+
return { revision: reported };
|
|
230
|
+
onDefect(`the org rule overlay reported revision ${JSON.stringify(reported)} — a snapshot revision is a finite number; ` +
|
|
231
|
+
`the adjudication stands, but the ask carries no revision evidence for this call`);
|
|
232
|
+
return {};
|
|
233
|
+
}
|
|
234
|
+
function persistedRuleHitOf(admitting) {
|
|
235
|
+
return admitting === undefined ? undefined : { rule: admitting.rule, dots: admitting.adds.map((a) => ({ actor: a.dot.actor, counter: a.dot.counter })) };
|
|
236
|
+
}
|
|
195
237
|
export async function prepareTask(spec, deps, sessions, resume, internals, runnerSelf) {
|
|
196
238
|
const doors = prepareConfigDoors({ spec, deps, sessions, resume, internals });
|
|
197
239
|
spec = doors.spec;
|
|
@@ -204,7 +246,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
204
246
|
e.code = "resume.session_not_found";
|
|
205
247
|
throw e;
|
|
206
248
|
}
|
|
207
|
-
const { acquired, session, conflictRef, wakeRecovered, resumeAtBeforeParentId } = await prepareAcquireReconcile({ sessions, spec, resume, toolEffects });
|
|
249
|
+
const { acquired, session, conflictRef, wakeRecovered, resumeAtBeforeParentId } = await prepareAcquireReconcile({ sessions, spec, resume, toolEffects, ...(() => { const g = durableParkGapFor(deps, spec); return g !== undefined ? { durableParkGap: g } : {}; })() });
|
|
208
250
|
const sessionId = acquired.sessionId;
|
|
209
251
|
const hostTaskId = spec.taskId ?? sessionId;
|
|
210
252
|
if (compModel !== undefined) {
|
|
@@ -1834,38 +1876,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1834
1876
|
}
|
|
1835
1877
|
catch {
|
|
1836
1878
|
}
|
|
1837
|
-
if (envFacts.isGitRepo === true) {
|
|
1838
|
-
const SEP = "@@SEMA_ENV_GIT_SPLIT@@";
|
|
1839
|
-
try {
|
|
1840
|
-
const snap = await executionEnv.exec(`(m=$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null); m=\${m#origin/}; for s in "$m" main master; do if [ -n "$s" ] && git show-ref --verify --quiet "refs/remotes/origin/$s"; then echo "$s"; break; fi; done) || true; echo "${SEP}"; (git config user.name 2>/dev/null || true); echo "${SEP}"; gs=$(git --no-optional-locks status --short) || exit 41; printf '%s\\n' "$gs"; echo "${SEP}"; gl=$(git --no-optional-locks log --oneline -n 5) || exit 42; printf '%s\\n' "$gl"`, { cwd: envFacts.cwd, timeout: 10 });
|
|
1841
|
-
if (snap.ok && snap.value.exitCode === 0) {
|
|
1842
|
-
const parts = snap.value.stdout.split(`${SEP}\n`);
|
|
1843
|
-
if (parts.length === 4) {
|
|
1844
|
-
envFacts.gitSnapshot = buildGitSnapshot({
|
|
1845
|
-
branch: envFacts.gitBranch ?? "HEAD",
|
|
1846
|
-
mainBranch: parts[0].trim() || "main",
|
|
1847
|
-
...(parts[1].trim() ? { userName: parts[1].trim() } : {}),
|
|
1848
|
-
status: parts[2],
|
|
1849
|
-
log: parts[3],
|
|
1850
|
-
});
|
|
1851
|
-
}
|
|
1852
|
-
}
|
|
1853
|
-
else {
|
|
1854
|
-
const reason = snap.ok
|
|
1855
|
-
? snap.value.exitCode === 41
|
|
1856
|
-
? "git status failed (exit 41)"
|
|
1857
|
-
: snap.value.exitCode === 42
|
|
1858
|
-
? "git log failed (exit 42)"
|
|
1859
|
-
: `git exited ${snap.value.exitCode}`
|
|
1860
|
-
: `exec failed: ${snap.error.message}`;
|
|
1861
|
-
deps.onError?.(new Error(`env git snapshot skipped — ${reason}`), { phase: "degraded", sessionId, classification: "env-git-snapshot" });
|
|
1862
|
-
}
|
|
1863
|
-
}
|
|
1864
|
-
catch (err) {
|
|
1865
|
-
deps.onError?.(new Error(`env git snapshot skipped — ${err instanceof Error ? err.message : String(err)}`), { phase: "degraded", sessionId, classification: "env-git-snapshot" });
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
1879
|
}
|
|
1880
|
+
const gitStatusRef = await probeGitStatusLane({
|
|
1881
|
+
executionEnv,
|
|
1882
|
+
envFacts,
|
|
1883
|
+
handsEnabled,
|
|
1884
|
+
taskRoot: taskRootFinal,
|
|
1885
|
+
onDegrade: (reason) => deps.onError?.(new Error(`env git snapshot degraded — ${reason}`), { phase: "degraded", sessionId, classification: "env-git-snapshot" }),
|
|
1886
|
+
});
|
|
1869
1887
|
if (toolFaceSnapshot.exclude !== undefined && toolFaceSnapshot.exclude.length > 0) {
|
|
1870
1888
|
const excluded = new Set(toolFaceSnapshot.exclude);
|
|
1871
1889
|
for (let i = tools.length - 1; i >= 0; i--)
|
|
@@ -2778,6 +2796,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2778
2796
|
return {};
|
|
2779
2797
|
return { riskAxes: { ...(irreversible !== undefined ? { irreversible } : {}), ...(egress !== undefined ? { egress } : {}) } };
|
|
2780
2798
|
};
|
|
2799
|
+
const inheritedAskEvidence = inheritedAskRuleEvidence(deps);
|
|
2781
2800
|
const permissionRuleLane = (() => {
|
|
2782
2801
|
const provider = deps.permissionRuleStore;
|
|
2783
2802
|
const localOwnerDeclared = deps.localOwnerRules === true;
|
|
@@ -2817,9 +2836,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2817
2836
|
message: err instanceof Error ? err.message : String(err),
|
|
2818
2837
|
ts: Date.now(),
|
|
2819
2838
|
}));
|
|
2820
|
-
return
|
|
2839
|
+
return { unreadable: true };
|
|
2821
2840
|
}
|
|
2822
|
-
return findAdmittingRule(listed.rules, { tool: req.toolName, command, cwd: root })
|
|
2841
|
+
return persistedRuleHitOf(findAdmittingRule(listed.rules, { tool: req.toolName, command, cwd: root }));
|
|
2823
2842
|
},
|
|
2824
2843
|
};
|
|
2825
2844
|
})();
|
|
@@ -2840,11 +2859,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2840
2859
|
}
|
|
2841
2860
|
if (resolution.status === "unavailable")
|
|
2842
2861
|
return { status: "unavailable", disclosures: resolution.disclosures };
|
|
2862
|
+
const revisionCell = orgRevisionEvidenceOf(resolution, (message) => deps.onError?.(new Error(message), { phase: "config", sessionId }));
|
|
2843
2863
|
const command = req.args?.command;
|
|
2844
2864
|
if (req.toolName !== PERSISTED_RULE_TOOL || typeof command !== "string")
|
|
2845
|
-
return { status: "available" };
|
|
2865
|
+
return { status: "available", ...revisionCell };
|
|
2846
2866
|
const verdict = orgRuleVerdictFor(resolution.rules, { tool: req.toolName, command });
|
|
2847
|
-
return verdict === undefined ? { status: "available" } : { status: "available", verdict };
|
|
2867
|
+
return verdict === undefined ? { status: "available", ...revisionCell } : { status: "available", verdict, ...revisionCell };
|
|
2848
2868
|
},
|
|
2849
2869
|
};
|
|
2850
2870
|
})();
|
|
@@ -2926,6 +2946,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2926
2946
|
...riskAxesOf(creq.toolName),
|
|
2927
2947
|
...(re.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
2928
2948
|
...(re.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: re.persistedRuleShadowed } : {}),
|
|
2949
|
+
ruleEvidence: inheritedAskEvidence,
|
|
2929
2950
|
}, onAskOf, csignal ?? abortController.signal);
|
|
2930
2951
|
if (rr.action !== "allow")
|
|
2931
2952
|
return rr;
|
|
@@ -3011,6 +3032,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3011
3032
|
...riskAxesOf(creq.toolName),
|
|
3012
3033
|
...(first.action === "ask" && first.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
3013
3034
|
...(first.action === "ask" && first.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: first.persistedRuleShadowed } : {}),
|
|
3035
|
+
ruleEvidence: inheritedAskEvidence,
|
|
3014
3036
|
}, pc.onAsk, csignal ?? abortController.signal);
|
|
3015
3037
|
const askWaitMs = Math.max(0, now() - askT0);
|
|
3016
3038
|
if (resolved.action === "deny" && resolved.approverUnavailable === true) {
|
|
@@ -3098,6 +3120,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3098
3120
|
...riskAxesOf(creq.toolName),
|
|
3099
3121
|
...(decision.action === "ask" && decision.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
3100
3122
|
...(decision.action === "ask" && decision.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: decision.persistedRuleShadowed } : {}),
|
|
3123
|
+
ruleEvidence: inheritedAskEvidence,
|
|
3101
3124
|
}, pc.onAsk, csignal ?? abortController.signal);
|
|
3102
3125
|
const askWaitMs = Math.max(0, now() - askT0);
|
|
3103
3126
|
if (resolved.action === "deny" && resolved.approverUnavailable === true) {
|
|
@@ -3239,7 +3262,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3239
3262
|
}
|
|
3240
3263
|
const preToolContexts = new Map();
|
|
3241
3264
|
const blockedToolCalls = new Set();
|
|
3242
|
-
const
|
|
3265
|
+
const approvalSettlement = new Map();
|
|
3243
3266
|
const blockedTracked = Boolean(hooks?.postToolUse || hooks?.preToolUse || hooks?.postToolUseFailure || hooks?.postToolBatch);
|
|
3244
3267
|
const restoreSurfaceGap = ownedEnv !== undefined && isRemoteExecutionEnv(ownedEnv) && ownedEnv.capabilities.suspendable ? missingRestoreSurface(ownedEnv) : [];
|
|
3245
3268
|
const incompleteSuspendAdapter = restoreSurfaceGap.length > 0 ? restoreSurfaceGap : undefined;
|
|
@@ -3413,6 +3436,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3413
3436
|
...(decision.action === "ask" && decision.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: decision.persistedRuleShadowed } : {}),
|
|
3414
3437
|
...(decision.action === "ask" && decision.probeReason !== undefined ? { probeReason: decision.probeReason } : {}),
|
|
3415
3438
|
...(decision.action === "ask" && decision.probeCause !== undefined ? { probeCause: decision.probeCause } : {}),
|
|
3439
|
+
...(decision.action === "ask" && decision.ruleEvidence !== undefined ? { ruleEvidence: decision.ruleEvidence } : {}),
|
|
3416
3440
|
}, onAsk, abortController.signal);
|
|
3417
3441
|
const waitMs = Math.max(0, now() - t0);
|
|
3418
3442
|
if (resolved.approverUnavailable !== true) {
|
|
@@ -3530,6 +3554,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3530
3554
|
...(announcedListingsRef.models !== undefined ? { models: [...announcedListingsRef.models] } : {}),
|
|
3531
3555
|
}
|
|
3532
3556
|
: undefined,
|
|
3557
|
+
gitAnnouncement: gitStatusRef.announced !== undefined ? { ...gitStatusRef.announced } : undefined,
|
|
3533
3558
|
delegationProvenance: internals?.delegationProvenance !== undefined ? { ...internals.delegationProvenance.ref.current } : undefined,
|
|
3534
3559
|
});
|
|
3535
3560
|
const commitSuspendSaga = async (token, cp, remoteEnv, remoteHandle) => {
|
|
@@ -4230,17 +4255,11 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4230
4255
|
if (blockedTracked && (result.block || result.suspend)) {
|
|
4231
4256
|
blockedToolCalls.add(e.toolCallId);
|
|
4232
4257
|
}
|
|
4233
|
-
const
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
`it is one of "human" / "timeout" / "aborted", and only "human" can be the source of a call that runs; the frame carries no source`), { phase: "config", sessionId });
|
|
4239
|
-
}
|
|
4240
|
-
else {
|
|
4241
|
-
approvalSettledBy.set(e.toolCallId, reported);
|
|
4242
|
-
}
|
|
4243
|
-
}
|
|
4258
|
+
const settlement = screenGateSettlement(result, result.block === true);
|
|
4259
|
+
for (const defect of settlement.defects)
|
|
4260
|
+
deps.onError?.(new Error(defect), { phase: "config", sessionId });
|
|
4261
|
+
if (settlement.record !== undefined)
|
|
4262
|
+
approvalSettlement.set(e.toolCallId, settlement.record);
|
|
4244
4263
|
return result.block
|
|
4245
4264
|
? { block: true, reason: result.reason }
|
|
4246
4265
|
: result.updatedInput !== undefined
|
|
@@ -4347,6 +4366,20 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4347
4366
|
ts: Date.now(),
|
|
4348
4367
|
}));
|
|
4349
4368
|
}
|
|
4369
|
+
trimmed = applyGitFrameGuard({
|
|
4370
|
+
before: edited,
|
|
4371
|
+
trimmed,
|
|
4372
|
+
budgetTokens: guardAt,
|
|
4373
|
+
ref: gitStatusRef,
|
|
4374
|
+
charsPerToken,
|
|
4375
|
+
onDegrade: (message) => {
|
|
4376
|
+
try {
|
|
4377
|
+
deps.onError?.(new Error(message), { phase: "degraded", sessionId, classification: "env-git-snapshot" });
|
|
4378
|
+
}
|
|
4379
|
+
catch {
|
|
4380
|
+
}
|
|
4381
|
+
},
|
|
4382
|
+
});
|
|
4350
4383
|
const swept = dropOrphanToolResults(trimmed);
|
|
4351
4384
|
if (swept.dropped.length > 0) {
|
|
4352
4385
|
try {
|
|
@@ -4363,7 +4396,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4363
4396
|
mediaCapped !== capped ||
|
|
4364
4397
|
edited !== mediaCapped ||
|
|
4365
4398
|
trimDroppedMessages ||
|
|
4366
|
-
swept.dropped.length > 0
|
|
4399
|
+
swept.dropped.length > 0 ||
|
|
4400
|
+
gitStatusRef.overBudgetShrunk === true;
|
|
4367
4401
|
return { messages: swept.messages };
|
|
4368
4402
|
});
|
|
4369
4403
|
const cacheBreakDetector = deps.cacheBreakDetection === false ? undefined : new CacheBreakDetector();
|
|
@@ -4552,7 +4586,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4552
4586
|
const effectiveReadFaceObserved = carrierReadFace();
|
|
4553
4587
|
const effectiveReadDenyObserved = readDenyAdditionsNormalized.length > 0 ? readDenyAdditionsNormalized.map((e) => ({ ...e })) : undefined;
|
|
4554
4588
|
const preparedHolder = {};
|
|
4555
|
-
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath: taskRootFinal, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls,
|
|
4589
|
+
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath: taskRootFinal, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettlement, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, suspendRef, suspendProgressRef, 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, lastBrainContext, 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, gitStatusRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
4556
4590
|
const prepared = buildPrepared();
|
|
4557
4591
|
preparedHolder.current = prepared;
|
|
4558
4592
|
return prepared;
|
|
@@ -80,13 +80,18 @@ declare function toolEndBodyFrom(result: unknown, isError: boolean,
|
|
|
80
80
|
* a parameter and never derived from `result`: a tool's own `details` (which post-tool hooks may
|
|
81
81
|
* also replace) is writable by layers that adjudicate nothing, so reading provenance out of it would
|
|
82
82
|
* let a failing tool claim a person approved it. Omitted ⇒ this call settled no approval. */
|
|
83
|
-
settledBy?: ApprovalSettledBy
|
|
83
|
+
settledBy?: ApprovalSettledBy,
|
|
84
|
+
/** design/252 G-7 — WHOSE settlement, from the same caller and the same channel as `settledBy`, and
|
|
85
|
+
* for the same reason it is a parameter: an attribution read out of a tool's own result would let a
|
|
86
|
+
* tool name the person who approved it. Omitted ⇒ this call's settlement named nobody. */
|
|
87
|
+
approver?: string): {
|
|
84
88
|
output?: unknown;
|
|
85
89
|
truncated?: boolean;
|
|
86
90
|
totalChars?: number;
|
|
87
91
|
structured?: unknown;
|
|
88
92
|
errorCode?: string;
|
|
89
93
|
settledBy?: ApprovalSettledBy;
|
|
94
|
+
approver?: string;
|
|
90
95
|
};
|
|
91
96
|
/**
|
|
92
97
|
* scan-1/A1 — the BODY of the synthetic `tool_end` that closes a reconcile-recovered orphan. ONE
|