@sema-agent/core 5.41.0 → 5.43.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.
@@ -56,7 +56,8 @@ import { prepareMemory } from "./prepare-memory.js";
56
56
  import { limitConfigError, prepareConfigDoors } from "./prepare-config-doors.js";
57
57
  import { NAMESPACED_NAME_SHAPES, prepareSafetyScan } from "./prepare-safety-scan.js";
58
58
  import { prepareAcquireReconcile } from "./prepare-acquire-reconcile.js";
59
- import { prepareWorkspaceRestore, rebaseWorkspacePath, remoteEnvFailureNote, restoreWorkspaceWithRetry } from "./prepare-workspace-restore.js";
59
+ import { prepareHandsMount, resolveHandsLessReadFace } from "./prepare-hands-readface.js";
60
+ import { prepareWorkspaceRestore, remoteEnvFailureNote, restoreWorkspaceWithRetry } from "./prepare-workspace-restore.js";
60
61
  import { defaultPromptProvider, buildEnvironmentContext, formatLocalDate, isValidTimeZone, PROJECT_CONTEXT_FRAMING } from "../../prompts/default.js";
61
62
  import { applyGitFrameGuard, probeGitStatusLane } from "./git-status-frame.js";
62
63
  import { assemblePrompt } from "../../prompt-assembly/assemble.js";
@@ -76,7 +77,7 @@ import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.j
76
77
  import { createTaskOutputTool, createTaskStopTool, defaultTaskRegistry } from "../task-registry.js";
77
78
  import { createMonitorTool } from "../../tools/monitor.js";
78
79
  import { createWorktreeTools } from "../../tools/worktree.js";
79
- import { applyCompactionToReadFileState, bashReversibilityProbe, compileReadDeny, createHandsToolkit, deploymentReadFaceClampNotice, isReadDedupStubResult, resolveReadDenyBuiltins, resolveReadFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, FULL_SHELL_CONTRACT_ID, HAND_TOOL_EFFECTS, pdfModelCapabilitiesOf } from "../../tools/fs/index.js";
80
+ import { applyCompactionToReadFileState, isReadDedupStubResult, FULL_SHELL_CONTRACT_ID, HAND_TOOL_EFFECTS } from "../../tools/fs/index.js";
80
81
  import { decodeTextBytes } from "../../tools/fs/encoding.js";
81
82
  import { ASK_USER_QUESTION_TOOL_NAME, createAskUserQuestionTool, classifyQuestionOutcome, isLiveQuestionFace, validateAskQuestions, } from "../ask-question.js";
82
83
  import { createSchedulerTools } from "../../tools/scheduler-tools.js";
@@ -86,8 +87,7 @@ import { createRunWorkflowTool, RUN_WORKFLOW_TOOL_NAME } from "../../orchestrati
86
87
  import { resolveWorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
87
88
  import { createLspTool, gitCheckIgnoreFilter, resolveLspPath } from "../lsp.js";
88
89
  import { resolveKey } from "../../tools/fs/safety.js";
89
- import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
90
- 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";
90
+ import { BINDING_CHECKPOINT_VERSION, mintCheckpointId, 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";
91
91
  import { boundInputHashOf } from "../canonical-json.js";
92
92
  import { countElicitOptIns, deriveWiringManifest, resolveAskSeamForm, resolveDeclaredDurability, resolveElicitSeam, resolveQuestionSeam } from "../wiring-manifest.js";
93
93
  import { durableParkGapFor } from "../park-selfcheck.js";
@@ -191,11 +191,7 @@ function announceToolModelGate(onNotice, modelId, gate) {
191
191
  }
192
192
  }
193
193
  }
194
- const readFaceClampAnnouncedSinks = new WeakSet();
195
- let readFaceClampConsoleAnnounced = false;
196
- export function __resetReadFaceClampAnnouncement() {
197
- readFaceClampConsoleAnnounced = false;
198
- }
194
+ export { __resetReadFaceClampAnnouncement } from "./prepare-hands-readface.js";
199
195
  const DEFAULT_MAX_SUSPENDS = 5;
200
196
  const ULTRA_REASONING_TIERS = new Set(["xhigh", "max"]);
201
197
  const DEFAULT_RESOURCE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
@@ -1036,14 +1032,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1036
1032
  resourceSpentMicroUsd: priorLedger?.spentMicroUsd ?? 0,
1037
1033
  suspendCount: priorSuspendCount,
1038
1034
  };
1039
- let readFileStateForCheckpoint;
1040
- let seedContextFiles;
1041
- let handsCwdRef;
1042
- let workspaceStateSettle;
1043
- let wsSnapshot;
1044
- let rebaseWsPath = (p) => p;
1045
1035
  let worktreeSessionRef;
1046
- let backgroundTaskToolsActive = false;
1047
1036
  let suspendForResource;
1048
1037
  let suspendForPlatformLimit;
1049
1038
  let suspendForReview;
@@ -1209,219 +1198,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1209
1198
  foldProtocolAxes(a2a.toolAxes, "A2A");
1210
1199
  const callIssuedAtRef = {};
1211
1200
  const memoryWriteGateRef = {};
1212
- const additionalRootsCanonical = [];
1213
- const additionalReadRootsCanonical = [];
1214
- let attachmentRootCanonical;
1215
- let readDenyMatcher;
1216
- if (handsEnabled) {
1217
- const rootRaw = taskRootFinal;
1218
- const canon = await executionEnv.canonicalPath(rootRaw);
1219
- const rootCanonical = canon.ok ? canon.value : rootRaw;
1220
- attachmentRootCanonical = rootCanonical;
1221
- const canonicalizeExtraDirs = async (dirs, field, sink) => {
1222
- for (const dir of dirs ?? []) {
1223
- if (!dir || !dir.trim())
1224
- continue;
1225
- const c = await executionEnv.canonicalPath(dir);
1226
- if (c.ok) {
1227
- sink.push(c.value);
1228
- }
1229
- else {
1230
- deps.onError?.(new Error(`${field} entry skipped (cannot canonicalize): ${dir}`), {
1231
- phase: "config",
1232
- sessionId,
1233
- });
1234
- emitTrace(deps.tracer, () => ({
1235
- kind: "config.additional_directory_skipped",
1236
- version: 1,
1237
- taskId: hostTaskId,
1238
- entry: dir,
1239
- ...(field !== "additionalDirectories" ? { field } : {}),
1240
- reason: `${c.error.code}: ${c.error.message}`,
1241
- ts: Date.now(),
1242
- }));
1243
- }
1244
- }
1245
- };
1246
- await canonicalizeExtraDirs(spec.additionalDirectories, "additionalDirectories", additionalRootsCanonical);
1247
- await canonicalizeExtraDirs(spec.additionalReadDirectories, "additionalReadDirectories", additionalReadRootsCanonical);
1248
- if (spec.envFacts?.scratchpadDir) {
1249
- let c = await executionEnv.canonicalPath(spec.envFacts.scratchpadDir);
1250
- if (!c.ok && c.error.code === "not_found") {
1251
- const marker = await executionEnv.joinPath([spec.envFacts.scratchpadDir, ".sema-scratchpad"]);
1252
- const exclusiveCreate = executionEnv.writeFileExclusive?.bind(executionEnv);
1253
- if (marker.ok && exclusiveCreate) {
1254
- const w = await exclusiveCreate(marker.value, "");
1255
- if (w.ok || w.error.code === "already_exists")
1256
- c = await executionEnv.canonicalPath(spec.envFacts.scratchpadDir);
1257
- }
1258
- }
1259
- if (c.ok)
1260
- additionalRootsCanonical.push(c.value);
1261
- }
1262
- for (const a of [additionalRootsCanonical, additionalReadRootsCanonical])
1263
- a.splice(0, a.length, ...new Set(a));
1264
- const readFileState = new Map((resume?.seed.readFileState ?? []).map(([k, v]) => [rebaseRestoredPath(k), v]));
1265
- readFileStateForCheckpoint = readFileState;
1266
- seedContextFiles = async (files) => {
1267
- for (const f of files) {
1268
- const rk = await resolveKey(executionEnv, rootCanonical, f.path, undefined, undefined, [...additionalRootsCanonical, ...additionalReadRootsCanonical], undefined, undefined, resolvedReadFace);
1269
- if (rk.ok)
1270
- seedReadFileStateFromContext(readFileState, rk.key, f.content);
1271
- }
1272
- };
1273
- const handsIncludeShell = !(executionEnv instanceof StubExecutionEnv);
1274
- const handsReadOnly = spec.handsReadOnly === true;
1275
- backgroundTaskToolsActive = handsIncludeShell && !handsReadOnly && hasBackgroundShell(executionEnv);
1276
- wsSnapshot = resume !== undefined || !(handsIncludeShell && !handsReadOnly) ? undefined : await session.getWorkspaceState().catch(() => undefined);
1277
- rebaseWsPath = (p) => (wsSnapshot === undefined || wsSnapshot.taskRoot === rootCanonical ? p : rebaseWorkspacePath(p, wsSnapshot.taskRoot, rootCanonical));
1278
- if (handsIncludeShell && !handsReadOnly) {
1279
- handsCwdRef = { current: rootCanonical };
1280
- if (resume?.seed.handsCwd !== undefined)
1281
- handsCwdRef.current = rebaseRestoredPath(resume.seed.handsCwd);
1282
- else if (wsSnapshot?.handsCwd !== undefined)
1283
- handsCwdRef.current = rebaseWsPath(wsSnapshot.handsCwd);
1284
- workspaceStateSettle = {
1285
- rootCanonical,
1286
- restoredHandsCwd: handsCwdRef.current !== rootCanonical ? handsCwdRef.current : undefined,
1287
- ...(resume !== undefined ? { baselineUnknown: true } : {}),
1288
- };
1289
- }
1290
- const seedReadFaceSection = resume?.seed.readFace;
1291
- const readDenyAdditions = [...(deps.readDenyPatterns ?? []), ...(spec.readDenyPatterns ?? []), ...(seedReadFaceSection?.denyEntries ?? [])];
1292
- const readDenyBuiltinCfg = {
1293
- ...(deps.readDenyBuiltinTiers !== undefined ? { tiers: [...deps.readDenyBuiltinTiers] } : {}),
1294
- ...(deps.readDenyBuiltinExclude !== undefined ? { exclude: [...deps.readDenyBuiltinExclude] } : {}),
1295
- };
1296
- readDenyMatcher = compileReadDeny(readDenyAdditions, "readDenyPatterns", readDenyBuiltinCfg);
1297
- readDenyAdditionsNormalized = compileReadDeny(readDenyAdditions, "readDenyPatterns", { tiers: [] }).entries;
1298
- let liveReadFace = resolveReadFace({
1299
- specReadFace: spec.readFace,
1300
- depsReadFace: deps.readFace,
1301
- readOnlyMount: handsReadOnly,
1302
- orgGoverned: deps.permissionRuleOrg !== undefined,
1303
- fullShellReachable,
1304
- onDeploymentClamp: () => {
1305
- const sink = deps.onNotice;
1306
- if (typeof sink === "function") {
1307
- if (readFaceClampAnnouncedSinks.has(sink))
1308
- return;
1309
- readFaceClampAnnouncedSinks.add(sink);
1310
- }
1311
- else {
1312
- if (readFaceClampConsoleAnnounced)
1313
- return;
1314
- readFaceClampConsoleAnnounced = true;
1315
- }
1316
- deliverEngineNotice(sink, deploymentReadFaceClampNotice());
1317
- },
1318
- });
1319
- if (resume !== undefined && (seedReadFaceSection === undefined || seedReadFaceSection.face === "roots"))
1320
- liveReadFace = "roots";
1321
- resolvedReadFace = liveReadFace;
1322
- if (resume === undefined && spec.sessionId !== undefined) {
1323
- const prior = await session.buildContext().catch(() => undefined);
1324
- for (const rec of wholeFileRecordsFromTranscript(prior?.messages ?? [])) {
1325
- const rk = await resolveKey(executionEnv, rootCanonical, rec.path, undefined, undefined, [...additionalRootsCanonical, ...additionalReadRootsCanonical], undefined, undefined, liveReadFace);
1326
- if (rk.ok)
1327
- seedReadFileStateFromTranscript(readFileState, rk.key, rec.content, rec.at);
1328
- }
1329
- }
1330
- const band = createHandsToolkit(executionEnv, readFileState, rootCanonical, {
1331
- ...(additionalRootsCanonical.length > 0 ? { additionalRoots: additionalRootsCanonical } : {}),
1332
- ...(additionalReadRootsCanonical.length > 0 ? { additionalReadRoots: additionalReadRootsCanonical } : {}),
1333
- ...(readDenyAdditions.length > 0 ? { readDenyPatterns: readDenyAdditions } : {}),
1334
- ...(readDenyBuiltinCfg.tiers !== undefined ? { readDenyBuiltinTiers: readDenyBuiltinCfg.tiers } : {}),
1335
- ...(readDenyBuiltinCfg.exclude !== undefined ? { readDenyBuiltinExclude: readDenyBuiltinCfg.exclude } : {}),
1336
- readFace: liveReadFace,
1337
- includeShell: handsIncludeShell,
1338
- readOnly: handsReadOnly,
1339
- ...(handsCwdRef ? { cwdRef: handsCwdRef } : {}),
1340
- taskRegistry: defaultTaskRegistry,
1341
- taskOwner: hostTaskId,
1342
- taskScope,
1343
- oneShot: spec.oneShot,
1344
- ...(sessionId !== undefined ? { sessionId } : {}),
1345
- ...(internals?.onTaskNotification !== undefined ? { taskNotification: internals.onTaskNotification } : {}),
1346
- ...(internals?.detachHub !== undefined ? { detachHub: internals.detachHub } : {}),
1347
- ...(deps.onNotice !== undefined ? { onNotice: deps.onNotice } : {}),
1348
- pdfModelCapabilities: pdfModelCapabilitiesOf(model),
1349
- ...(deps.hands?.bashReadonlyAllow !== undefined ? { bashReadonlyAllow: deps.hands.bashReadonlyAllow } : {}),
1350
- ...(deps.hands?.commitCoAuthor !== undefined ? { commitCoAuthor: deps.hands.commitCoAuthor } : {}),
1351
- ...(deps.hands?.readImageDownsampler !== undefined ? { readImageDownsampler: deps.hands.readImageDownsampler } : {}),
1352
- ...(deps.hands?.autoBackgroundOnTimeout !== undefined ? { autoBackgroundOnTimeout: deps.hands.autoBackgroundOnTimeout } : {}),
1353
- ...(deps.hands?.readCyberReminder !== undefined ? { readCyberReminder: deps.hands.readCyberReminder } : {}),
1354
- beforeWrite: async (w) => {
1355
- const deploymentGate = deps.hands?.beforeWrite;
1356
- if (deploymentGate) {
1357
- const verdict = await deploymentGate(w);
1358
- if (verdict !== undefined && verdict.ok === false)
1359
- return verdict;
1360
- }
1361
- return memoryWriteGateRef.current?.(w);
1362
- },
1363
- mountBackgroundTaskTools: false,
1364
- monitorToolActive: backgroundTaskToolsActive && !(toolFaceSnapshot.exclude?.includes("Monitor") ?? false),
1365
- });
1366
- {
1367
- const bandNames = new Set(band.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
1368
- const shadowed = (spec.tools ?? []).filter((t) => [t.name, ...(t.aliases ?? [])].some((n) => bandNames.has(n)));
1369
- if (shadowed.length > 0) {
1370
- const names = shadowed.map((t) => `"${t.name}"`).join(", ");
1371
- deps.onError?.(new Error(`spec.tools ${shadowed.length > 1 ? "entries" : "entry"} ${names} collide${shadowed.length > 1 ? "" : "s"} with the built-in hands tool band — the built-in wins (registered later; the harness tool Map is last-write-wins) and the caller tool never dispatches this task. Rename the caller tool, or don't inject executionEnv if you intend to replace the band.`), { phase: "config", sessionId });
1372
- }
1373
- }
1374
- tools.push(...band.map((t) => firstPartyOffload(t)));
1375
- const shellGate = effectiveShellGate;
1376
- if (shellGate === "off" && !(executionEnv instanceof StubExecutionEnv) && spec.handsReadOnly !== true) {
1377
- deps.onError?.(new Error(`shell gate doctrine is "off" while a real writable shell (Bash) is mounted — no shell safety-axis ` +
1378
- `fold applies to this run (commands are adjudicated by the ordinary policy/hook chain only). ` +
1379
- `Set spec.shellGate to "classify" or "always" if this deployment expects doctrine-gated shell behavior.`), { phase: "config", sessionId, classification: "shell-gate-off" });
1380
- }
1381
- if (shellGate !== "off" && !(executionEnv instanceof StubExecutionEnv) && spec.handsReadOnly !== true) {
1382
- const bashTierBefore = irreversibilityTier.get("Bash");
1383
- shellGatedBash = !egressTools.has("Bash") && bashTierBefore !== "always" && bashTierBefore !== "maybe";
1384
- const bashEffectiveTier = shellGate === "always" || bashTierBefore === "always" ? "always" : "maybe";
1385
- irreversibilityTier.set("Bash", bashEffectiveTier);
1386
- irreversibleTools.add("Bash");
1387
- const shellReadBoundary = () => ({
1388
- roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical],
1389
- ...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}),
1390
- denyMatch: (p) => readDenyMatcher?.matchPath(p)?.pattern ?? null,
1391
- ...(resolvedReadFace !== undefined ? { face: resolvedReadFace } : {}),
1392
- });
1393
- if (shellGate === "classify" && shellGatedBash)
1394
- reversibilityProbes.set("Bash", bashReversibilityProbe(undefined, shellReadBoundary));
1395
- if (backgroundTaskToolsActive) {
1396
- const monitorTierBefore = irreversibilityTier.get("Monitor");
1397
- shellGatedMonitor = !egressTools.has("Monitor") && monitorTierBefore !== "always" && monitorTierBefore !== "maybe";
1398
- const monitorEffectiveTier = shellGate === "always" || monitorTierBefore === "always" ? "always" : "maybe";
1399
- irreversibilityTier.set("Monitor", monitorEffectiveTier);
1400
- irreversibleTools.add("Monitor");
1401
- if (shellGate === "classify" && shellGatedMonitor)
1402
- reversibilityProbes.set("Monitor", bashReversibilityProbe(undefined, shellReadBoundary));
1403
- }
1404
- }
1405
- }
1406
- else {
1407
- const liveFace = resolveReadFace({
1408
- specReadFace: spec.readFace,
1409
- depsReadFace: deps.readFace,
1410
- readOnlyMount: spec.handsReadOnly === true,
1411
- orgGoverned: deps.permissionRuleOrg !== undefined,
1412
- fullShellReachable,
1413
- });
1414
- const seed = resume?.seed.readFace;
1415
- handsLessResolvedFace = resume !== undefined && (seed === undefined || seed.face === "roots") ? "roots" : liveFace;
1416
- const denyAdditions = [...(deps.readDenyPatterns ?? []), ...(spec.readDenyPatterns ?? []), ...(seed?.denyEntries ?? [])];
1417
- resolveReadDenyBuiltins({
1418
- ...(deps.readDenyBuiltinTiers !== undefined ? { tiers: deps.readDenyBuiltinTiers } : {}),
1419
- ...(deps.readDenyBuiltinExclude !== undefined ? { exclude: deps.readDenyBuiltinExclude } : {}),
1420
- });
1421
- if (denyAdditions.length > 0) {
1422
- readDenyAdditionsNormalized = compileReadDeny(denyAdditions, "readDenyPatterns", { tiers: [] }).entries;
1423
- }
1424
- }
1201
+ const handsReadFaceInput = { handsEnabled, executionEnv, taskRootFinal, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals, memoryWriteGateRef, firstPartyOffload, tools, egressTools, irreversibilityTier, irreversibleTools, reversibilityProbes };
1202
+ const handsReadFace = handsEnabled ? await prepareHandsMount(handsReadFaceInput) : resolveHandsLessReadFace(handsReadFaceInput);
1203
+ const { readFileStateForCheckpoint, seedContextFiles, handsCwdRef, workspaceStateSettle, wsSnapshot, rebaseWsPath, backgroundTaskToolsActive, additionalRootsCanonical, additionalReadRootsCanonical, attachmentRootCanonical, readDenyMatcher } = handsReadFace;
1204
+ resolvedReadFace = handsReadFace.resolvedReadFace;
1205
+ readDenyAdditionsNormalized = handsReadFace.readDenyAdditionsNormalized;
1206
+ handsLessResolvedFace = handsReadFace.handsLessResolvedFace;
1207
+ shellGatedBash = handsReadFace.shellGatedBash;
1208
+ shellGatedMonitor = handsReadFace.shellGatedMonitor;
1425
1209
  if (backgroundTaskToolsActive || workflowToolsActive) {
1426
1210
  toolEffects.set("TaskOutput", "read");
1427
1211
  toolEffects.set("TaskStop", "write");
@@ -3753,9 +3537,11 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3753
3537
  return { ok: false, reason };
3754
3538
  }
3755
3539
  };
3756
- const publishCommittedSuspend = (token, gate, scope, remoteHandle) => {
3540
+ const publishCommittedSuspend = (token, gate, scope, remoteHandle, checkpointId) => {
3757
3541
  const ref = gate.kind === "needs_review" || gate.kind === "plan_review" ? reviewRef : suspendRef;
3758
3542
  ref.token = token;
3543
+ if (checkpointId !== undefined)
3544
+ ref.checkpointId = checkpointId;
3759
3545
  ref.gate = gate;
3760
3546
  if (remoteHandle !== undefined)
3761
3547
  ref.restoreMode = remoteHandle.restoreMode === "park_only" ? "park_only" : "snapshot";
@@ -3821,6 +3607,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3821
3607
  const mintedAt = Date.now();
3822
3608
  const cp = {
3823
3609
  token,
3610
+ checkpointId: mintCheckpointId(),
3824
3611
  version: faceCheckpointState() ? FACE_CHECKPOINT_VERSION : f012CheckpointState() ? F012_CHECKPOINT_VERSION : orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : resourceLedgerOut.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : RESOURCE_CHECKPOINT_VERSION,
3825
3612
  scope,
3826
3613
  sessionId,
@@ -3840,7 +3627,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3840
3627
  };
3841
3628
  if (!(await commitSuspendSaga(token, cp, suspendableEnv, remoteHandle)).ok)
3842
3629
  return false;
3843
- publishCommittedSuspend(token, gate, scope, remoteHandle);
3630
+ publishCommittedSuspend(token, gate, scope, remoteHandle, cp.checkpointId);
3844
3631
  try {
3845
3632
  await sessions.pin?.(sessionId);
3846
3633
  }
@@ -3903,6 +3690,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3903
3690
  const mintedAt = Date.now();
3904
3691
  const cp = {
3905
3692
  token,
3693
+ checkpointId: mintCheckpointId(),
3906
3694
  version: faceCheckpointState() ? FACE_CHECKPOINT_VERSION : f012CheckpointState() ? F012_CHECKPOINT_VERSION : orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : reviewLedger.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
3907
3695
  scope,
3908
3696
  sessionId,
@@ -3925,7 +3713,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3925
3713
  };
3926
3714
  if (!(await commitSuspendSaga(token, cp, suspendableEnv, remoteHandle)).ok)
3927
3715
  return false;
3928
- publishCommittedSuspend(token, gate, scope, remoteHandle);
3716
+ publishCommittedSuspend(token, gate, scope, remoteHandle, cp.checkpointId);
3929
3717
  try {
3930
3718
  await sessions.pin?.(sessionId);
3931
3719
  }
@@ -4164,6 +3952,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4164
3952
  const mintedAt = Date.now();
4165
3953
  cp = {
4166
3954
  token,
3955
+ checkpointId: mintCheckpointId(),
4167
3956
  version: faceCheckpointState() ? FACE_CHECKPOINT_VERSION : realApproval !== undefined ? REAL_APPROVAL_CHECKPOINT_VERSION : f012CheckpointState() ? F012_CHECKPOINT_VERSION : orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : approvalLedger.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
4168
3957
  scope,
4169
3958
  sessionId,
@@ -4219,7 +4008,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4219
4008
  if (!committed.ok) {
4220
4009
  return committed.reason !== undefined ? { parkFailed: committed.reason } : undefined;
4221
4010
  }
4222
- publishCommittedSuspend(token, gate, cp.scope, remoteHandle);
4011
+ publishCommittedSuspend(token, gate, cp.scope, remoteHandle, cp.checkpointId);
4223
4012
  try {
4224
4013
  await sessions.pin?.(sessionId);
4225
4014
  }
@@ -1,6 +1,6 @@
1
1
  import { persistedReadDenyEntryProblem } from "../../tools/fs/read-deny.js";
2
2
  import { createSafeNotifier, observeThenableRejection } from "../safe-notify.js";
3
- import { deliverDelegationLifecycle, deliverEngineNotice } from "../types.js";
3
+ import { deliverDelegationLifecycle, deliverEngineNotice, undrainedUserInputNotices } from "../types.js";
4
4
  import { AgentHarness, DEFAULT_COMPACTION_SETTINGS, uuidv7 } from "../../internal/harness.js";
5
5
  import { snapshotActorAssertion } from "../../internal/llm.js";
6
6
  import { CheckpointError, BINDING_CHECKPOINT_VERSION, checkpointVersionOf, F012_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, FACE_CHECKPOINT_VERSION, remainingBudgetMicroUsd, readPendingSteerQueue, remainingTokens, LEGACY_PENDING_STEER_INPUT_ID, MAX_STEER_INPUT_ID_CHARS, validatePendingSteer, winnerFromOutcome, } from "../checkpoint-store.js";
@@ -2069,12 +2069,9 @@ export class Runner {
2069
2069
  this.pendingSessionNotifications.pend(notificationSessionId, p);
2070
2070
  };
2071
2071
  prepared.harness.onUndrainedUserInputs = (counts) => {
2072
- deliverEngineNotice(this.deps.onNotice, {
2073
- code: "task.user_steer_undrained",
2074
- message: `${counts.steer + counts.followUp} user input(s) accepted as "queued" were never consumed — the run ended first ` +
2075
- `(${counts.steer} steer, ${counts.followUp} follow-up). They are NOT redelivered; re-send against a live run if still wanted.`,
2076
- detail: { steer: counts.steer, followUp: counts.followUp, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}) },
2077
- });
2072
+ for (const notice of undrainedUserInputNotices(counts, spec.taskId)) {
2073
+ deliverEngineNotice(this.deps.onNotice, notice);
2074
+ }
2078
2075
  };
2079
2076
  prepared.harness.onEngineNoteConsumed = (p) => {
2080
2077
  const peer = p?.peer;
@@ -3475,6 +3472,7 @@ export class Runner {
3475
3472
  suspendRef: prepared.suspendRef.token !== undefined && prepared.suspendRef.gate !== undefined
3476
3473
  ? {
3477
3474
  token: prepared.suspendRef.token,
3475
+ ...(prepared.suspendRef.checkpointId !== undefined ? { checkpointId: prepared.suspendRef.checkpointId } : {}),
3478
3476
  gate: prepared.suspendRef.gate,
3479
3477
  ...(prepared.suspendRef.restoreMode !== undefined ? { restoreMode: prepared.suspendRef.restoreMode } : {}),
3480
3478
  }
@@ -3482,6 +3480,7 @@ export class Runner {
3482
3480
  reviewRef: prepared.reviewRef.token !== undefined && prepared.reviewRef.gate !== undefined
3483
3481
  ? {
3484
3482
  token: prepared.reviewRef.token,
3483
+ ...(prepared.reviewRef.checkpointId !== undefined ? { checkpointId: prepared.reviewRef.checkpointId } : {}),
3485
3484
  gate: prepared.reviewRef.gate,
3486
3485
  ...(prepared.reviewRef.restoreMode !== undefined ? { restoreMode: prepared.reviewRef.restoreMode } : {}),
3487
3486
  }
@@ -2881,6 +2881,10 @@ export interface TaskResult {
2881
2881
  * the resume capability, §6). Undefined for every other status.
2882
2882
  */
2883
2883
  checkpointToken?: import("./checkpoint-store.js").CheckpointToken;
2884
+ /** The suspend's NON-SECRET stable identity ({@link import("./checkpoint-store.js").Checkpoint.checkpointId}),
2885
+ * set beside `checkpointToken` on the suspended/needs_review terminals — the display/correlation key a
2886
+ * consumer may log or render where the token must not travel. Absent on pre-identity checkpoints. */
2887
+ checkpointId?: string;
2884
2888
  /** Set with `checkpointToken`: who/what must resume (e.g. `{kind:"human", reason, toolName}`), so the
2885
2889
  * caller knows what decision the suspension is awaiting. */
2886
2890
  checkpointGate?: import("./checkpoint-store.js").CheckpointGate;
@@ -4669,12 +4673,15 @@ export interface EngineNotice {
4669
4673
  * value IS in force the prepare refuses with the same code as `TaskResult.errorCode` (one
4670
4674
  * fact, one code, two loudness dialects); `detail: { raw }`.
4671
4675
  *
4672
- * - `"task.user_steer_undrained"` (#259) — user steers/follow-ups whose receipts said "queued"
4673
- * were still in the queues at agent_end: the run ended before any turn could drain them. They
4674
- * are NOT redelivered (a steer aimed at a finished run must not fire at the next one — unlike
4675
- * ENGINE notes, which pend per session); the notice is the loud half of the #257 contract's
4676
- * "accepted = enqueued, not consumed" sentence; `detail: { steer, followUp, taskId? }`.
4677
- * Per-run, at most once (the terminal sweep is a single site).
4676
+ * - `"task.user_steer_undrained"` / `"task.user_followup_undrained"` (#259) — user steers /
4677
+ * follow-ups whose receipts said "queued" were still in their queue at agent_end: the run
4678
+ * ended before any turn could drain them. One notice PER family (a consumer routing on `code`
4679
+ * alone must not mistake a stranded follow-up for a stranded steer same split as the settled
4680
+ * frame's two count keys). They are NOT redelivered (a steer aimed at a finished run must not
4681
+ * fire at the next one unlike ENGINE notes, which pend per session); the loud half of the
4682
+ * #257 contract's "accepted = enqueued, not consumed" sentence;
4683
+ * `detail: { steer, taskId? }` / `{ followUp, taskId? }`. Per-run, at most once per family
4684
+ * (the terminal sweep is a single site).
4678
4685
  *
4679
4686
  * Deliberately NOT a notice family: brain retry/reconnect liveness (a rate limit, a 5xx, a
4680
4687
  * transient network failure being retried). Those are per-attempt liveness frames with their own
@@ -4709,6 +4716,16 @@ export declare function __resetMalformedNoticeSeatAnnouncement(): void;
4709
4716
  * · an ABSENT seat prints the historic `console.warn` line verbatim (byte-compat loudness).
4710
4717
  */
4711
4718
  export declare function deliverEngineNotice(onNotice: ((notice: EngineNotice) => void) | undefined, notice: EngineNotice): void;
4719
+ /**
4720
+ * #259 loud half, one notice PER FAMILY (`task.user_steer_undrained` / `task.user_followup_undrained`):
4721
+ * a consumer routing on `code` alone must never mistake a stranded follow-up for a stranded steer, so
4722
+ * the code carries exactly the semantics its name claims — the same two-key split the settled frame
4723
+ * uses. Pure (the terminal sweep race window is not constructible deterministically; this seam is).
4724
+ */
4725
+ export declare function undrainedUserInputNotices(counts: {
4726
+ steer: number;
4727
+ followUp: number;
4728
+ }, taskId?: string): EngineNotice[];
4712
4729
  /** Runtime dependencies shared across tasks. */
4713
4730
  export interface RunnerDeps {
4714
4731
  brain: Brain;
@@ -49,3 +49,23 @@ export function deliverEngineNotice(onNotice, notice) {
49
49
  }
50
50
  console.warn(notice.message);
51
51
  }
52
+ export function undrainedUserInputNotices(counts, taskId) {
53
+ const tid = taskId !== undefined ? { taskId } : {};
54
+ const tail = `accepted as "queued" were never consumed — the run ended first. They are NOT redelivered; re-send against a live run if still wanted.`;
55
+ const out = [];
56
+ if (counts.steer > 0) {
57
+ out.push({
58
+ code: "task.user_steer_undrained",
59
+ message: `${counts.steer} user steer(s) ${tail}`,
60
+ detail: { steer: counts.steer, ...tid },
61
+ });
62
+ }
63
+ if (counts.followUp > 0) {
64
+ out.push({
65
+ code: "task.user_followup_undrained",
66
+ message: `${counts.followUp} user follow-up(s) ${tail}`,
67
+ detail: { followUp: counts.followUp, ...tid },
68
+ });
69
+ }
70
+ return out;
71
+ }
@@ -1,4 +1,5 @@
1
1
  import { type ExecResult, type ExecutionEnv, ExecutionError, type ExecutionEnvExecOptions, FileError, type FileInfo, type Result } from "../harness/types.js";
2
+ import { type SecretEnvFinding } from "../../core/secret-env.js";
2
3
  import type { BackgroundShellCapability, BackgroundShellId, BackgroundPoll, BackgroundSpawnOptions } from "../../core/background-shell.js";
3
4
  import { BackgroundShellError } from "../../core/background-shell.js";
4
5
  import type { SchedulerCapability, ScheduledIntent, SchedulerContext, ScheduledTaskId, ScheduledTaskSummary } from "../../core/scheduler.js";
@@ -18,6 +19,8 @@ export declare function getShellConfig(customShellPath?: string): Promise<Result
18
19
  shell: string;
19
20
  args: string[];
20
21
  }, ExecutionError>>;
22
+ /** test seam (mirrors the notice-seat announce reset): the once-per-process fallback latches. */
23
+ export declare function __resetSecretScrubAnnouncement(): void;
21
24
  /** RB-235 ([1937] BB2): the spool-reclaim decision, extracted PURE so every rule is unit-pinnable.
22
25
  * Both reclaim sites (foreground `reclaimSpool`, background `syncSpool`) route through this one
23
26
  * function so they cannot drift apart.
@@ -83,6 +86,7 @@ export declare class NodeExecutionEnv implements ExecutionEnv, BackgroundShellCa
83
86
  private readonly bgShells;
84
87
  private bgCounter;
85
88
  private readonly retainBackgroundProcesses;
89
+ private readonly onSecretEnvScrub?;
86
90
  /** design/128 T1-1: lazily-created env-owned spool dir (mkdtemp under os tmp — unpredictable, never the cwd). */
87
91
  private bgSpoolDir?;
88
92
  readonly backgroundCapabilities: BackgroundShellCapability["backgroundCapabilities"];
@@ -108,6 +112,10 @@ export declare class NodeExecutionEnv implements ExecutionEnv, BackgroundShellCa
108
112
  * deployments (their pre-suspend dispose becomes a no-op too — do not combine).
109
113
  */
110
114
  retainBackgroundProcesses?: boolean;
115
+ /** The D1 scrub decision's observation seat: called with every NON-EMPTY finding set the default
116
+ * "scrub" leg withholds from a shell child. Absent ⇒ a once-per-process console summary (loud
117
+ * enough to notice, quiet enough not to spam). Never called on the "all"/allowlist legs. */
118
+ onSecretEnvScrub?: (findings: readonly SecretEnvFinding[]) => void;
111
119
  });
112
120
  get schedulerCapabilities(): SchedulerCapability["schedulerCapabilities"];
113
121
  schedule(intent: ScheduledIntent, ctx: SchedulerContext): Promise<Result<{
@@ -188,7 +188,13 @@ export async function getShellConfig(customShellPath) {
188
188
  return ok({ shell: "sh", args: ["-c"] });
189
189
  }
190
190
  const EXEC_FORCE_SETTLE_GRACE_MS = 3000;
191
- function getShellEnv(inheritEnv, baseEnv, extraEnv) {
191
+ let secretScrubConsoleAnnounced = false;
192
+ let secretScrubSeatDefectAnnounced = false;
193
+ export function __resetSecretScrubAnnouncement() {
194
+ secretScrubConsoleAnnounced = false;
195
+ secretScrubSeatDefectAnnounced = false;
196
+ }
197
+ function getShellEnv(inheritEnv, baseEnv, extraEnv, onScrub) {
192
198
  let inherited;
193
199
  if (inheritEnv === "all") {
194
200
  inherited = process.env;
@@ -200,7 +206,36 @@ function getShellEnv(inheritEnv, baseEnv, extraEnv) {
200
206
  inherited[k] = process.env[k];
201
207
  }
202
208
  else {
203
- inherited = scrubSecretEnv(process.env);
209
+ const scrubbed = [];
210
+ inherited = scrubSecretEnv(process.env, scrubbed);
211
+ const findings = scrubbed.filter((f) => baseEnv?.[f.key] === undefined && extraEnv?.[f.key] === undefined);
212
+ if (findings.length > 0) {
213
+ if (typeof onScrub === "function") {
214
+ try {
215
+ const r = onScrub(findings);
216
+ if (typeof r?.then === "function") {
217
+ r.then(undefined, () => {
218
+ });
219
+ }
220
+ }
221
+ catch {
222
+ }
223
+ }
224
+ else {
225
+ if (onScrub !== undefined && !secretScrubSeatDefectAnnounced) {
226
+ secretScrubSeatDefectAnnounced = true;
227
+ try {
228
+ console.warn(`onSecretEnvScrub seat holds ${onScrub === null ? "null" : typeof onScrub} — not a function; scrub findings fall back to the console summary (once per process)`);
229
+ }
230
+ catch {
231
+ }
232
+ }
233
+ if (!secretScrubConsoleAnnounced) {
234
+ secretScrubConsoleAnnounced = true;
235
+ console.warn(`secret_env_scrubbed: ${findings.length} env key(s) withheld from the shell child (${findings.map((f) => f.key).join(", ")}) — once per process; wire onSecretEnvScrub for per-exec telemetry`);
236
+ }
237
+ }
238
+ }
204
239
  }
205
240
  return {
206
241
  ...inherited,
@@ -254,6 +289,7 @@ export class NodeExecutionEnv {
254
289
  bgShells = new Map();
255
290
  bgCounter = 0;
256
291
  retainBackgroundProcesses;
292
+ onSecretEnvScrub;
257
293
  bgSpoolDir;
258
294
  backgroundCapabilities;
259
295
  constructor(options) {
@@ -263,6 +299,7 @@ export class NodeExecutionEnv {
263
299
  this.inheritEnv = options.inheritEnv ?? "scrub";
264
300
  this.scheduler = options.scheduler;
265
301
  this.retainBackgroundProcesses = options.retainBackgroundProcesses ?? false;
302
+ this.onSecretEnvScrub = options.onSecretEnvScrub;
266
303
  this.backgroundCapabilities = {
267
304
  supported: true,
268
305
  maxConcurrent: 8,
@@ -420,7 +457,7 @@ export class NodeExecutionEnv {
420
457
  child = spawn(shellConfig.value.shell, [...shellConfig.value.args, command], {
421
458
  cwd,
422
459
  detached: process.platform !== "win32",
423
- env: getShellEnv(this.inheritEnv, this.shellEnv, options?.env),
460
+ env: getShellEnv(this.inheritEnv, this.shellEnv, options?.env, this.onSecretEnvScrub),
424
461
  stdio: execSpoolFds ? ["ignore", execSpoolFds[0], execSpoolFds[1]] : ["ignore", "pipe", "pipe"],
425
462
  windowsHide: true,
426
463
  });
@@ -1114,7 +1151,7 @@ export class NodeExecutionEnv {
1114
1151
  child = spawn(shellConfig.value.shell, [...shellConfig.value.args, command], {
1115
1152
  cwd: options?.cwd ? resolvePath(this.cwd, options.cwd) : this.cwd,
1116
1153
  detached: process.platform !== "win32",
1117
- env: getShellEnv(this.inheritEnv, this.shellEnv, options?.env),
1154
+ env: getShellEnv(this.inheritEnv, this.shellEnv, options?.env, this.onSecretEnvScrub),
1118
1155
  stdio: spoolFds ? ["ignore", spoolFds[0], spoolFds[1]] : ["ignore", "pipe", "pipe"],
1119
1156
  windowsHide: true,
1120
1157
  });
package/dist/index.d.ts CHANGED
@@ -93,7 +93,7 @@ export { READ_FACE_DEFAULT_DENY_ENTRIES, READ_FACE_BUILTIN_DENY_TABLE, READ_DENY
93
93
  export { deploymentReadFaceClampNotice, resolveReadFace, type ReadFace, type ReadFaceInputs } from "./tools/fs/index.js";
94
94
  export { WRITE_PROTECTED_DEFAULT_TABLE, resolveWriteProtectedTable, compileWriteProtection, type WriteProtectedEntry, type WriteProtectedRow, type WriteProtectedKind, type WriteProtectedHit, type WriteProtectionMatcher, } from "./core/write-protect.js";
95
95
  export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, type ToolResultProvenance, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, type ToolResultStore, type ToolResultSlice, type ToolResultDeletionReport, } from "./core/tool-result-store.js";
96
- export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, type ActorAssertion, type PendingSteerEntry, type PendingSteerInput, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, type RiskDescriptor, type ProbeCause, type ProbeCauseOperands, type CheckpointStore, type CheckpointSummary, type Checkpoint, type CheckpointToken, type CheckpointGate, type CheckpointState, type SerializedCheckpointState, type CheckpointFaultMode, type PendingAction, type ResumeOutcome, type ResolvedOutcome, type ReopenReason, type ResolveExpectation, type SafetyAxis, type RealApprovalGateBit, type ResourceLedger, type ResourceLimitReason, type PlatformLimitReason, } from "./core/checkpoint-store.js";
96
+ export { InMemoryCheckpointStore, CheckpointError, mintCheckpointId, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, type ActorAssertion, type PendingSteerEntry, type PendingSteerInput, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, type RiskDescriptor, type ProbeCause, type ProbeCauseOperands, type CheckpointStore, type CheckpointSummary, type Checkpoint, type CheckpointToken, type CheckpointGate, type CheckpointState, type SerializedCheckpointState, type CheckpointFaultMode, type PendingAction, type ResumeOutcome, type ResolvedOutcome, type ReopenReason, type ResolveExpectation, type SafetyAxis, type RealApprovalGateBit, type ResourceLedger, type ResourceLimitReason, type PlatformLimitReason, } from "./core/checkpoint-store.js";
97
97
  export { InMemoryUsageWindowStore, GLOBAL_USAGE_KEY, EMPTY_USAGE_WINDOW_RECORD, chargeUsageRecord, readUsageRecord, usageRetryAfterMs, resolveUsageWindows, type UsageWindow, type UsageWindowStore, type UsageWindowReading, type UsageWindowRecord, type UsageSlot, type UsageBucketRow, } from "./core/usage-window-store.js";
98
98
  export { FileUsageWindowStore } from "./stores/file/usage-window-store.js";
99
99
  export { ENV_LIFETIME_SUSPEND_MARGIN_MS, USAGE_WINDOW_REAP_MARGIN_MS } from "./core/runner/prepare-task.js";
package/dist/index.js CHANGED
@@ -73,7 +73,7 @@ export { READ_FACE_DEFAULT_DENY_ENTRIES, READ_FACE_BUILTIN_DENY_TABLE, READ_DENY
73
73
  export { deploymentReadFaceClampNotice, resolveReadFace } from "./tools/fs/index.js";
74
74
  export { WRITE_PROTECTED_DEFAULT_TABLE, resolveWriteProtectedTable, compileWriteProtection, } from "./core/write-protect.js";
75
75
  export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, } from "./core/tool-result-store.js";
76
- export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, } from "./core/checkpoint-store.js";
76
+ export { InMemoryCheckpointStore, CheckpointError, mintCheckpointId, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, } from "./core/checkpoint-store.js";
77
77
  export { InMemoryUsageWindowStore, GLOBAL_USAGE_KEY, EMPTY_USAGE_WINDOW_RECORD, chargeUsageRecord, readUsageRecord, usageRetryAfterMs, resolveUsageWindows, } from "./core/usage-window-store.js";
78
78
  export { FileUsageWindowStore } from "./stores/file/usage-window-store.js";
79
79
  export { ENV_LIFETIME_SUSPEND_MARGIN_MS, USAGE_WINDOW_REAP_MARGIN_MS } from "./core/runner/prepare-task.js";
@@ -2,6 +2,14 @@ import type { AgentTool, ExecutionEnv } from "../../internal/harness-types.js";
2
2
  import type { ToolEffect } from "../../core/types.js";
3
3
  import type { ReadDenyMatcher } from "./read-deny.js";
4
4
  import type { ReadFace } from "./read-face.js";
5
+ /**
6
+ * The Grep card's details assembly, PURE over the engine text ({mode, offset} from the request) —
7
+ * exported so the text→structured mapping is pinnable with synthetic texts (the byte-truncation and
8
+ * fenced-partial shapes are impractical to construct through a live tool call). Follow-up #313 will
9
+ * replace the text parsing with structured rows from runGrep; until then this seam is the honesty
10
+ * boundary.
11
+ */
12
+ export declare function grepDetailFields(text: string, mode: "files_with_matches" | "content" | "count", offset?: number, structuredRows?: readonly import("./search.js").GrepRow[]): Record<string, unknown>;
5
13
  export declare function createGrepTool(env: ExecutionEnv, rootCanonical: string, additionalRoots?: readonly string[], readDeny?: ReadDenyMatcher, readFace?: ReadFace): AgentTool;
6
14
  export declare function createGlobTool(env: ExecutionEnv, rootCanonical: string, additionalRoots?: readonly string[], readDeny?: ReadDenyMatcher, readFace?: ReadFace): AgentTool;
7
15
  /** Static side-effect class of every hand tool, by name (design/44 §3). Used by prepare-task to (a) feed