@sema-agent/core 5.45.0 → 5.47.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 +97 -0
- package/dist/agents/subagent.js +130 -3
- package/dist/core/checkpoint-store.d.ts +12 -0
- package/dist/core/governance-codes.d.ts +13 -0
- package/dist/core/governance-codes.js +33 -0
- package/dist/core/hooks.d.ts +9 -2
- package/dist/core/hooks.js +6 -5
- package/dist/core/memory-engine/content-origin.d.ts +3 -1
- package/dist/core/memory-engine/delegation-settlement.d.ts +318 -0
- package/dist/core/memory-engine/delegation-settlement.js +661 -0
- package/dist/core/memory-engine/engine.d.ts +209 -4
- package/dist/core/memory-engine/engine.js +885 -39
- package/dist/core/memory-engine/export-bundle.d.ts +10 -1
- package/dist/core/memory-engine/export-bundle.js +21 -0
- package/dist/core/memory-engine/file-backend.d.ts +34 -4
- package/dist/core/memory-engine/file-backend.js +168 -40
- package/dist/core/memory-engine/frontmatter.d.ts +69 -1
- package/dist/core/memory-engine/frontmatter.js +156 -2
- package/dist/core/memory-engine/header-hints.d.ts +17 -0
- package/dist/core/memory-engine/header-hints.js +6 -0
- package/dist/core/memory-engine/index.d.ts +7 -5
- package/dist/core/memory-engine/index.js +5 -3
- package/dist/core/memory-engine/layout.d.ts +39 -2
- package/dist/core/memory-engine/layout.js +27 -14
- package/dist/core/memory-engine/memory-backend-contract.js +108 -0
- package/dist/core/memory-engine/origin-clearance.d.ts +66 -0
- package/dist/core/memory-engine/origin-clearance.js +84 -0
- package/dist/core/memory-engine/provenance-wording.d.ts +50 -0
- package/dist/core/memory-engine/provenance-wording.js +15 -0
- package/dist/core/memory-engine/sync-client.d.ts +1 -1
- package/dist/core/memory-engine/sync-client.js +33 -1
- package/dist/core/memory-engine/tools.d.ts +64 -3
- package/dist/core/memory-engine/tools.js +37 -9
- package/dist/core/memory-engine/types.d.ts +145 -3
- package/dist/core/memory-engine/types.js +1 -1
- package/dist/core/reminder-mint.d.ts +70 -0
- package/dist/core/reminder-mint.js +25 -0
- package/dist/core/runner/git-status-frame.d.ts +3 -14
- package/dist/core/runner/git-status-frame.js +39 -14
- package/dist/core/runner/prepare-config-doors.d.ts +4 -0
- package/dist/core/runner/prepare-config-doors.js +15 -0
- package/dist/core/runner/prepare-hands-readface.d.ts +5 -11
- package/dist/core/runner/prepare-hands-readface.js +26 -0
- package/dist/core/runner/prepare-memory.d.ts +11 -0
- package/dist/core/runner/prepare-memory.js +61 -24
- package/dist/core/runner/prepare-task.d.ts +46 -1
- package/dist/core/runner/prepare-task.js +128 -23
- package/dist/core/runner/runtask.js +62 -55
- package/dist/core/session-reconcile.js +3 -2
- package/dist/core/side-query.d.ts +11 -1
- package/dist/core/side-query.js +3 -0
- package/dist/core/types.d.ts +85 -10
- package/dist/core/types.js +3 -0
- package/dist/engine/harness/types.d.ts +46 -1
- package/dist/engine/harness/types.js +11 -0
- package/dist/engine/session/import-validate.js +6 -1
- package/dist/engine/session/session.d.ts +20 -0
- package/dist/engine/session/session.js +26 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/orchestration/run-workflow-tool.d.ts +16 -0
- package/dist/orchestration/run-workflow-tool.js +23 -3
- package/dist/orchestration/workflow-governance.d.ts +8 -1
- package/dist/prompt-assembly/epoch.js +2 -0
- package/dist/prompt-assembly/types.d.ts +6 -0
- package/dist/prompts/default.d.ts +13 -1
- package/dist/prompts/default.js +5 -1
- package/dist/tools/fs/fs-bash.d.ts +4 -0
- package/dist/tools/fs/fs-bash.js +1 -1
- package/dist/tools/fs/fs-read.d.ts +1 -1
- package/dist/tools/fs/fs-read.js +8 -7
- package/dist/tools/fs/fs-shared.d.ts +10 -4
- package/dist/tools/fs/fs-shared.js +6 -3
- package/dist/tools/fs/gh-rate-limit.d.ts +4 -1
- package/dist/tools/fs/gh-rate-limit.js +3 -2
- package/dist/tools/fs/index.d.ts +10 -2
- package/dist/tools/fs/index.js +2 -1
- package/dist/tools/task-list.d.ts +5 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +21 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { persistedReadDenyEntryProblem } from "../../tools/fs/read-deny.js";
|
|
2
2
|
import { createSafeNotifier, observeThenableRejection } from "../safe-notify.js";
|
|
3
|
+
import { mintSystemReminder, openSystemReminder } from "../reminder-mint.js";
|
|
3
4
|
import { deliverDelegationLifecycle, deliverEngineNotice, undrainedUserInputNotices } from "../types.js";
|
|
4
5
|
import { AgentHarness, DEFAULT_COMPACTION_SETTINGS, uuidv7 } from "../../internal/harness.js";
|
|
5
6
|
import { snapshotActorAssertion } from "../../internal/llm.js";
|
|
@@ -122,12 +123,12 @@ const SNAPSHOT_TIMEOUT_MS = 30_000;
|
|
|
122
123
|
const SNAPSHOT_TOO_LARGE_TTL_MS = 30 * 60_000;
|
|
123
124
|
const LIMIT_APPROACH_DEFAULT_THRESHOLDS = [0.8, 0.95];
|
|
124
125
|
const DEFAULT_PRECALL_OUTPUT_TOKENS = 4096;
|
|
125
|
-
function limitApproachFrames(index, axis, percent) {
|
|
126
|
+
function limitApproachFrames(index, axis, percent, mark) {
|
|
126
127
|
return index === 0
|
|
127
|
-
?
|
|
128
|
+
? `${openSystemReminder(mark)}[limit approach] This task has used about ${percent}% of its ${axis}. ` +
|
|
128
129
|
`Start converging: finish the step you are on, drop work that is not required for the deliverable, and ` +
|
|
129
130
|
`avoid starting anything you cannot complete within what is left.</system-reminder>`
|
|
130
|
-
:
|
|
131
|
+
: `${openSystemReminder(mark)}[limit approach] About ${percent}% of the ${axis} is gone. Deliver now: ` +
|
|
131
132
|
`stop exploring, write up what you have — including what is incomplete and what you would do next — ` +
|
|
132
133
|
`and end the task. Work that is not reported before the allowance runs out is lost.</system-reminder>`;
|
|
133
134
|
}
|
|
@@ -244,16 +245,16 @@ function assertOutcomeText(value, field) {
|
|
|
244
245
|
throw new CheckpointError("checkpoint.invalid_outcome", `resume \`${field}\` is not a plain string (got ${typeof value}) — a decide's text payload is an operator's plain data, not a live object; refusing pre-CAS, the checkpoint stays pending`);
|
|
245
246
|
}
|
|
246
247
|
}
|
|
247
|
-
function resumeContinuation(resume) {
|
|
248
|
+
function resumeContinuation(resume, mark) {
|
|
248
249
|
if (resume.outcome.gate === "wake") {
|
|
249
250
|
return formatHookFeedback("You were WOKEN from a parked pause by an operator. Before continuing, re-orient from the workspace: " +
|
|
250
251
|
"run `git status` and review your recent changes / last commits to confirm what is already done, then " +
|
|
251
|
-
"continue the remaining work. Do NOT restart the task or re-run work that is already committed.");
|
|
252
|
+
"continue the remaining work. Do NOT restart the task or re-run work that is already committed.", mark);
|
|
252
253
|
}
|
|
253
254
|
if (resume.outcome.gate === "resource_limit") {
|
|
254
255
|
return formatHookFeedback("You were resumed after a pause. Before continuing, re-orient from the workspace: run `git status` and " +
|
|
255
256
|
"review your recent changes / last commits to confirm what is already done, then continue the " +
|
|
256
|
-
"remaining work. Do NOT restart the task or re-run work that is already committed.");
|
|
257
|
+
"remaining work. Do NOT restart the task or re-run work that is already committed.", mark);
|
|
257
258
|
}
|
|
258
259
|
if (resume.outcome.gate === "dry_run_review") {
|
|
259
260
|
const verdict = resume.outcome.decision === "approve"
|
|
@@ -261,31 +262,31 @@ function resumeContinuation(resume) {
|
|
|
261
262
|
: `Your predicted change was REVIEWED and REJECTED${resume.outcome.reason ? `: ${delimitUntrusted("reviewer note", resume.outcome.reason, REVIEWER_NOTE_MAX_BODY)}` : ""}; it was NOT applied`;
|
|
262
263
|
return formatHookFeedback(`You were resumed after a dry-run review. ${verdict}. Before continuing, re-orient from the workspace ` +
|
|
263
264
|
"(run `git status` and review your recent changes) to confirm the current state, then continue the " +
|
|
264
|
-
"remaining work. Do NOT restart the task or re-run work that is already done.");
|
|
265
|
+
"remaining work. Do NOT restart the task or re-run work that is already done.", mark);
|
|
265
266
|
}
|
|
266
267
|
if (resume.outcome.gate === "plan_review") {
|
|
267
268
|
if (resume.outcome.decision === "approve") {
|
|
268
269
|
return formatHookFeedback("Your proposed PLAN was REVIEWED and APPROVED. Proceed with that plan now — begin executing it. " +
|
|
269
|
-
"This is a RESUMED task; do NOT re-plan or restart from scratch, just carry out the approved plan.");
|
|
270
|
+
"This is a RESUMED task; do NOT re-plan or restart from scratch, just carry out the approved plan.", mark);
|
|
270
271
|
}
|
|
271
272
|
if (resume.outcome.decision === "edit") {
|
|
272
273
|
if (!resume.outcome.editedPlan) {
|
|
273
274
|
return formatHookFeedback("Your proposed PLAN was REVIEWED and EDITED, but no revised plan text was supplied — proceed with your " +
|
|
274
|
-
"ORIGINAL plan as-is, begin executing it now. This is a RESUMED task; do NOT re-plan or restart from scratch.");
|
|
275
|
+
"ORIGINAL plan as-is, begin executing it now. This is a RESUMED task; do NOT re-plan or restart from scratch.", mark);
|
|
275
276
|
}
|
|
276
277
|
return formatHookFeedback("Your proposed PLAN was REVIEWED and EDITED by a human reviewer. Proceed with the REVISED plan below " +
|
|
277
278
|
"(it supersedes your earlier plan); begin executing it now. This is a RESUMED task; do NOT re-plan " +
|
|
278
279
|
"or restart from scratch, just carry out the revised plan.\n\nThe REVISED plan to follow is:\n" +
|
|
279
|
-
delimitUntrusted("revised plan", resume.outcome.editedPlan));
|
|
280
|
+
delimitUntrusted("revised plan", resume.outcome.editedPlan), mark);
|
|
280
281
|
}
|
|
281
282
|
const why = resume.outcome.reason ? ` Reviewer note: ${delimitUntrusted("reviewer note", resume.outcome.reason, REVIEWER_NOTE_MAX_BODY)}` : "";
|
|
282
283
|
return formatHookFeedback(`Your proposed PLAN was REVIEWED and REJECTED; it was NOT executed.${why} Produce a NEW plan that ` +
|
|
283
284
|
"addresses the concern, then continue. This is a RESUMED task — re-plan from the current state; do " +
|
|
284
|
-
"NOT execute the rejected plan.");
|
|
285
|
+
"NOT execute the rejected plan.", mark);
|
|
285
286
|
}
|
|
286
287
|
const { pendingAction } = resume.cp;
|
|
287
288
|
if (pendingAction.kind !== "tool_approval") {
|
|
288
|
-
return formatHookFeedback("This is a RESUMED task — continue from where you left off without restarting or re-running prior work.");
|
|
289
|
+
return formatHookFeedback("This is a RESUMED task — continue from where you left off without restarting or re-running prior work.", mark);
|
|
289
290
|
}
|
|
290
291
|
const decided = resume.outcome.decision === "allow"
|
|
291
292
|
? "was APPROVED and has now been executed — its result is in the tool results above"
|
|
@@ -297,7 +298,7 @@ function resumeContinuation(resume) {
|
|
|
297
298
|
return formatHookFeedback(`The pending tool call "${pendingAction.toolName}" ${decided}.${batchNote} ` +
|
|
298
299
|
`This is a RESUMED task — every step before this point is already done and its results are in the ` +
|
|
299
300
|
`conversation above. Do NOT restart the task or re-run any tool you already ran; continue from this ` +
|
|
300
|
-
`exact point, building on the existing results, and finish the remaining work
|
|
301
|
+
`exact point, building on the existing results, and finish the remaining work.`, mark);
|
|
301
302
|
}
|
|
302
303
|
const ENV_DUE_GOVERNANCE_READ_BUDGET_MS = 5_000;
|
|
303
304
|
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
@@ -514,7 +515,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
514
515
|
if (tightest !== undefined && tightest.ratio >= thresholds[index]) {
|
|
515
516
|
rs.counters.approachNoticesSent += 1;
|
|
516
517
|
const percent = Math.min(100, Math.round(tightest.ratio * 100));
|
|
517
|
-
const body = limitApproachFrames(index === 0 ? 0 : 1, tightest.axis, percent);
|
|
518
|
+
const body = limitApproachFrames(index === 0 ? 0 : 1, tightest.axis, percent, prepared.reminderMark);
|
|
518
519
|
void prepared.harness.steer(body, { engineMinted: true }).catch(() => undefined);
|
|
519
520
|
queue.push({ type: "steering_injected", source: "limit_approach", preview: body.slice(0, 220), ...ident() });
|
|
520
521
|
boundarySteered = true;
|
|
@@ -739,7 +740,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
739
740
|
}
|
|
740
741
|
}
|
|
741
742
|
if (due.length > 0) {
|
|
742
|
-
attachmentsPayload = due.map((a) =>
|
|
743
|
+
attachmentsPayload = due.map((a) => mintSystemReminder(sanitizeUntrustedText(a.body), prepared.reminderMark)).join("\n");
|
|
743
744
|
for (const a of due)
|
|
744
745
|
boundaryAttachmentBytes += Buffer.byteLength(a.body, "utf8");
|
|
745
746
|
rs.attach.attachmentsInjected += due.length;
|
|
@@ -766,7 +767,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
766
767
|
const clipped = clipToBytes(body, budget) === body
|
|
767
768
|
? body
|
|
768
769
|
: `${clipToBytes(body, budget - Buffer.byteLength(BATCH_TRUNCATION_MARKER, "utf8"))}${BATCH_TRUNCATION_MARKER}`;
|
|
769
|
-
batchContextBlock =
|
|
770
|
+
batchContextBlock = mintSystemReminder(clipped, prepared.reminderMark);
|
|
770
771
|
}
|
|
771
772
|
}
|
|
772
773
|
}
|
|
@@ -1000,8 +1001,8 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
1000
1001
|
};
|
|
1001
1002
|
return onTurnBoundary;
|
|
1002
1003
|
}
|
|
1003
|
-
function wrapGitFrame(body) {
|
|
1004
|
-
return
|
|
1004
|
+
function wrapGitFrame(body, mark) {
|
|
1005
|
+
return mintSystemReminder(sanitizeUntrustedText(body), mark);
|
|
1005
1006
|
}
|
|
1006
1007
|
async function resolveGitLegDelivery(prepared, cpMirror, report) {
|
|
1007
1008
|
const ref = prepared.gitStatusRef;
|
|
@@ -1044,7 +1045,7 @@ async function resolveGitLegDelivery(prepared, cpMirror, report) {
|
|
|
1044
1045
|
}
|
|
1045
1046
|
if (prior !== undefined && !pending && prior.entryId !== undefined) {
|
|
1046
1047
|
try {
|
|
1047
|
-
const newest = newestEngineGitFrame(await prepared.session.getBranch());
|
|
1048
|
+
const newest = newestEngineGitFrame(await prepared.session.getBranch(), prepared.reminderMark);
|
|
1048
1049
|
if (newest === undefined || newest.entryId !== prior.entryId)
|
|
1049
1050
|
pending = true;
|
|
1050
1051
|
}
|
|
@@ -1060,7 +1061,7 @@ async function resolveGitLegDelivery(prepared, cpMirror, report) {
|
|
|
1060
1061
|
if (prior === undefined && !pending) {
|
|
1061
1062
|
let mustDisown;
|
|
1062
1063
|
try {
|
|
1063
|
-
mustDisown = branchCarriesVisiblePositiveGitFrame(await prepared.session.getBranch());
|
|
1064
|
+
mustDisown = branchCarriesVisiblePositiveGitFrame(await prepared.session.getBranch(), prepared.reminderMark);
|
|
1064
1065
|
}
|
|
1065
1066
|
catch (err) {
|
|
1066
1067
|
mustDisown = true;
|
|
@@ -1073,9 +1074,9 @@ async function resolveGitLegDelivery(prepared, cpMirror, report) {
|
|
|
1073
1074
|
return frame.body;
|
|
1074
1075
|
}
|
|
1075
1076
|
if (!pending && prior !== undefined && prior.kind === frame.kind && prior.hash === frame.hash) {
|
|
1076
|
-
ref.protectedText = wrapGitFrame(frame.body);
|
|
1077
|
+
ref.protectedText = wrapGitFrame(frame.body, prepared.reminderMark);
|
|
1077
1078
|
if (frame.kind === "full" && frame.shrunk !== undefined) {
|
|
1078
|
-
ref.wrappedShrink = { find: ref.protectedText, replace: wrapGitFrame(frame.shrunk.body) };
|
|
1079
|
+
ref.wrappedShrink = { find: ref.protectedText, replace: wrapGitFrame(frame.shrunk.body, prepared.reminderMark) };
|
|
1079
1080
|
}
|
|
1080
1081
|
return undefined;
|
|
1081
1082
|
}
|
|
@@ -1759,9 +1760,9 @@ export class Runner {
|
|
|
1759
1760
|
const actorIn = options?.actor;
|
|
1760
1761
|
const actor = actorIn === undefined ? undefined : snapshotActorAssertion(actorIn);
|
|
1761
1762
|
const projected = projectHumanInput({ text, actor, source: "steer" });
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1763
|
+
let payload;
|
|
1764
|
+
let mintsAFrame;
|
|
1765
|
+
let replay;
|
|
1765
1766
|
const noteAccepted = (h) => {
|
|
1766
1767
|
if (!mintsAFrame)
|
|
1767
1768
|
return;
|
|
@@ -1790,6 +1791,9 @@ export class Runner {
|
|
|
1790
1791
|
const h = handle ?? (await orTimeout(ready));
|
|
1791
1792
|
if (!h)
|
|
1792
1793
|
throw steeringError("the task is not running");
|
|
1794
|
+
payload = trusted ? formatHookFeedback(projected, h.reminderMark) : projected;
|
|
1795
|
+
mintsAFrame = payload.trim().length !== 0;
|
|
1796
|
+
replay = { payload, trusted, ...(actor !== undefined ? { actor } : {}) };
|
|
1793
1797
|
if (typeof inputId === "string") {
|
|
1794
1798
|
const prior = acceptedSteerInputs.get(inputId);
|
|
1795
1799
|
if (prior !== undefined) {
|
|
@@ -2099,7 +2103,7 @@ export class Runner {
|
|
|
2099
2103
|
}
|
|
2100
2104
|
}
|
|
2101
2105
|
const loopLatch = { ended: false };
|
|
2102
|
-
onReady({ harness: prepared.harness, abortController: prepared.abortController, loop: loopLatch });
|
|
2106
|
+
onReady({ harness: prepared.harness, abortController: prepared.abortController, loop: loopLatch, reminderMark: prepared.reminderMark });
|
|
2103
2107
|
const stats = { turns: 0, tokens: 0, toolCalls: 0, promptTokens: 0, totalInputTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: 0, costMicroUsd: 0 };
|
|
2104
2108
|
prepared.liveSpendRef.get = () => ({ costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) });
|
|
2105
2109
|
if (resume &&
|
|
@@ -2313,12 +2317,12 @@ export class Runner {
|
|
|
2313
2317
|
return [];
|
|
2314
2318
|
const full = textOf(e.message);
|
|
2315
2319
|
if (m.engineMinted === true)
|
|
2316
|
-
return [stripGitStatusUnits(full)];
|
|
2320
|
+
return [stripGitStatusUnits(full, prepared.reminderMark)];
|
|
2317
2321
|
if (Array.isArray(m.engineSegments) && m.engineSegments.length > 0) {
|
|
2318
|
-
return m.engineSegments.map((s) => stripGitStatusUnits(full.slice(Math.max(0, s.start), Math.max(0, s.end))));
|
|
2322
|
+
return m.engineSegments.map((s) => stripGitStatusUnits(full.slice(Math.max(0, s.start), Math.max(0, s.end)), prepared.reminderMark));
|
|
2319
2323
|
}
|
|
2320
2324
|
if (typeof m.enginePrefixChars === "number" && m.enginePrefixChars > 0)
|
|
2321
|
-
return [stripGitStatusUnits(full.slice(0, m.enginePrefixChars))];
|
|
2325
|
+
return [stripGitStatusUnits(full.slice(0, m.enginePrefixChars), prepared.reminderMark)];
|
|
2322
2326
|
return [];
|
|
2323
2327
|
});
|
|
2324
2328
|
}
|
|
@@ -2407,7 +2411,8 @@ export class Runner {
|
|
|
2407
2411
|
const text = typeof m.content === "string"
|
|
2408
2412
|
? m.content
|
|
2409
2413
|
: m.content.map((b) => (b !== null && typeof b === "object" && b.type === "text" ? String(b.text ?? "") : "")).join("\n");
|
|
2410
|
-
|
|
2414
|
+
const fvMarked = m.engineMinted === true && text.includes(`<system-reminder mark="${prepared.reminderMark}">[final verification]`);
|
|
2415
|
+
if ((fvMarked || text.includes("<system-reminder>[final verification]")) && rs.counters.finalVerifyInjections < 2)
|
|
2411
2416
|
rs.counters.finalVerifyInjections += 1;
|
|
2412
2417
|
}
|
|
2413
2418
|
else if (m.role === "toolResult" && !rs.counters.wroteThisRun && (prepared.toolEffects.get(m.toolName) ?? "write") !== "read") {
|
|
@@ -2687,7 +2692,8 @@ export class Runner {
|
|
|
2687
2692
|
finalVerifyBudgetFill() < 0.9) {
|
|
2688
2693
|
rs.counters.finalVerifyInjections += 1;
|
|
2689
2694
|
if (rs.counters.finalVerifyInjections === 2) {
|
|
2690
|
-
const reentryBody =
|
|
2695
|
+
const reentryBody = openSystemReminder(prepared.reminderMark) +
|
|
2696
|
+
"[final verification] Your tool calls in this run worked with raw bytes, structural parsing, " +
|
|
2691
2697
|
"or checksum/digest computation — the deliverable very likely embeds verifiable structure (structural fields, an " +
|
|
2692
2698
|
"embedded checksum-family value, reference data it must match, or a replayable deterministic path). You MUST " +
|
|
2693
2699
|
"execute the grounding check that structure supports — recompute the embedded value and compare it against the " +
|
|
@@ -2706,7 +2712,8 @@ export class Runner {
|
|
|
2706
2712
|
},
|
|
2707
2713
|
];
|
|
2708
2714
|
}
|
|
2709
|
-
const nudgeBody =
|
|
2715
|
+
const nudgeBody = openSystemReminder(prepared.reminderMark) +
|
|
2716
|
+
"[final verification] Before finishing: re-verify the FINAL deliverable through its REAL entry point, " +
|
|
2710
2717
|
"exactly as the acceptance criteria would exercise it — execute the binary/function/endpoint directly and read the ACTUAL " +
|
|
2711
2718
|
"output and exit code. Do NOT rely on earlier self-tests, shell redirections, or assumptions (a program that prints to " +
|
|
2712
2719
|
"stdout is not a program that writes the required file). If anything mismatches the task's requirements, fix it before " +
|
|
@@ -2770,7 +2777,7 @@ export class Runner {
|
|
|
2770
2777
|
messages.push({
|
|
2771
2778
|
role: "user",
|
|
2772
2779
|
engineMinted: true,
|
|
2773
|
-
content: formatHookFeedback(`Stop hook additional context: ${result.additionalContext}
|
|
2780
|
+
content: formatHookFeedback(`Stop hook additional context: ${result.additionalContext}`, prepared.reminderMark),
|
|
2774
2781
|
timestamp: Date.now(),
|
|
2775
2782
|
});
|
|
2776
2783
|
}
|
|
@@ -2790,7 +2797,7 @@ export class Runner {
|
|
|
2790
2797
|
messages.push({
|
|
2791
2798
|
role: "user",
|
|
2792
2799
|
engineMinted: true,
|
|
2793
|
-
content: formatHookFeedback(`Stop hook stopped continuation: ${result.block}
|
|
2800
|
+
content: formatHookFeedback(`Stop hook stopped continuation: ${result.block}`, prepared.reminderMark),
|
|
2794
2801
|
timestamp: Date.now(),
|
|
2795
2802
|
});
|
|
2796
2803
|
}
|
|
@@ -2912,7 +2919,7 @@ export class Runner {
|
|
|
2912
2919
|
const use = ref.overBudgetShrunk && frame.shrunk !== undefined
|
|
2913
2920
|
? { kind: "degraded", body: frame.shrunk.body, hash: frame.shrunk.hash }
|
|
2914
2921
|
: { kind: frame.kind, body: frame.body, hash: frame.hash };
|
|
2915
|
-
const wrapped = wrapGitFrame(use.body);
|
|
2922
|
+
const wrapped = wrapGitFrame(use.body, prepared.reminderMark);
|
|
2916
2923
|
try {
|
|
2917
2924
|
const entryId = await prepared.session.appendMessage({
|
|
2918
2925
|
role: "user",
|
|
@@ -2923,7 +2930,7 @@ export class Runner {
|
|
|
2923
2930
|
ref.announced = { kind: use.kind, hash: use.hash, entryId };
|
|
2924
2931
|
ref.protectedText = wrapped;
|
|
2925
2932
|
if (use.kind === "full" && frame.shrunk !== undefined) {
|
|
2926
|
-
ref.wrappedShrink = { find: wrapped, replace: wrapGitFrame(frame.shrunk.body) };
|
|
2933
|
+
ref.wrappedShrink = { find: wrapped, replace: wrapGitFrame(frame.shrunk.body, prepared.reminderMark) };
|
|
2927
2934
|
}
|
|
2928
2935
|
else {
|
|
2929
2936
|
delete ref.wrappedShrink;
|
|
@@ -3045,13 +3052,13 @@ export class Runner {
|
|
|
3045
3052
|
rs.limits.budgetAxis = "cost";
|
|
3046
3053
|
}
|
|
3047
3054
|
else {
|
|
3048
|
-
let continuation = resumeContinuation(resume);
|
|
3055
|
+
let continuation = resumeContinuation(resume, prepared.reminderMark);
|
|
3049
3056
|
const bgSnapshot = resume.cp.state.runningBackgroundTasks;
|
|
3050
3057
|
if (bgSnapshot !== undefined && bgSnapshot.length > 0) {
|
|
3051
3058
|
const alive = new Set(prepared.listBackgroundTasks().map((t) => t.id));
|
|
3052
3059
|
const orphans = bgSnapshot.filter((t) => !alive.has(t.id));
|
|
3053
3060
|
if (orphans.length > 0) {
|
|
3054
|
-
continuation += "\n\n" + formatHookFeedback(renderOrphanedBackgroundTasks(orphans));
|
|
3061
|
+
continuation += "\n\n" + formatHookFeedback(renderOrphanedBackgroundTasks(orphans), prepared.reminderMark);
|
|
3055
3062
|
}
|
|
3056
3063
|
}
|
|
3057
3064
|
let gitResumeDelivered;
|
|
@@ -3065,11 +3072,11 @@ export class Runner {
|
|
|
3065
3072
|
});
|
|
3066
3073
|
const gitFrame = prepared.gitStatusRef.frame;
|
|
3067
3074
|
if (gitBody !== undefined && gitFrame !== undefined) {
|
|
3068
|
-
const wrappedGit = wrapGitFrame(gitBody);
|
|
3075
|
+
const wrappedGit = wrapGitFrame(gitBody, prepared.reminderMark);
|
|
3069
3076
|
continuation += "\n\n" + wrappedGit;
|
|
3070
3077
|
gitResumeDelivered = { kind: gitFrame.kind, hash: gitFrame.hash, wrapped: wrappedGit };
|
|
3071
3078
|
if (gitFrame.kind === "full" && gitFrame.shrunk !== undefined) {
|
|
3072
|
-
prepared.gitStatusRef.wrappedShrink = { find: wrappedGit, replace: wrapGitFrame(gitFrame.shrunk.body) };
|
|
3079
|
+
prepared.gitStatusRef.wrappedShrink = { find: wrappedGit, replace: wrapGitFrame(gitFrame.shrunk.body, prepared.reminderMark) };
|
|
3073
3080
|
}
|
|
3074
3081
|
}
|
|
3075
3082
|
}
|
|
@@ -3083,7 +3090,7 @@ export class Runner {
|
|
|
3083
3090
|
const projected = projectHumanInput({ text: steer.text, actor: steer.actor, source });
|
|
3084
3091
|
continuation +=
|
|
3085
3092
|
"\n\n" +
|
|
3086
|
-
(steer.trusted ? formatHookFeedback(projected) : delimitUntrusted("supervisor steering message", projected));
|
|
3093
|
+
(steer.trusted ? formatHookFeedback(projected, prepared.reminderMark) : delimitUntrusted("supervisor steering message", projected));
|
|
3087
3094
|
if (steer.trusted)
|
|
3088
3095
|
engineSegments.push({ start, end: continuation.length });
|
|
3089
3096
|
queue.push({
|
|
@@ -3131,11 +3138,11 @@ export class Runner {
|
|
|
3131
3138
|
try {
|
|
3132
3139
|
const decision = await userPromptSubmit(spec.objective, { identity: prepared.hookIdentity });
|
|
3133
3140
|
if (decision?.block) {
|
|
3134
|
-
prepared.blockedRef.reason = formatHookFeedback(decision.block);
|
|
3141
|
+
prepared.blockedRef.reason = formatHookFeedback(decision.block, prepared.reminderMark);
|
|
3135
3142
|
promptBlocked = true;
|
|
3136
3143
|
}
|
|
3137
3144
|
else if (decision?.additionalContext) {
|
|
3138
|
-
effectiveObjective = `${formatHookFeedback(decision.additionalContext)}\n\n${projectedObjective}`;
|
|
3145
|
+
effectiveObjective = `${formatHookFeedback(decision.additionalContext, prepared.reminderMark)}\n\n${projectedObjective}`;
|
|
3139
3146
|
}
|
|
3140
3147
|
}
|
|
3141
3148
|
catch (hookErr) {
|
|
@@ -3146,7 +3153,7 @@ export class Runner {
|
|
|
3146
3153
|
catch {
|
|
3147
3154
|
}
|
|
3148
3155
|
prepared.blockedRef.reason = formatHookFeedback(`the deployment's userPromptSubmit hook crashed while screening this prompt (${err.message}); ` +
|
|
3149
|
-
`the prompt was NOT submitted (fail-closed)
|
|
3156
|
+
`the prompt was NOT submitted (fail-closed)`, prepared.reminderMark);
|
|
3150
3157
|
promptBlocked = true;
|
|
3151
3158
|
}
|
|
3152
3159
|
}
|
|
@@ -3161,13 +3168,13 @@ export class Runner {
|
|
|
3161
3168
|
});
|
|
3162
3169
|
const gitFrame = prepared.gitStatusRef.frame;
|
|
3163
3170
|
if (gitBody !== undefined && gitFrame !== undefined) {
|
|
3164
|
-
const wrappedGit = wrapGitFrame(gitBody);
|
|
3171
|
+
const wrappedGit = wrapGitFrame(gitBody, prepared.reminderMark);
|
|
3165
3172
|
const standalone = spec.images !== undefined && spec.images.length > 0;
|
|
3166
3173
|
if (!standalone)
|
|
3167
3174
|
effectiveObjective = `${wrappedGit}\n${effectiveObjective}`;
|
|
3168
3175
|
gitLegDelivered = { kind: gitFrame.kind, hash: gitFrame.hash, standalone, wrapped: wrappedGit };
|
|
3169
3176
|
if (gitFrame.kind === "full" && gitFrame.shrunk !== undefined) {
|
|
3170
|
-
prepared.gitStatusRef.wrappedShrink = { find: wrappedGit, replace: wrapGitFrame(gitFrame.shrunk.body) };
|
|
3177
|
+
prepared.gitStatusRef.wrappedShrink = { find: wrappedGit, replace: wrapGitFrame(gitFrame.shrunk.body, prepared.reminderMark) };
|
|
3171
3178
|
}
|
|
3172
3179
|
}
|
|
3173
3180
|
}
|
|
@@ -3205,7 +3212,7 @@ export class Runner {
|
|
|
3205
3212
|
}
|
|
3206
3213
|
}
|
|
3207
3214
|
if (firstFrames.length > 0) {
|
|
3208
|
-
effectiveObjective = `${firstFrames.map((f) =>
|
|
3215
|
+
effectiveObjective = `${firstFrames.map((f) => mintSystemReminder(sanitizeUntrustedText(f.body), prepared.reminderMark)).join("\n")}\n${effectiveObjective}`;
|
|
3209
3216
|
}
|
|
3210
3217
|
}
|
|
3211
3218
|
const gitQueuedChars = gitLegDelivered !== undefined && gitLegDelivered.standalone ? gitLegDelivered.wrapped.length : 0;
|
|
@@ -4385,8 +4392,8 @@ export class Runner {
|
|
|
4385
4392
|
return l !== undefined && l !== name ? { label: l } : {};
|
|
4386
4393
|
})();
|
|
4387
4394
|
emit({ type: "tool_start", toolCallId: id, toolName: name, ...displayLabel, args: deferredArgs.get(id) ?? {} });
|
|
4388
|
-
emit({ type: "tool_end", toolCallId: id, toolName: name, ...displayLabel, isError: true, ...toolEndBodyFrom({ content: formatHookFeedback(DEFERRED_REISSUE) }, true) });
|
|
4389
|
-
const eid = await prepared.session.appendMessage(toolResultMsg(id, name, formatHookFeedback(DEFERRED_REISSUE), true));
|
|
4395
|
+
emit({ type: "tool_end", toolCallId: id, toolName: name, ...displayLabel, isError: true, ...toolEndBodyFrom({ content: formatHookFeedback(DEFERRED_REISSUE, prepared.reminderMark) }, true) });
|
|
4396
|
+
const eid = await prepared.session.appendMessage(toolResultMsg(id, name, formatHookFeedback(DEFERRED_REISSUE, prepared.reminderMark), true));
|
|
4390
4397
|
emitCommitted(eid, "toolResult", id);
|
|
4391
4398
|
}
|
|
4392
4399
|
}
|
|
@@ -4407,8 +4414,8 @@ export class Runner {
|
|
|
4407
4414
|
? `The approval for the pending tool call "${pendingAction.toolName}" ended without anyone deciding it (it was cancelled or could not be delivered), so it was not executed.`
|
|
4408
4415
|
: `The pending tool call "${pendingAction.toolName}" was denied by an approver.`;
|
|
4409
4416
|
const reason = outcome.reason ? delimitUntrusted("reviewer note", outcome.reason, REVIEWER_NOTE_MAX_BODY) : defaultDenial;
|
|
4410
|
-
emitEnd(true, { content: formatHookFeedback(reason) });
|
|
4411
|
-
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(reason), true));
|
|
4417
|
+
emitEnd(true, { content: formatHookFeedback(reason, prepared.reminderMark) });
|
|
4418
|
+
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(reason, prepared.reminderMark), true));
|
|
4412
4419
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
4413
4420
|
return;
|
|
4414
4421
|
}
|
|
@@ -4431,7 +4438,7 @@ export class Runner {
|
|
|
4431
4438
|
const beltUnsafe = rechecked.action === "deny" ||
|
|
4432
4439
|
(rechecked.updatedInput !== undefined && !deepJsonEqual(rechecked.updatedInput, resolvedArgs));
|
|
4433
4440
|
if (beltUnsafe) {
|
|
4434
|
-
const editedDenial = formatHookFeedback(`The approver EDITED this call's input; the edited call is refused by the deployment's tool policy and was not executed${rechecked.action === "deny" && rechecked.message ? `: ${rechecked.message}` : " (the policy rewrote the edit to a value that was not approved)"}
|
|
4441
|
+
const editedDenial = formatHookFeedback(`The approver EDITED this call's input; the edited call is refused by the deployment's tool policy and was not executed${rechecked.action === "deny" && rechecked.message ? `: ${rechecked.message}` : " (the policy rewrote the edit to a value that was not approved)"}.`, prepared.reminderMark);
|
|
4435
4442
|
emitEnd(true, { content: editedDenial });
|
|
4436
4443
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, editedDenial, true));
|
|
4437
4444
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
@@ -4441,7 +4448,7 @@ export class Runner {
|
|
|
4441
4448
|
const gateRealApproval = resume.cp.gate.kind === "irreversible_ask" ? resume.cp.gate.realApproval : undefined;
|
|
4442
4449
|
const gateOrgGoverned = gateRealApproval?.origin === "org_rule" || gateRealApproval?.origin === "org_unavailable";
|
|
4443
4450
|
if (gateOrgGoverned && prepared.permissionRuleOrg === undefined) {
|
|
4444
|
-
const unwiredDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: its approval was minted under organization governance (${gateRealApproval.origin}), and this worker has no org adjudication wiring — a governed approval may only be redeemed where governance can be enforced. This approval is spent; re-issue the call on an org-wired worker
|
|
4451
|
+
const unwiredDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: its approval was minted under organization governance (${gateRealApproval.origin}), and this worker has no org adjudication wiring — a governed approval may only be redeemed where governance can be enforced. This approval is spent; re-issue the call on an org-wired worker.`, prepared.reminderMark);
|
|
4445
4452
|
emitEnd(true, { content: unwiredDenial });
|
|
4446
4453
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, unwiredDenial, true));
|
|
4447
4454
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
@@ -4472,7 +4479,7 @@ export class Runner {
|
|
|
4472
4479
|
const orgDisclosures = org.status === "unavailable" && org.disclosures.length > 0
|
|
4473
4480
|
? ` Governance disclosures: ${inlineUntrusted(org.disclosures.join("; "), ORG_DISCLOSURE_MAX_CHARS)}.`
|
|
4474
4481
|
: "";
|
|
4475
|
-
const orgDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${blocked}.${orgDisclosures} This approval is spent — the call has to be re-issued and approved again once organization policy permits it
|
|
4482
|
+
const orgDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${blocked}.${orgDisclosures} This approval is spent — the call has to be re-issued and approved again once organization policy permits it.`, prepared.reminderMark);
|
|
4476
4483
|
emitEnd(true, { content: orgDenial });
|
|
4477
4484
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, orgDenial, true));
|
|
4478
4485
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
@@ -4488,7 +4495,7 @@ export class Runner {
|
|
|
4488
4495
|
...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
|
|
4489
4496
|
}, prepared.abortController.signal));
|
|
4490
4497
|
if (narrowed.action === "deny") {
|
|
4491
|
-
const narrowedDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" is now denied by a session rule and was not executed${narrowed.message ? `: ${narrowed.message}` : ""}
|
|
4498
|
+
const narrowedDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" is now denied by a session rule and was not executed${narrowed.message ? `: ${narrowed.message}` : ""}.`, prepared.reminderMark);
|
|
4492
4499
|
emitEnd(true, { content: narrowedDenial });
|
|
4493
4500
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, narrowedDenial, true));
|
|
4494
4501
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
@@ -9,7 +9,8 @@ const INTERRUPTED_IDEMPOTENT = "[INTERRUPTED] The previous run ended before this
|
|
|
9
9
|
"may not have taken effect. It is safe to REPLAY: re-issuing the same call converges the state to " +
|
|
10
10
|
"what you intended. If you decide not to re-issue it, verify the current state first.";
|
|
11
11
|
const INTERRUPTED_NEVER_STARTED = "[INTERRUPTED] The run was aborted before this tool call started. It was never executed and had " +
|
|
12
|
-
"no side effects
|
|
12
|
+
"no side effects. If the run was stopped by a pending approval or an unresolved gate, wait for it " +
|
|
13
|
+
"to be resolved before re-issuing; re-issuing is otherwise safe if you still need this call.";
|
|
13
14
|
const INTERRUPTED_REPEAT_UNKNOWN = "[INTERRUPTED] Same as an earlier interrupted call in this batch: no result was recorded and the outcome is " +
|
|
14
15
|
"UNKNOWN — verify before relying on it.";
|
|
15
16
|
const INTERRUPTED_REPEAT_SAFE = "[INTERRUPTED] Same as an earlier interrupted call in this batch: this tool is read-only/idempotent, so just " +
|
|
@@ -17,7 +18,7 @@ const INTERRUPTED_REPEAT_SAFE = "[INTERRUPTED] Same as an earlier interrupted ca
|
|
|
17
18
|
const INTERRUPTED_REPEAT_IDEMPOTENT = "[INTERRUPTED] Same as an earlier interrupted call in this batch: it is safe to REPLAY — re-issue it, or verify " +
|
|
18
19
|
"the current state if you decide not to.";
|
|
19
20
|
const INTERRUPTED_REPEAT_NEVER_STARTED = "[INTERRUPTED] Same as an earlier interrupted call in this batch: it never started and had no side effects — " +
|
|
20
|
-
"safe
|
|
21
|
+
"re-issuing is safe once whatever stopped the run is resolved.";
|
|
21
22
|
const REPEAT_TEXT = {
|
|
22
23
|
never_started: INTERRUPTED_REPEAT_NEVER_STARTED,
|
|
23
24
|
read: INTERRUPTED_REPEAT_SAFE,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AssistantMessage, StopReason, Tool, ToolResultMessage, Usage, UserMessage } from "../internal/llm.js";
|
|
2
|
-
import type { Brain, Model, ModelRef, ModelRole, ModelRoles, ThinkingLevel } from "./types.js";
|
|
2
|
+
import type { Brain, Model, ModelRef, ModelRole, ModelRoles, TaskSpec, ThinkingLevel } from "./types.js";
|
|
3
3
|
import { type DegradationInfo } from "../brain/degrading.js";
|
|
4
4
|
/** A tool DEFINITION exposed to the model in a side query (never executed by the engine). */
|
|
5
5
|
export interface SideQueryToolDef {
|
|
@@ -54,6 +54,16 @@ export interface SideQuerySpec {
|
|
|
54
54
|
tools?: SideQueryToolDef[];
|
|
55
55
|
/** Per-request output cap (provider max_tokens). */
|
|
56
56
|
maxOutputTokens?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Per-model auth — MIRRORS {@link TaskSpec.getApiKeyAndHeaders} (same signature, resolved per
|
|
59
|
+
* call against the RESOLVED model, exactly like the task path's per-call hook). The brain
|
|
60
|
+
* contract is `options.apiKey ?? config.apiKey`, and a model's own `baseUrl` outranks the
|
|
61
|
+
* brain's — so before this seat existed, a side query routed to a model carrying its own
|
|
62
|
+
* `baseUrl` + per-model key fell back to the brain's construction-time credential and sent the
|
|
63
|
+
* GATEWAY key to the per-model (possibly external) URL: a credential leak the task path already
|
|
64
|
+
* prevents. Absent ⇒ construction-time credentials apply, options byte-identical to before.
|
|
65
|
+
*/
|
|
66
|
+
getApiKeyAndHeaders?: TaskSpec["getApiKeyAndHeaders"];
|
|
57
67
|
signal?: AbortSignal;
|
|
58
68
|
}
|
|
59
69
|
export interface SideQueryResult {
|
package/dist/core/side-query.js
CHANGED
|
@@ -44,10 +44,13 @@ export async function runSideQuery(spec, deps) {
|
|
|
44
44
|
: {}),
|
|
45
45
|
};
|
|
46
46
|
const thinking = resolved.thinking ?? resolved.model.defaultThinking;
|
|
47
|
+
const auth = await spec.getApiKeyAndHeaders?.(resolved.model);
|
|
47
48
|
const options = {
|
|
48
49
|
...(spec.signal !== undefined ? { signal: spec.signal } : {}),
|
|
49
50
|
...(spec.maxOutputTokens !== undefined ? { maxTokens: spec.maxOutputTokens } : {}),
|
|
50
51
|
...(thinking !== undefined ? { reasoning: thinking } : {}),
|
|
52
|
+
...(auth?.apiKey !== undefined ? { apiKey: auth.apiKey } : {}),
|
|
53
|
+
...(auth?.headers !== undefined ? { headers: auth.headers } : {}),
|
|
51
54
|
};
|
|
52
55
|
const stream = await Promise.resolve(deps.brain.stream(resolved.model, context, options));
|
|
53
56
|
const msg = (await stream.result());
|