@sema-agent/core 7.30.1 → 7.31.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 +242 -0
- package/dist/agents/cross-session-judge.d.ts +4 -12
- package/dist/agents/cross-session-judge.js +4 -11
- package/dist/agents/peer-session-drain.js +0 -5
- package/dist/agents/send-message-tool.js +1 -0
- package/dist/agents/subagent.d.ts +1 -0
- package/dist/agents/subagent.js +1 -0
- package/dist/core/ask-origin.d.ts +6 -0
- package/dist/core/ask-origin.js +10 -0
- package/dist/core/checkpoint-store.d.ts +14 -0
- package/dist/core/checkpoint-store.js +1 -0
- package/dist/core/engine-notice.d.ts +4 -0
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +3 -0
- package/dist/core/memory-engine/data-plane.d.ts +3 -1
- package/dist/core/memory-engine/data-plane.js +1 -1
- package/dist/core/memory-engine/delegation-settlement.d.ts +44 -3
- package/dist/core/memory-engine/delegation-settlement.js +38 -3
- package/dist/core/memory-engine/engine.d.ts +123 -26
- package/dist/core/memory-engine/engine.js +426 -149
- package/dist/core/memory-engine/entry-scan.d.ts +23 -0
- package/dist/core/memory-engine/entry-scan.js +55 -0
- package/dist/core/memory-engine/file-backend.d.ts +16 -23
- package/dist/core/memory-engine/file-backend.js +122 -99
- package/dist/core/memory-engine/layer-observations.d.ts +109 -0
- package/dist/core/memory-engine/layer-observations.js +148 -0
- package/dist/core/memory-engine/layout.d.ts +11 -0
- package/dist/core/memory-engine/layout.js +20 -4
- package/dist/core/memory-engine/types.d.ts +26 -2
- package/dist/core/parent-spec-seat.d.ts +4 -1
- package/dist/core/parent-spec-seat.js +2 -0
- package/dist/core/park-selfcheck.js +1 -0
- package/dist/core/runner/announce-once-ledger.d.ts +26 -0
- package/dist/core/runner/announce-once-ledger.js +37 -0
- package/dist/core/runner/contracts.d.ts +22 -5
- package/dist/core/runner/denial-limit-arms.d.ts +2 -2
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +0 -4
- package/dist/core/runner/prepare-acquire-reconcile.js +1 -3
- package/dist/core/runner/prepare-file-history.d.ts +13 -15
- package/dist/core/runner/prepare-file-history.js +57 -43
- package/dist/core/runner/prepare-hands-readface.d.ts +2 -2
- package/dist/core/runner/prepare-listings.d.ts +3 -1
- package/dist/core/runner/prepare-listings.js +2 -2
- package/dist/core/runner/prepare-memory.js +16 -5
- package/dist/core/runner/prepare-park-ask.js +27 -16
- package/dist/core/runner/prepare-prompt-inputs.d.ts +8 -1
- package/dist/core/runner/prepare-prompt-inputs.js +5 -3
- package/dist/core/runner/prepare-task.js +7 -5
- package/dist/core/runner/prepare-wiring-manifest.d.ts +1 -0
- package/dist/core/runner/prepare-wiring-manifest.js +2 -1
- package/dist/core/runner/run-attachment-seats.js +1 -1
- package/dist/core/runner/run-leg.js +61 -52
- package/dist/core/runner/run-settle-and-teardown.js +4 -0
- package/dist/core/runner/run-turn-boundary.js +6 -6
- package/dist/core/runner/turn-attachments.d.ts +6 -6
- package/dist/core/runner-deps.d.ts +3 -1
- package/dist/core/secret-env.d.ts +11 -0
- package/dist/core/secret-env.js +20 -0
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +22 -0
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +17 -8
- package/dist/core/tool-spec.d.ts +1 -1
- package/dist/core/wiring-manifest.d.ts +10 -0
- package/dist/core/wiring-manifest.js +7 -1
- package/dist/engine/execution-env/node-execution-env.d.ts +6 -0
- package/dist/engine/execution-env/node-execution-env.js +15 -17
- package/dist/engine/harness/agent-harness.js +7 -5
- package/dist/engine/lsp/node-lsp-manager.d.ts +4 -0
- package/dist/engine/lsp/node-lsp-manager.js +10 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +7 -1
- package/wire-changes/core@7.31.0.json +705 -0
|
@@ -30,7 +30,7 @@ import { nextHumanInputSeq } from "./steer-admission.js";
|
|
|
30
30
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
31
31
|
import { toolEndBodyFrom, toolResultMsg } from "./tool-end-body.js";
|
|
32
32
|
import { announceListingTruncation } from "../skill-listing-budget.js";
|
|
33
|
-
import { attachmentEnvelopeTags, commitAgentListing, commitSkillsListing, reduceToolEnd, renderAgentListingFrame, renderOrphanedBackgroundTasks, renderSkillsListingFrame, stampWriteAnchor } from "./turn-attachments.js";
|
|
33
|
+
import { attachmentEnvelopeTags, collectDateChange, commitAgentListing, commitSkillsListing, reduceToolEnd, renderAgentListingFrame, renderOrphanedBackgroundTasks, renderSkillsListingFrame, stampWriteAnchor } from "./turn-attachments.js";
|
|
34
34
|
import { flushOwedAnnouncedSnapshot } from "./announce-once-ledger.js";
|
|
35
35
|
import { asThrownError, thrownMarksOf, thrownText } from "../thrown-value.js";
|
|
36
36
|
import { ruleSourcePhrase } from "../rule-denial-sentence.js";
|
|
@@ -235,22 +235,6 @@ export async function runLeg(input) {
|
|
|
235
235
|
...ident(),
|
|
236
236
|
});
|
|
237
237
|
}
|
|
238
|
-
if (resume !== undefined)
|
|
239
|
-
resume.decisionDelivered = true;
|
|
240
|
-
if (gitResumeDelivered !== undefined) {
|
|
241
|
-
const delivered = gitResumeDelivered;
|
|
242
|
-
prepared.gitStatusRef.protectedText = delivered.wrapped;
|
|
243
|
-
prepared.frameReceipts.pending.push({
|
|
244
|
-
source: "git_status",
|
|
245
|
-
text: continuation,
|
|
246
|
-
commit: (entryId) => {
|
|
247
|
-
prepared.gitStatusRef.announced = { kind: delivered.kind, hash: delivered.hash, entryId };
|
|
248
|
-
queue.push({ type: "steering_injected", source: "git_status", preview: GIT_STATUS_ECHO_PREVIEW[delivered.kind], ...ident() });
|
|
249
|
-
rs.attach.attachmentsInjected += 1;
|
|
250
|
-
prepared.gitStatusRef.mirrorOwed = { kind: delivered.kind, hash: delivered.hash, entryId };
|
|
251
|
-
},
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
238
|
const pending = resume.cp.pendingAction;
|
|
255
239
|
const rejectionHalt = pending.kind === "tool_approval" && resume.outcome.gate === "policy_ask"
|
|
256
240
|
? maybeHumanRejectionHalt({ gate: resume.outcome.gateOutcome, isDelegatedChild, toolName: pending.toolName, toolCallId: pending.toolCallId })
|
|
@@ -258,18 +242,36 @@ export async function runLeg(input) {
|
|
|
258
242
|
if (rejectionHalt !== undefined && pending.kind === "tool_approval")
|
|
259
243
|
prepared.batchHaltRef.current = { rejectedToolCallId: pending.toolCallId };
|
|
260
244
|
prepared.undeliveredContinuationRef.current = { parkedSteers: resumeFrames.map(({ entry }) => entry).filter((e) => !withheldSteerIds.has(e.inputId)), orphanTasks };
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
245
|
+
if (!prepared.abortController.signal.aborted) {
|
|
246
|
+
if (resume !== undefined)
|
|
247
|
+
resume.decisionDelivered = true;
|
|
248
|
+
if (gitResumeDelivered !== undefined) {
|
|
249
|
+
const delivered = gitResumeDelivered;
|
|
250
|
+
prepared.gitStatusRef.protectedText = delivered.wrapped;
|
|
251
|
+
prepared.frameReceipts.pending.push({
|
|
252
|
+
source: "git_status",
|
|
253
|
+
text: continuation,
|
|
254
|
+
commit: (entryId) => {
|
|
255
|
+
prepared.gitStatusRef.announced = { kind: delivered.kind, hash: delivered.hash, entryId };
|
|
256
|
+
queue.push({ type: "steering_injected", source: "git_status", preview: GIT_STATUS_ECHO_PREVIEW[delivered.kind], ...ident() });
|
|
257
|
+
rs.attach.attachmentsInjected += 1;
|
|
258
|
+
prepared.gitStatusRef.mirrorOwed = { kind: delivered.kind, hash: delivered.hash, entryId };
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
final = await withBrainSinks(() => prepared.harness.prompt(continuation, {
|
|
263
|
+
engineSegments,
|
|
264
|
+
settleUnansweredToolCalls: rejectionHalt !== undefined ? { halt: rejectionHalt } : {},
|
|
265
|
+
onDelivered: () => {
|
|
266
|
+
prepared.undeliveredContinuationRef.current = { parkedSteers: [], orphanTasks: [] };
|
|
267
|
+
for (const e of appliedEvents)
|
|
268
|
+
queue.push(e);
|
|
269
|
+
},
|
|
270
|
+
}));
|
|
271
|
+
await flushGitMirror();
|
|
272
|
+
if (prepared.gitStatusRef.announced?.pending === true)
|
|
273
|
+
await prepared.gitStatusRef.reassert?.();
|
|
274
|
+
}
|
|
273
275
|
}
|
|
274
276
|
}
|
|
275
277
|
else {
|
|
@@ -339,6 +341,10 @@ export async function runLeg(input) {
|
|
|
339
341
|
}
|
|
340
342
|
}
|
|
341
343
|
const firstFrames = [];
|
|
344
|
+
const legToday = rs.attach.dateState !== undefined ? prepared.dateChange.today() : undefined;
|
|
345
|
+
const dateFrame = legToday !== undefined ? collectDateChange(rs.attach.dateState, legToday) : undefined;
|
|
346
|
+
if (dateFrame !== undefined)
|
|
347
|
+
firstFrames.push({ source: "date_change", body: dateFrame.body, commit: () => void (rs.attach.dateState.announcedDate = legToday), disclose: () => undefined });
|
|
342
348
|
if (rs.attach.attachState !== undefined) {
|
|
343
349
|
const attach = rs.attach.attachState;
|
|
344
350
|
if (rs.attach.agentListingOn && prepared.agentListing !== undefined) {
|
|
@@ -350,6 +356,7 @@ export async function runLeg(input) {
|
|
|
350
356
|
body: frame.text,
|
|
351
357
|
commit: () => {
|
|
352
358
|
commitAgentListing(attach, listing.entries, listing.models);
|
|
359
|
+
prepared.frameReceipts.listingSnapshotOwed = true;
|
|
353
360
|
prepared.announcedListingsRef.agents = listing.entries.map((e) => e.name);
|
|
354
361
|
if (listing.models !== undefined)
|
|
355
362
|
prepared.announcedListingsRef.models = [...listing.models];
|
|
@@ -367,15 +374,16 @@ export async function runLeg(input) {
|
|
|
367
374
|
body: frame.text,
|
|
368
375
|
commit: () => {
|
|
369
376
|
commitSkillsListing(attach, listing.entries);
|
|
377
|
+
prepared.frameReceipts.listingSnapshotOwed = true;
|
|
370
378
|
prepared.announcedListingsRef.skills = listing.entries.map((e) => e.name);
|
|
371
379
|
},
|
|
372
380
|
disclose: () => announceListingTruncation(runner.deps.onNotice, { listing: "skills", total: frame.listed, nameOnly: frame.nameOnly, dropped: frame.dropped, budget: listing.budget, sessionId: prepared.sessionId }),
|
|
373
381
|
});
|
|
374
382
|
}
|
|
375
383
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
384
|
+
}
|
|
385
|
+
if (firstFrames.length > 0) {
|
|
386
|
+
effectiveObjective = `${firstFrames.map((f) => mintSystemReminder(sanitizeUntrustedText(f.body, attachmentEnvelopeTags(f.source)), prepared.reminderMark)).join("\n")}\n${effectiveObjective}`;
|
|
379
387
|
}
|
|
380
388
|
if (composeApprovalPostureDirective(prepared.approvalPosture) !== undefined) {
|
|
381
389
|
const postureDirective = composeApprovalPostureDirective(prepared.approvalPosture, await statedReminderBodiesOf(prepared.session, prepared.reminderMark));
|
|
@@ -451,32 +459,33 @@ export async function runLeg(input) {
|
|
|
451
459
|
f.commit();
|
|
452
460
|
queue.push({ type: "steering_injected", source: f.source, preview: redactThenCut(f.body, 220), ...ident() });
|
|
453
461
|
rs.attach.attachmentsInjected += 1;
|
|
454
|
-
prepared.frameReceipts.listingSnapshotOwed = true;
|
|
455
462
|
f.disclose();
|
|
456
463
|
},
|
|
457
464
|
});
|
|
458
465
|
}
|
|
459
466
|
const enginePrefixChars = effectiveObjective.length - projectedObjective.length;
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
467
|
+
if (!prepared.abortController.signal.aborted) {
|
|
468
|
+
queue.push({
|
|
469
|
+
...buildHumanInputEvent({
|
|
470
|
+
carrier: "objective",
|
|
471
|
+
source: "objective",
|
|
472
|
+
delivery: "applied",
|
|
473
|
+
sessionSeq: nextHumanInputSeq(prepared.harness),
|
|
474
|
+
...(objectiveActor !== undefined ? { actor: objectiveActor } : {}),
|
|
475
|
+
...(objectiveActor?.issuer !== undefined ? { issuer: objectiveActor.issuer } : {}),
|
|
476
|
+
...(spec.principal !== undefined ? { principal: spec.principal } : {}),
|
|
477
|
+
}),
|
|
478
|
+
...ident(),
|
|
479
|
+
});
|
|
480
|
+
final = await withBrainSinks(() => prepared.harness.prompt(effectiveObjective, {
|
|
481
|
+
...(images && images.length > 0 ? { images } : {}),
|
|
482
|
+
...(enginePrefixChars > 0 ? { enginePrefixChars } : {}),
|
|
466
483
|
...(objectiveActor !== undefined ? { actor: objectiveActor } : {}),
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
472
|
-
final = await withBrainSinks(() => prepared.harness.prompt(effectiveObjective, {
|
|
473
|
-
...(images && images.length > 0 ? { images } : {}),
|
|
474
|
-
...(enginePrefixChars > 0 ? { enginePrefixChars } : {}),
|
|
475
|
-
...(objectiveActor !== undefined ? { actor: objectiveActor } : {}),
|
|
476
|
-
}));
|
|
477
|
-
await flushGitMirror();
|
|
478
|
-
if (prepared.gitStatusRef.announced?.pending === true)
|
|
479
|
-
await prepared.gitStatusRef.reassert?.();
|
|
484
|
+
}));
|
|
485
|
+
await flushGitMirror();
|
|
486
|
+
if (prepared.gitStatusRef.announced?.pending === true)
|
|
487
|
+
await prepared.gitStatusRef.reassert?.();
|
|
488
|
+
}
|
|
480
489
|
}
|
|
481
490
|
}
|
|
482
491
|
loopLatch.ended = true;
|
|
@@ -19,6 +19,7 @@ import { resumeDecisionWasNegative } from "./decide-continuation.js";
|
|
|
19
19
|
import { drainForwardedFramesBeforeDone, forwardsSubagentEvents } from "./prepare-run-refs.js";
|
|
20
20
|
import { generatePromptSuggestions } from "./prompt-suggestions.js";
|
|
21
21
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
22
|
+
import { owedDateAnchor } from "./announce-once-ledger.js";
|
|
22
23
|
import { autoRunConsolidationFromRecommendation, autoRunOnRecommendationEffective, scopeHasUnconsumedRecommendation } from "./memory-consolidation-driver.js";
|
|
23
24
|
import { amendTerminal, terminalProjection } from "./terminal-projection.js";
|
|
24
25
|
import { mintCompactedEvent } from "../auto-compaction.js";
|
|
@@ -293,6 +294,9 @@ export async function runSettleAndTeardown(input) {
|
|
|
293
294
|
}
|
|
294
295
|
finally {
|
|
295
296
|
settleOwedUserInput(owedUserInput, !prepared.suspendProgressRef.executedApproved, { taskId: spec.taskId ?? prepared.sessionId, sessionId: prepared.sessionId, runId: prepared.runId }, runner.deps.onNotice);
|
|
297
|
+
const writeDateAnchor = owedDateAnchor(prepared.session, prepared.dateChange, rs.attach.dateState);
|
|
298
|
+
if (writeDateAnchor !== undefined)
|
|
299
|
+
await settleTeardownLeg(writeDateAnchor, "date anchor (settle leg)", (e) => runner.deps.onError?.(e, { phase: "degraded", sessionId: prepared.sessionId }));
|
|
296
300
|
const ownedEnvDying = prepared.ownedEnv !== undefined &&
|
|
297
301
|
hasDestroy(prepared.ownedEnv) &&
|
|
298
302
|
isIsolated(prepared.ownedEnv) &&
|
|
@@ -211,7 +211,7 @@ export function createTurnBoundary(input) {
|
|
|
211
211
|
}
|
|
212
212
|
let boundaryAttachmentBytes = 0;
|
|
213
213
|
let attachmentsPayload;
|
|
214
|
-
const
|
|
214
|
+
const receiptCommits = [];
|
|
215
215
|
if ((rs.attach.dateState !== undefined ||
|
|
216
216
|
rs.attach.instrState !== undefined ||
|
|
217
217
|
rs.attach.sizeGuidelineState !== undefined ||
|
|
@@ -382,7 +382,7 @@ export function createTurnBoundary(input) {
|
|
|
382
382
|
if (agentListingDue && due.some((a) => a.source === "agent_listing")) {
|
|
383
383
|
const listing = prepared.agentListing;
|
|
384
384
|
const frame = renderAgentListingFrame(attachState, listing.entries, listing.toolName, listing.models, listing.budget);
|
|
385
|
-
|
|
385
|
+
receiptCommits.push({
|
|
386
386
|
source: "agent_listing",
|
|
387
387
|
commit: () => {
|
|
388
388
|
commitAgentListing(attachState, listing.entries, listing.models);
|
|
@@ -397,7 +397,7 @@ export function createTurnBoundary(input) {
|
|
|
397
397
|
if (skillsListingDue && due.some((a) => a.source === "skills_listing")) {
|
|
398
398
|
const listing = prepared.skillsListing;
|
|
399
399
|
const frame = renderSkillsListingFrame(attachState, listing.entries, listing.budget);
|
|
400
|
-
|
|
400
|
+
receiptCommits.push({
|
|
401
401
|
source: "skills_listing",
|
|
402
402
|
commit: () => {
|
|
403
403
|
commitSkillsListing(attachState, listing.entries);
|
|
@@ -410,12 +410,12 @@ export function createTurnBoundary(input) {
|
|
|
410
410
|
if (announcedOnceDirty)
|
|
411
411
|
prepared.frameReceipts.listingSnapshotOwed = true;
|
|
412
412
|
}
|
|
413
|
-
if (rs.attach.dateState !== undefined && rs.turn.lastTurnHadToolCalls) {
|
|
413
|
+
if (rs.attach.dateState !== undefined && rs.turn.lastTurnHadToolCalls && !prepared.frameReceipts.pending.some((r) => r.source === "date_change")) {
|
|
414
414
|
const today = prepared.dateChange.today();
|
|
415
415
|
const cand = collectDateChange(rs.attach.dateState, today);
|
|
416
416
|
if (cand !== undefined) {
|
|
417
417
|
due.unshift(cand);
|
|
418
|
-
rs.attach.dateState.announcedDate = today;
|
|
418
|
+
receiptCommits.push({ source: "date_change", commit: () => void (rs.attach.dateState.announcedDate = today) });
|
|
419
419
|
}
|
|
420
420
|
}
|
|
421
421
|
if (rs.attach.sizeGuidelineState !== undefined && rs.turn.lastTurnHadToolCalls) {
|
|
@@ -496,7 +496,7 @@ export function createTurnBoundary(input) {
|
|
|
496
496
|
}
|
|
497
497
|
if ((attachmentsPayload !== undefined || batchContextBlock !== undefined) && prepared.batchHaltRef.current === undefined) {
|
|
498
498
|
const payload = attachmentsPayload !== undefined && batchContextBlock !== undefined ? `${attachmentsPayload}\n${batchContextBlock}` : (attachmentsPayload ?? batchContextBlock);
|
|
499
|
-
for (const c of
|
|
499
|
+
for (const c of receiptCommits)
|
|
500
500
|
prepared.frameReceipts.pending.push({ source: c.source, text: payload, commit: c.commit });
|
|
501
501
|
void prepared.harness.steer(payload, { engineMinted: true }).catch(() => {
|
|
502
502
|
prepared.frameReceipts.pending = prepared.frameReceipts.pending.filter((r) => r.text !== payload);
|
|
@@ -309,10 +309,10 @@ export interface AttachmentState {
|
|
|
309
309
|
* A1-R — the date_change lane's OWN state, deliberately NOT an
|
|
310
310
|
* {@link AttachmentState} member: allocating that for a date-only run would activate the cadence
|
|
311
311
|
* content scan + projection reducers on every non-opted run (the reopen's exact finding). Two
|
|
312
|
-
* fields, zero reducers. `announcedDate` starts at the
|
|
313
|
-
* commits it (the producer below is pure)
|
|
314
|
-
* fixed ~140-byte item, so no clipping face exists
|
|
315
|
-
*
|
|
312
|
+
* fields, zero reducers. `announcedDate` starts at the session anchor's announced date; the RUN LOOP
|
|
313
|
+
* commits it (the producer below is pure) on the carrying message's receipt — the date block rides ABOVE
|
|
314
|
+
* the 8KB bundle cap as a fixed ~140-byte item, so no clipping face exists (contrast tools_delta's
|
|
315
|
+
* drain-on-intact, which lives inside the capped bundle).
|
|
316
316
|
*/
|
|
317
317
|
export interface DateChangeState {
|
|
318
318
|
announcedDate: string;
|
|
@@ -663,11 +663,11 @@ export declare function renderToolSearchUsageReminder(undiscovered: readonly str
|
|
|
663
663
|
export declare function renderDateChange(newDate: string): string;
|
|
664
664
|
/**
|
|
665
665
|
* A1-R — the date_change producer (CC attachments.ts:1400-1443 parity): the system-prefix
|
|
666
|
-
* date is FROZEN for the
|
|
666
|
+
* date is FROZEN for the session segment (upstream: "cached for the duration of the conversation" — re-rendering
|
|
667
667
|
* the prefix at midnight costs ~920K effective tokens per overnight session, upstream's own comment);
|
|
668
668
|
* when the boundary-time LOCAL date walks away from the last announced one, the model is told at the
|
|
669
669
|
* TAIL instead. String INEQUALITY (backward NTP jumps announce too). PURE — the caller commits
|
|
670
|
-
* `state.announcedDate = today` when
|
|
670
|
+
* `state.announcedDate = today` when the carrying message's receipt settles (see {@link DateChangeState}).
|
|
671
671
|
*/
|
|
672
672
|
export declare function collectDateChange(state: DateChangeState, today: string): TurnAttachment | undefined;
|
|
673
673
|
/**
|
|
@@ -520,7 +520,7 @@ export interface RunnerDeps {
|
|
|
520
520
|
/**
|
|
521
521
|
* design/381 — the per-edited-file rewind history backend (CC FileHistory topology; replaces the
|
|
522
522
|
* E19 whole-tree `fileSnapshotStore`). WIRING IS THE OPT-IN (DV-9, the reference's own SDK
|
|
523
|
-
* posture): with a store present, every Edit/Write/NotebookEdit first-touch is backed up before
|
|
523
|
+
* posture): with a store present (and {@link fileHistoryCapture} not `"off"`), every Edit/Write/NotebookEdit first-touch is backed up before
|
|
524
524
|
* the byte changes, every turn mints a history boundary keyed by its own initial session entry
|
|
525
525
|
* ("file state when this turn began"), and a {@link TaskSpec.resumeAt}+{@link TaskSpec.restoreFiles}
|
|
526
526
|
* (or {@link TaskSpec.rewindFilesTo}) run converges the tracked set back to a boundary. Omitted ⇒
|
|
@@ -534,6 +534,8 @@ export interface RunnerDeps {
|
|
|
534
534
|
* key earns on a filesystem-capable run (`classification: "file-history-no-store"`).
|
|
535
535
|
*/
|
|
536
536
|
fileHistoryStore?: import("./file-history-store.js").FileHistoryStore | "disabled";
|
|
537
|
+
/** The deployment's CAPTURE switch, apart from the store seat: `"off"` (the only word, and only beside a wired store; anything else refuses prepare, `config.invalid_file_history_capture`) ⇒ no run records — no first-touch record, no turn boundary, no fork carry, no liveness advisory, manifest `fileHistory: "disabled"` — while a wired {@link fileHistoryStore} still serves {@link TaskSpec.restoreFiles} / {@link TaskSpec.rewindFilesTo} from what it already holds. */
|
|
538
|
+
fileHistoryCapture?: "off";
|
|
537
539
|
/**
|
|
538
540
|
* design/381 DV-14 (终审落锤) — what an Edit/Write/NotebookEdit does when its FIRST-TOUCH history
|
|
539
541
|
* record cannot be persisted (`FileHistoryStore.trackEdit` answered `{ok:false}` = NO durable
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* per-adapter concern (each exec env owns how it composes its child env); only the secret PATTERN is shared.
|
|
18
18
|
*/
|
|
19
19
|
import type { RedactionConfidence, RedactionSource } from "./arg-summary.js";
|
|
20
|
+
import { type EngineNotice } from "./engine-notice.js";
|
|
20
21
|
/** Which rule classified a key as secret-shaped — the env arm's `kind` vocabulary. */
|
|
21
22
|
export type SecretEnvFindingKind = "suffix-rule" | "exact-name";
|
|
22
23
|
/** One env entry the `scrub` policy dropped: the key (the VALUE is never carried — this record is meant
|
|
@@ -42,3 +43,13 @@ export declare function isSecretEnvKey(key: string): boolean;
|
|
|
42
43
|
* dropped and why, read it back after the call. Omitting it leaves this function byte-for-byte what it
|
|
43
44
|
* was before the channel existed. */
|
|
44
45
|
export declare function scrubSecretEnv(env: NodeJS.ProcessEnv, findings?: SecretEnvFinding[]): NodeJS.ProcessEnv;
|
|
46
|
+
/** Test seam: never called by production code. */
|
|
47
|
+
export declare function __resetSecretEnvScrubNoticeAnnouncement(): void;
|
|
48
|
+
/**
|
|
49
|
+
* The ONE disclosure point for a scrub's withheld keys. Routed through {@link deliverEngineNotice} — the
|
|
50
|
+
* engine's one notice throat — so a wired `onNotice` seat REPLACES the console line exactly as every
|
|
51
|
+
* other `EngineNotice` does, and an absent seat prints the same words `console.warn` always printed here.
|
|
52
|
+
* `detail.names` carries every withheld key. Once per PROCESS, never per call or per site; an EMPTY
|
|
53
|
+
* findings array is a no-op.
|
|
54
|
+
*/
|
|
55
|
+
export declare function announceSecretEnvScrub(onNotice: ((notice: EngineNotice) => void) | undefined, findings: readonly SecretEnvFinding[]): void;
|
package/dist/core/secret-env.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { deliverEngineNotice } from "./engine-notice.js";
|
|
1
2
|
const TOKEN_COUNT_WORDS = ["MAX", "OUTPUT", "INPUT", "THINKING", "NUM", "TOTAL"];
|
|
2
3
|
const SECRET_ENV_RE = new RegExp(`(^|_)(KEY|KEYS|TOKEN|(?<!(?:^|_)(?:${TOKEN_COUNT_WORDS.join("|")})_)TOKENS|SECRET|SECRETS|PASSWORD|PASSWORDS|PASSWD|CREDENTIAL|CREDENTIALS|PRIVATEKEY|APIKEY|ACCESS_KEY|SESSION_TOKEN)(?:_(?:\\d+|ID))?$`, "i");
|
|
3
4
|
const SECRET_ENV_EXACT_NAMES = new Set([
|
|
@@ -7,6 +8,10 @@ const SECRET_ENV_EXACT_NAMES = new Set([
|
|
|
7
8
|
]);
|
|
8
9
|
const NON_SECRET_ENV_EXACT_NAMES = new Set([
|
|
9
10
|
"PARTITION_KEY", "SORT_KEY", "RANGE_KEY", "IDEMPOTENCY_KEY", "CACHE_KEY", "ROUTING_KEY",
|
|
11
|
+
"DB_FOREIGN_KEYS", "JSON_SORT_KEYS",
|
|
12
|
+
"MAX_NEW_TOKENS", "MAX_COMPLETION_TOKENS", "AIDER_MAP_TOKENS", "AIDER_MAX_CHAT_HISTORY_TOKENS",
|
|
13
|
+
"MAX_BATCH_PREFILL_TOKENS", "MAX_WAITING_TOKENS", "MAX_TOP_N_TOKENS", "RATE_LIMIT_TOKENS",
|
|
14
|
+
"PROMPT_TOKENS", "CONTEXT_TOKENS",
|
|
10
15
|
]);
|
|
11
16
|
const SECRET_ENV_KIND_CONFIDENCE = {
|
|
12
17
|
"exact-name": "high",
|
|
@@ -45,3 +50,18 @@ export function scrubSecretEnv(env, findings) {
|
|
|
45
50
|
}
|
|
46
51
|
return out;
|
|
47
52
|
}
|
|
53
|
+
let secretEnvScrubNoticeAnnounced = false;
|
|
54
|
+
export function __resetSecretEnvScrubNoticeAnnouncement() {
|
|
55
|
+
secretEnvScrubNoticeAnnounced = false;
|
|
56
|
+
}
|
|
57
|
+
export function announceSecretEnvScrub(onNotice, findings) {
|
|
58
|
+
if (findings.length === 0 || secretEnvScrubNoticeAnnounced)
|
|
59
|
+
return;
|
|
60
|
+
secretEnvScrubNoticeAnnounced = true;
|
|
61
|
+
const names = findings.map((f) => f.key);
|
|
62
|
+
deliverEngineNotice(onNotice, {
|
|
63
|
+
code: "config.secret_env_scrubbed",
|
|
64
|
+
message: `secret_env_scrubbed: ${findings.length} env key(s) withheld from a spawned child's environment (${names.join(", ")}) — once per process; wire onNotice for the structured form`,
|
|
65
|
+
detail: { names },
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -50,6 +50,7 @@ export declare const EXPECTED_LISTBYSCOPE_SUMMARIES: ({
|
|
|
50
50
|
toolCallId: string;
|
|
51
51
|
toolName: string;
|
|
52
52
|
toolInput: string;
|
|
53
|
+
mandated: boolean;
|
|
53
54
|
origin: string;
|
|
54
55
|
spentMicroUsd?: undefined;
|
|
55
56
|
} | {
|
|
@@ -66,5 +67,6 @@ export declare const EXPECTED_LISTBYSCOPE_SUMMARIES: ({
|
|
|
66
67
|
toolCallId?: undefined;
|
|
67
68
|
toolName?: undefined;
|
|
68
69
|
toolInput?: undefined;
|
|
70
|
+
mandated?: undefined;
|
|
69
71
|
origin?: undefined;
|
|
70
72
|
})[];
|
|
@@ -343,6 +343,26 @@ export async function checkpointStoreContract(make, runAssertion) {
|
|
|
343
343
|
assert.equal("previewWithheld" in byId.get("pw-absent"), false, "absent must project the key OMITTED, not null/empty");
|
|
344
344
|
assert.equal("previewWithheld" in byId.get("pw-bogus"), false, "an out-of-contract spelling must read as absent, never project verbatim");
|
|
345
345
|
});
|
|
346
|
+
run("#1079 mandated presence bit: a row's `true` survives + projects; absent stays absent; an out-of-contract `false` or string reads as absent", async () => {
|
|
347
|
+
const store = make();
|
|
348
|
+
const asserted = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "md-true" });
|
|
349
|
+
asserted.pendingAction.mandated = true;
|
|
350
|
+
const absent = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "md-absent" });
|
|
351
|
+
const falsed = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "md-false" });
|
|
352
|
+
falsed.pendingAction.mandated = false;
|
|
353
|
+
const stringed = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "md-string" });
|
|
354
|
+
stringed.pendingAction.mandated = "yes";
|
|
355
|
+
for (const cp of [asserted, absent, falsed, stringed])
|
|
356
|
+
await store.put(cp.token, cp);
|
|
357
|
+
const back = (await store.get(asserted.token)).pendingAction;
|
|
358
|
+
assert.equal(back.mandated, true, "the row's mandated bit must survive the round-trip verbatim");
|
|
359
|
+
assert.equal("mandated" in (await store.get(absent.token)).pendingAction, false, "an absent bit must stay OMITTED on the row");
|
|
360
|
+
const byId = new Map((await store.listByScope("tenant-a")).map((s) => [s.sessionId, s]));
|
|
361
|
+
assert.equal(byId.get("md-true")?.mandated, true);
|
|
362
|
+
assert.equal("mandated" in byId.get("md-absent"), false, "absent must project the key OMITTED, not null/false");
|
|
363
|
+
assert.equal("mandated" in byId.get("md-false"), false, "a written false is out-of-contract and must read as absent, never project");
|
|
364
|
+
assert.equal("mandated" in byId.get("md-string"), false, "a non-boolean spelling must read as absent, never project verbatim");
|
|
365
|
+
});
|
|
346
366
|
await settle();
|
|
347
367
|
}
|
|
348
368
|
const sortByToken = (a, b) => a.token.localeCompare(b.token);
|
|
@@ -367,6 +387,7 @@ export async function checkpointListByScopeSummaries(store) {
|
|
|
367
387
|
pendingAction: {
|
|
368
388
|
kind: "tool_approval",
|
|
369
389
|
origin: "policy",
|
|
390
|
+
mandated: true,
|
|
370
391
|
toolCallId: "call-pr",
|
|
371
392
|
toolName: "open_pr",
|
|
372
393
|
args: { repo: "x" },
|
|
@@ -406,6 +427,7 @@ export const EXPECTED_LISTBYSCOPE_SUMMARIES = [
|
|
|
406
427
|
toolCallId: "call-pr",
|
|
407
428
|
toolName: "open_pr",
|
|
408
429
|
toolInput: '{"repo":"x"}',
|
|
430
|
+
mandated: true,
|
|
409
431
|
origin: "policy",
|
|
410
432
|
},
|
|
411
433
|
{
|
|
@@ -402,7 +402,7 @@ export interface TaskResult {
|
|
|
402
402
|
* ignores the field is unchanged. A rewind that could NOT be delivered as asked is NOT a note — it is a
|
|
403
403
|
* terminal failure (`rewind_snapshot.unresolvable` / `rewind.store_unconfigured` /
|
|
404
404
|
* `rewind.restore_failed` / `rewind.invalid_spec` / `rewind.rewind_files_retired` /
|
|
405
|
-
* `rewind.conflicting_targets` / `rewind.child_scope_unsupported`).
|
|
405
|
+
* `rewind.conflicting_targets` / `rewind.child_scope_unsupported` / `rewind.capture_disabled`).
|
|
406
406
|
*
|
|
407
407
|
* - `conversation_only` — {@link TaskSpec.resumeAt} branched the transcript WITHOUT
|
|
408
408
|
* {@link TaskSpec.restoreFiles}, so the working tree was deliberately left where it was (CC's
|
package/dist/core/task-spec.d.ts
CHANGED
|
@@ -213,12 +213,12 @@ export interface TaskSpec {
|
|
|
213
213
|
* that could support this without a second root is a recorded design candidate, not built.)
|
|
214
214
|
*
|
|
215
215
|
* File-history interaction (design/381): with {@link restoreFiles}, the `"before"` file anchor is the
|
|
216
|
-
* target's OWN entry id
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
216
|
+
* target's OWN entry id — history boundaries are keyed by a turn's initial entry and mean "file state
|
|
217
|
+
* when that turn began", so B(target) IS the before-state. A target with no boundary of its own (a
|
|
218
|
+
* steer / follow-up entry, a turn that recorded no history, a mixed-epoch session) refuses at prepare
|
|
219
|
+
* (`rewind_snapshot.unresolvable`) with the files untouched: an earlier boundary would also revert edits
|
|
220
|
+
* the conversation keeps, so the anchor never falls back to an ancestor and never crosses into an
|
|
221
|
+
* earlier turn.
|
|
222
222
|
* Fail-loud: setting this without {@link resumeAt}, or an unknown mode value, rejects at prepare.
|
|
223
223
|
*/
|
|
224
224
|
resumeAtMode?: "at" | "before";
|
|
@@ -311,6 +311,15 @@ export interface TaskSpec {
|
|
|
311
311
|
count?: number;
|
|
312
312
|
role?: ModelRole;
|
|
313
313
|
};
|
|
314
|
+
/**
|
|
315
|
+
* The per-run OFF SWITCH for file history (`"disabled"`, the only word; `checkpointStore`'s spelling). Omitted ⇒
|
|
316
|
+
* the deployment (`RunnerDeps.fileHistoryStore` / `fileHistoryCapture`) decides. `"disabled"` ⇒ this run makes no history-store call: no first-touch record
|
|
317
|
+
* (the edit proceeds), no turn boundary, no fork carry, no liveness advisory; a restore request ({@link restoreFiles}
|
|
318
|
+
* / {@link rewindFilesTo}) refuses `rewind.capture_disabled` (the store may be wired; the request turned it off).
|
|
319
|
+
* The manifest reports `fileHistory: "disabled"`. Children inherit it (parent spec seat; a retained child woken by
|
|
320
|
+
* such a run, for that cycle); no word turns it back on below. Any other value refuses (`rewind.invalid_spec`).
|
|
321
|
+
*/
|
|
322
|
+
fileHistory?: "disabled";
|
|
314
323
|
/**
|
|
315
324
|
* design/381 (DV-9) — the RESTORE half of file rewind: when this task uses {@link resumeAt} to branch at
|
|
316
325
|
* a prior entry, ALSO converge the working tree's **agent-edited files** (the tracked set — files this
|
|
@@ -323,8 +332,8 @@ export interface TaskSpec {
|
|
|
323
332
|
* Fail-loud: setting this without {@link resumeAt} rejects at prepare (`rewind.invalid_spec`) — a
|
|
324
333
|
* restore with no anchor is a contradiction, not a default.
|
|
325
334
|
*
|
|
326
|
-
* There is NO capture
|
|
327
|
-
* `RunnerDeps.
|
|
335
|
+
* There is NO capture request anymore: first-touch tracking is ON whenever a `RunnerDeps.fileHistoryStore`
|
|
336
|
+
* is wired, `RunnerDeps.fileHistoryCapture` is not `"off"`, and no {@link fileHistory} off switch is set (per-edited-file backups are near-zero cost — no tree walk,
|
|
328
337
|
* no size gate), which is what retired the old `rewindFiles` capture request. The retired
|
|
329
338
|
* `rewindFiles` spelling is handled at prepare: its capture sense (true, no `resumeAt`) is a
|
|
330
339
|
* tolerated no-op with a deprecation disclosure (the intent is structurally satisfied); its restore
|
package/dist/core/tool-spec.d.ts
CHANGED
|
@@ -752,7 +752,7 @@ export interface ToolExecuteContext {
|
|
|
752
752
|
parentCwd?: string;
|
|
753
753
|
/**
|
|
754
754
|
* The RUNNING task's file-history LINEAGE (Runner-filled, read-only, never a model/tool argument;
|
|
755
|
-
* present iff a `RunnerDeps.fileHistoryStore`
|
|
755
|
+
* present iff the run records file history — a `RunnerDeps.fileHistoryStore` wired, capture not `"off"`, not `"disabled"`): the history scope this run
|
|
756
756
|
* records its first-touch edits into and the TREE those records' keys are minted against — the
|
|
757
757
|
* canonical root spelling plus the env's filesystem identity (`fs`: a remote workspace's handle,
|
|
758
758
|
* `"host"` for the control-plane host's filesystem, else a per-env-instance token).
|
|
@@ -407,6 +407,14 @@ export interface WiringManifest {
|
|
|
407
407
|
autoConsolidation?: {
|
|
408
408
|
onRecommendation: true;
|
|
409
409
|
};
|
|
410
|
+
/**
|
|
411
|
+
* EFFECTIVE half only, ALWAYS present on an engine-minted effective manifest: does this leg capture file history.
|
|
412
|
+
* `"on"` — it records (store wired, neither layer declared it off, a filesystem env mounted). `"disabled"` — it does
|
|
413
|
+
* not, whichever layer or missing half made it so (`TaskSpec.fileHistory`, `RunnerDeps.fileHistoryStore: "disabled"`, `RunnerDeps.fileHistoryCapture: "off"`,
|
|
414
|
+
* no store, no filesystem env); the host knows its own deployment seat. ABSENT = an older mint or an external
|
|
415
|
+
* derivation, never a state. Out of {@link configFingerprint}: the request word is a per-task seat (`hooks` reasoning).
|
|
416
|
+
*/
|
|
417
|
+
fileHistory?: "on" | "disabled";
|
|
410
418
|
/** EFFECTIVE half only — a short, non-sensitive fingerprint (sha256 prefix over the canonical
|
|
411
419
|
* JSON of this manifest's own resolved facts; every field here is an enum/boolean/count, no
|
|
412
420
|
* secrets) so an operator can correlate legs that ran under the same resolved assembly. */
|
|
@@ -498,6 +506,8 @@ export interface WiringFacts {
|
|
|
498
506
|
/** Effective half only — `RunnerDeps.autoRunOnRecommendation === true`
|
|
499
507
|
* ({@link WiringManifest.autoConsolidation}); false ⇒ no section (byte-identical manifest). */
|
|
500
508
|
autoConsolidationOnRecommendation?: boolean;
|
|
509
|
+
/** Effective half only — the file-history phase's word ({@link WiringManifest.fileHistory}); the engine always passes it. */
|
|
510
|
+
fileHistory?: "on" | "disabled";
|
|
501
511
|
}
|
|
502
512
|
/** Named view of the deps seats the static half reads (a `Pick` of the real {@link RunnerDeps} —
|
|
503
513
|
* single-source shapes, no parallel hand-copied interface). */
|
|
@@ -84,6 +84,11 @@ function readAutoModeFact(fact) {
|
|
|
84
84
|
}
|
|
85
85
|
return { armed, reason, ...(deniedSource !== undefined ? { deniedSource } : {}) };
|
|
86
86
|
}
|
|
87
|
+
function readFileHistoryFact(word) {
|
|
88
|
+
if (word !== "on" && word !== "disabled")
|
|
89
|
+
throw new Error(`the file-history wiring fact ${JSON.stringify(word)} is not one of on|disabled`);
|
|
90
|
+
return word;
|
|
91
|
+
}
|
|
87
92
|
export function deriveWiringManifest(facts) {
|
|
88
93
|
if (facts.half === "static" && facts.leg !== undefined) {
|
|
89
94
|
throw new Error("a static wiring manifest has no leg — leg identity is an effective-half fact");
|
|
@@ -146,13 +151,14 @@ export function deriveWiringManifest(facts) {
|
|
|
146
151
|
? { readDeny: { builtinTiers: [...facts.readDenyBuiltinTiersActive] } }
|
|
147
152
|
: {}),
|
|
148
153
|
...(facts.half === "effective" && facts.autoConsolidationOnRecommendation === true ? { autoConsolidation: { onRecommendation: true } } : {}),
|
|
154
|
+
...(facts.half === "effective" && facts.fileHistory !== undefined ? { fileHistory: readFileHistoryFact(facts.fileHistory) } : {}),
|
|
149
155
|
...(facts.half === "effective" && facts.teamRole !== undefined ? { teamRole: facts.teamRole } : {}),
|
|
150
156
|
...(facts.half === "effective" && facts.peerPresence !== undefined && (facts.peerPresence.agent !== undefined || facts.peerPresence.intent !== undefined)
|
|
151
157
|
? { peerPresence: { ...(facts.peerPresence.agent !== undefined ? { agent: facts.peerPresence.agent } : {}), ...(facts.peerPresence.intent !== undefined ? { intent: facts.peerPresence.intent } : {}) } }
|
|
152
158
|
: {}),
|
|
153
159
|
};
|
|
154
160
|
if (facts.half === "effective") {
|
|
155
|
-
const { leg: _leg, mcp: _mcp, tools: _tools, lsp: _lsp, hooks: _hooks, teamRole: _teamRole, peerPresence: _peerPresence, ...assembly } = manifest;
|
|
161
|
+
const { leg: _leg, mcp: _mcp, tools: _tools, lsp: _lsp, hooks: _hooks, teamRole: _teamRole, peerPresence: _peerPresence, fileHistory: _fileHistory, ...assembly } = manifest;
|
|
156
162
|
const { provenance: _askSeat, ...askForHash } = assembly.ask;
|
|
157
163
|
const { provenance: _questionSeat, ...questionForHash } = assembly.question;
|
|
158
164
|
manifest.configFingerprint = createHash("sha256")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ExecResult, type ExecutionEnv, ExecutionError, type ExecutionEnvExecOptions, FileError, type FileInfo, type Result } from "../harness/types.js";
|
|
2
2
|
import { type SecretEnvFinding } from "../../core/secret-env.js";
|
|
3
|
+
import type { EngineNotice } from "../../core/engine-notice.js";
|
|
3
4
|
import type { SymlinkChain } from "../harness/types.js";
|
|
4
5
|
import type { BackgroundShellCapability, BackgroundShellId, BackgroundPoll, BackgroundSpawnOptions } from "../../core/background-shell.js";
|
|
5
6
|
import { BackgroundShellError } from "../../core/background-shell.js";
|
|
@@ -106,6 +107,7 @@ export declare class NodeExecutionEnv implements ExecutionEnv, BackgroundShellCa
|
|
|
106
107
|
private bgCounter;
|
|
107
108
|
private readonly retainBackgroundProcesses;
|
|
108
109
|
private readonly onSecretEnvScrub?;
|
|
110
|
+
private readonly onNotice?;
|
|
109
111
|
/** design/128 T1-1: lazily-created env-owned spool dir (mkdtemp under os tmp — unpredictable, never the cwd). */
|
|
110
112
|
private bgSpoolDir?;
|
|
111
113
|
readonly backgroundCapabilities: BackgroundShellCapability["backgroundCapabilities"];
|
|
@@ -135,6 +137,10 @@ export declare class NodeExecutionEnv implements ExecutionEnv, BackgroundShellCa
|
|
|
135
137
|
* "scrub" leg withholds from a shell child. Absent ⇒ a once-per-process console summary (loud
|
|
136
138
|
* enough to notice, quiet enough not to spam). Never called on the "all"/allowlist legs. */
|
|
137
139
|
onSecretEnvScrub?: (findings: readonly SecretEnvFinding[]) => void;
|
|
140
|
+
/** #1081 — the structured-notice seat for the SAME withheld-keys disclosure (`EngineNotice`,
|
|
141
|
+
* `code: "config.secret_env_scrubbed"`, `detail.names`); independent of `onSecretEnvScrub` (a raw
|
|
142
|
+
* findings collector) except: that seat wired alone stays console-silent, as before this existed. */
|
|
143
|
+
onNotice?: (notice: EngineNotice) => void;
|
|
138
144
|
});
|
|
139
145
|
get schedulerCapabilities(): SchedulerCapability["schedulerCapabilities"];
|
|
140
146
|
schedule(intent: ScheduledIntent, ctx: SchedulerContext): Promise<Result<{
|