@sema-agent/core 7.12.0 → 7.14.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.
Files changed (122) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/dist/agents/subagent.d.ts +4 -9
  3. package/dist/agents/subagent.js +16 -12
  4. package/dist/agents/teacher.js +1 -1
  5. package/dist/brain/reasoning.d.ts +23 -0
  6. package/dist/brain/reasoning.js +35 -4
  7. package/dist/brain/stream-engine.js +35 -17
  8. package/dist/brain/timeout.d.ts +25 -4
  9. package/dist/brain/timeout.js +1 -1
  10. package/dist/core/ask-origin.d.ts +83 -9
  11. package/dist/core/ask-origin.js +31 -5
  12. package/dist/core/auto-mode.d.ts +3 -3
  13. package/dist/core/checkpoint-seat.d.ts +18 -0
  14. package/dist/core/checkpoint-seat.js +6 -0
  15. package/dist/core/checkpoint-store.d.ts +9 -16
  16. package/dist/core/checkpoint-store.js +7 -4
  17. package/dist/core/engine-notice.d.ts +21 -1
  18. package/dist/core/env-knob-announce.d.ts +16 -0
  19. package/dist/core/env-knob-announce.js +14 -0
  20. package/dist/core/gate-fold.js +2 -0
  21. package/dist/core/gate-lanes.d.ts +0 -40
  22. package/dist/core/gate-lanes.js +24 -29
  23. package/dist/core/gate-outcome.d.ts +42 -3
  24. package/dist/core/gate-outcome.js +22 -0
  25. package/dist/core/governance-codes.d.ts +1 -1
  26. package/dist/core/governance-codes.js +3 -1
  27. package/dist/core/hooks.d.ts +6 -17
  28. package/dist/core/hooks.js +7 -5
  29. package/dist/core/mcp.d.ts +3 -2
  30. package/dist/core/mcp.js +5 -11
  31. package/dist/core/memory-engine/engine.d.ts +2 -2
  32. package/dist/core/runner/advertised-writable-dirs.d.ts +74 -0
  33. package/dist/core/runner/advertised-writable-dirs.js +48 -0
  34. package/dist/core/runner/assemble-result.d.ts +50 -1
  35. package/dist/core/runner/assemble-result.js +4 -1
  36. package/dist/core/runner/compaction-seams.d.ts +42 -0
  37. package/dist/core/runner/compaction-seams.js +80 -0
  38. package/dist/core/runner/contracts.d.ts +44 -6
  39. package/dist/core/runner/denial-limit-arms.d.ts +7 -9
  40. package/dist/core/runner/denial-limit-arms.js +11 -12
  41. package/dist/core/runner/gate-exit.d.ts +15 -3
  42. package/dist/core/runner/gate-exit.js +6 -4
  43. package/dist/core/runner/permission-rule-lanes.d.ts +2 -1
  44. package/dist/core/runner/permission-rule-lanes.js +2 -1
  45. package/dist/core/runner/prepare-caps-and-workflow.d.ts +34 -4
  46. package/dist/core/runner/prepare-caps-and-workflow.js +47 -12
  47. package/dist/core/runner/prepare-gate-stations.d.ts +1 -1
  48. package/dist/core/runner/prepare-hands-readface.d.ts +10 -4
  49. package/dist/core/runner/prepare-hands-readface.js +5 -14
  50. package/dist/core/runner/prepare-inherited-gate.d.ts +5 -3
  51. package/dist/core/runner/prepare-inherited-gate.js +1 -1
  52. package/dist/core/runner/prepare-memory.d.ts +31 -34
  53. package/dist/core/runner/prepare-memory.js +73 -51
  54. package/dist/core/runner/prepare-policy-chain.js +8 -4
  55. package/dist/core/runner/prepare-prompt-inputs.d.ts +4 -0
  56. package/dist/core/runner/prepare-prompt-inputs.js +2 -2
  57. package/dist/core/runner/prepare-protocol-tools.js +5 -0
  58. package/dist/core/runner/prepare-question-face.d.ts +10 -8
  59. package/dist/core/runner/prepare-question-face.js +1 -3
  60. package/dist/core/runner/prepare-safety-scan.js +11 -16
  61. package/dist/core/runner/prepare-task.d.ts +9 -1
  62. package/dist/core/runner/prepare-task.js +63 -49
  63. package/dist/core/runner/prepare-wiring-manifest.d.ts +2 -2
  64. package/dist/core/runner/prepare-wiring-manifest.js +1 -1
  65. package/dist/core/runner/resume-claim.d.ts +2 -2
  66. package/dist/core/runner/resume-preflight.d.ts +2 -2
  67. package/dist/core/runner/run-attachment-seats.d.ts +2 -2
  68. package/dist/core/runner/run-compaction-machinery.js +2 -0
  69. package/dist/core/runner/run-git-lane.d.ts +1 -1
  70. package/dist/core/runner/run-harness-handlers.js +5 -1
  71. package/dist/core/runner/run-identity-wiring.d.ts +7 -0
  72. package/dist/core/runner/run-identity-wiring.js +3 -1
  73. package/dist/core/runner/run-leg.d.ts +7 -5
  74. package/dist/core/runner/run-leg.js +256 -5
  75. package/dist/core/runner/run-notification-lane.d.ts +4 -3
  76. package/dist/core/runner/run-recovery-lanes.d.ts +4 -17
  77. package/dist/core/runner/run-recovery-lanes.js +5 -4
  78. package/dist/core/runner/run-settle-and-teardown.d.ts +14 -10
  79. package/dist/core/runner/run-settle-and-teardown.js +112 -4
  80. package/dist/core/runner/run-stop-and-final-verify.d.ts +2 -2
  81. package/dist/core/runner/run-terminal-adoption.d.ts +16 -16
  82. package/dist/core/runner/run-terminal-adoption.js +87 -7
  83. package/dist/core/runner/run-turn-boundary.js +5 -4
  84. package/dist/core/runner/runtask.d.ts +10 -90
  85. package/dist/core/runner/runtask.js +77 -596
  86. package/dist/core/runner/stream-lifecycle-verbs.js +10 -1
  87. package/dist/core/runner/stream-settle-backstop.js +1 -1
  88. package/dist/core/runtime-caps.d.ts +21 -0
  89. package/dist/core/runtime-caps.js +5 -1
  90. package/dist/core/task-event.d.ts +11 -2
  91. package/dist/core/task-registry-shared.js +8 -6
  92. package/dist/core/task-result.d.ts +15 -0
  93. package/dist/core/task-stream.d.ts +10 -1
  94. package/dist/core/terminal-cause.d.ts +6 -2
  95. package/dist/core/tool-face.d.ts +8 -0
  96. package/dist/core/tool-face.js +1 -0
  97. package/dist/core/tool-policy.d.ts +39 -31
  98. package/dist/core/tool-policy.js +29 -5
  99. package/dist/core/tool-registry.d.ts +11 -3
  100. package/dist/core/tool-registry.js +7 -1
  101. package/dist/core/tool-roster.d.ts +26 -0
  102. package/dist/core/tool-roster.js +40 -7
  103. package/dist/core/tool-spec.d.ts +13 -12
  104. package/dist/core/types.d.ts +2 -1
  105. package/dist/core/types.js +1 -0
  106. package/dist/core/wiring-manifest.d.ts +16 -9
  107. package/dist/core/wiring-manifest.js +8 -3
  108. package/dist/index.d.ts +8 -7
  109. package/dist/index.js +7 -6
  110. package/dist/orchestration/run-workflow-tool.d.ts +8 -8
  111. package/dist/orchestration/run-workflow-tool.js +1 -1
  112. package/dist/orchestration/workflow-primitives.d.ts +4 -3
  113. package/dist/orchestration/workflow-primitives.js +3 -3
  114. package/dist/orchestration/workflow-types.d.ts +14 -0
  115. package/dist/orchestration/workflow.d.ts +41 -4
  116. package/dist/orchestration/workflow.js +23 -5
  117. package/dist/server/http.js +2 -1
  118. package/dist/tools/fs/fs-shared.js +9 -3
  119. package/dist/tools/fs/read-deny.d.ts +24 -8
  120. package/dist/tools/fs/read-deny.js +20 -1
  121. package/package.json +2 -1
  122. package/test/export-surface.snapshot.json +35 -1
@@ -1077,19 +1077,20 @@ export interface ToolExecuteContext {
1077
1077
  ttlMs?: number;
1078
1078
  };
1079
1079
  /**
1080
- * Ruled 2026-08-04 — the host run set {@link TaskSpec.checkpointStore} to `"disabled"` (the per-run
1081
- * off switch for the durable machine). Runner-filled, read-only, NEVER a model/tool argument; absent
1082
- * on every other run, so a deployment that does not use the off switch sees no change at all.
1080
+ * The host run's RESOLVED checkpoint seat, for its children — the store object the run parks in (the ONE value
1081
+ * `resolveCheckpointStore(spec, deps)` produced), or the word `"disabled"` when the caller disarmed the run.
1082
+ * Runner-filled, read-only, NEVER a model/tool argument. ABSENT when the tool runs outside a Runner task and
1083
+ * when the host has no seat at all (unset spec, no deployment store): nothing to inherit, so the child's own
1084
+ * door resolves its runner's deployment — a split child-execution runner may carry a store the host has none of.
1083
1085
  *
1084
- * A delegation tool MUST copy `checkpointStore: "disabled"` onto every child spec it builds (sync,
1085
- * background and fork alike). Without that copy the off switch is a ONE-LEVEL guarantee: the child
1086
- * runs on the same Runner, so it resolves `RunnerDeps.checkpointStore` on its own and re-arms every
1087
- * suspend leg the parent just disarmed a machine-started run would then park in its subtree, which
1088
- * is exactly the permanently-pending checkpoint the null exists to make impossible. Same discipline
1089
- * as `principal` (inherit verbatim down the delegation tree off the trusted seat), and strictly a
1090
- * capability REMOVAL, so it can never widen a child.
1091
- */
1092
- checkpointStoreDisabledForChildren?: true;
1086
+ * A delegation tool copies this seat VERBATIM onto every child spec it builds (sync, background and fork
1087
+ * alike) one rule: a store the parent resolved at SPEC level reaches the child (a child that re-resolved
1088
+ * `RunnerDeps.checkpointStore` on its own found no store on a deployment that wires it per task, and its ask
1089
+ * fell to the fail-closed deny while the parent could park), and `"disabled"` presses on the whole subtree (a
1090
+ * capability REMOVAL, never re-armed one level down by a deployment store a machine-started run must not
1091
+ * park in its subtree). Same discipline as `principal`: inherit verbatim off the trusted seat.
1092
+ */
1093
+ checkpointStoreForChildren?: import("./checkpoint-store.js").CheckpointStore | "disabled";
1093
1094
  /**
1094
1095
  * design/99 (nested-subagent live observability): the host run's stable taskId (`spec.taskId ?? sessionId`),
1095
1096
  * filled by the Runner. A delegation tool (`createSubagentTool`) reads it AT SPAWN TIME and threads it into the
@@ -22,7 +22,8 @@ export type { TaskStatus, RemoteEnvFailureNote, EffectiveMemoryScopes, TaskResul
22
22
  export type { TerminalCause, PausedCause } from "./terminal-cause.js";
23
23
  export type { TaskEventIdentity, BrainStatusPhase, BrainRetryErrClass, BrainStatus, ToolActivity, HumanInputSource, HumanInputDelivery, DelegationTaskType, TaskEvent, HumanInputEvent, CompactOutcome } from "./task-event.js";
24
24
  export type { TaskStream } from "./task-stream.js";
25
- export type { WorkflowGovernanceBaseline, RuntimeCaps } from "./runtime-caps.js";
25
+ export type { WorkflowGovernanceBaseline, RuntimeCaps, AutoModeDenySource } from "./runtime-caps.js";
26
+ export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./runtime-caps.js";
26
27
  export type { BackgroundChildEvent, DelegationLifecycleEvent } from "./delegation-frames.js";
27
28
  export { __resetMalformedDelegationSeatAnnouncement, deliverDelegationLifecycle } from "./delegation-frames.js";
28
29
  export type { ProjectMemoryLoad, ResumePreflightInfo, ResumePreflightVerdict, RunnerDeps } from "./runner-deps.js";
@@ -1,2 +1,3 @@
1
+ export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./runtime-caps.js";
1
2
  export { __resetMalformedDelegationSeatAnnouncement, deliverDelegationLifecycle } from "./delegation-frames.js";
2
3
  export { __resetMalformedNoticeSeatAnnouncement, deliverEngineNotice, undrainedUserInputNotices } from "./engine-notice.js";
@@ -74,6 +74,18 @@ export type ParkLaneReason = "no_checkpoint_store" | "no_durable_approval_opt_in
74
74
  */
75
75
  export declare const AUTO_MODE_ARM_REASONS: readonly ["armed", "no_intent", "no_face", "denied", "resolver_fault"];
76
76
  export type AutoModeArmReason = (typeof AUTO_MODE_ARM_REASONS)[number];
77
+ /**
78
+ * The leg's auto-mode arming FACT as the manifest carries it: `armed ⇔ reason === "armed"`; `deniedSource` (#692 C-d,
79
+ * additive) is present only beside a STATED `denied` and names which settings plane's ratchet turned auto mode off
80
+ * ({@link import("./runtime-caps.js").AUTO_MODE_DENY_SOURCES}: `org` / `local` / `settings`) — the resolver's
81
+ * own word, carried through, never inferred; a `denied` with no source is a resolver that does not track sources,
82
+ * and a `resolver_fault` never carries one (the screen strips a source it refused).
83
+ */
84
+ export interface AutoModeArmFact {
85
+ armed: boolean;
86
+ reason: AutoModeArmReason;
87
+ deniedSource?: import("./runtime-caps.js").AutoModeDenySource;
88
+ }
77
89
  /**
78
90
  * One declared MCP server's materialization result for THIS leg — the per-server connection-state
79
91
  * read face on {@link WiringManifest.mcp}. `status` is the connect-time verdict of this leg's
@@ -242,10 +254,7 @@ export interface WiringManifest {
242
254
  * round is a deny that says so, and the next round is classified afresh), so there is no session-level
243
255
  * breaker face here and nothing about availability enters {@link configFingerprint}.
244
256
  */
245
- autoMode?: {
246
- armed: boolean;
247
- reason: AutoModeArmReason;
248
- };
257
+ autoMode?: AutoModeArmFact;
249
258
  /**
250
259
  * EFFECTIVE half only, and ALWAYS present on an engine-minted effective manifest (the `autoMode`
251
260
  * posture: every leg has an answer — an empty array is "this leg declared no servers", absence is
@@ -333,11 +342,9 @@ export interface WiringFacts {
333
342
  restore: string;
334
343
  };
335
344
  /** Effective half only — see {@link WiringManifest.autoMode}; the static half has no leg to arm.
336
- * `armed` must agree with `reason` (`armed ⇔ reason === "armed"`); a contradicting pair is refused. */
337
- autoMode?: {
338
- armed: boolean;
339
- reason: AutoModeArmReason;
340
- };
345
+ * `armed` must agree with `reason` (`armed ⇔ reason === "armed"`); a contradicting pair is refused, and so
346
+ * is a `deniedSource` beside any reason but `denied` or spelled outside its closed set. */
347
+ autoMode?: AutoModeArmFact;
341
348
  /** Effective half only — see {@link WiringManifest.mcp}; the static half materializes nothing.
342
349
  * Copied entry-wise onto the manifest (the caller's array is never aliased). */
343
350
  mcp?: readonly WiringManifestMcpEntry[];
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { canonicalize } from "./canonical-json.js";
3
3
  import { resolveCheckpointStore } from "./checkpoint-store.js";
4
4
  import { isLiveQuestionFace } from "./ask-question.js";
5
+ import { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./runtime-caps.js";
5
6
  export const AUTO_MODE_ARM_REASONS = ["armed", "no_intent", "no_face", "denied", "resolver_fault"];
6
7
  export function resolveDeclaredDurability(store, storeName) {
7
8
  const declared = store?.durability;
@@ -68,10 +69,14 @@ function deriveParkLane(facts) {
68
69
  return { capable: true, effective: unresolved ? "unresolved" : false, reasons, ...durability };
69
70
  }
70
71
  function readAutoModeFact(fact) {
71
- if (!AUTO_MODE_ARM_REASONS.includes(fact.reason) || fact.armed !== (fact.reason === "armed")) {
72
- throw new Error(`the auto-mode wiring fact is inconsistent (armed=${String(fact.armed)}, reason=${JSON.stringify(fact.reason)}) armed must hold exactly when reason is "armed", and reason must be one of ${AUTO_MODE_ARM_REASONS.join("|")}`);
72
+ const { armed, reason, deniedSource } = fact;
73
+ if (!AUTO_MODE_ARM_REASONS.includes(reason) || armed !== (reason === "armed")) {
74
+ throw new Error(`the auto-mode wiring fact is inconsistent (armed=${String(armed)}, reason=${JSON.stringify(reason)}) — armed must hold exactly when reason is "armed", and reason must be one of ${AUTO_MODE_ARM_REASONS.join("|")}`);
73
75
  }
74
- return { armed: fact.armed, reason: fact.reason };
76
+ if (deniedSource !== undefined && (reason !== "denied" || !isAutoModeDenySource(deniedSource))) {
77
+ throw new Error(`the auto-mode wiring fact is inconsistent (reason=${JSON.stringify(reason)}, deniedSource=${JSON.stringify(deniedSource)}) — deniedSource may stand only beside reason "denied", as one of ${AUTO_MODE_DENY_SOURCES.join("|")}`);
78
+ }
79
+ return { armed, reason, ...(deniedSource !== undefined ? { deniedSource } : {}) };
75
80
  }
76
81
  export function deriveWiringManifest(facts) {
77
82
  if (facts.half === "static" && facts.leg !== undefined) {
package/dist/index.d.ts CHANGED
@@ -126,9 +126,9 @@ export { createFsWriteGatePolicy, type FsWriteGatePolicyOptions } from "./core/f
126
126
  export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
127
127
  export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
128
128
  export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, isTerminalTaskNotification, SystemInjectionQueue, type TaskNotificationPayload, type TaskNotificationStatus, type ExternalNotificationInput, type SystemInjection, type SystemInjectionPriority, SYSTEM_INJECTION_PRIORITIES, isSystemInjectionPriority, AGENT_MESSAGE_TAG, renderAgentMessageFrame, type SemaProvenance, } from "./core/task-notification.js";
129
- export { TOOL_FAMILIES, TOOL_MOUNT_TAGS, TOOL_APPROVAL_CARDS, TOOL_PATH_ACCESSES, TOOL_PATH_BASES, TOOL_PATH_ABSENCES, TOOL_WIRE_NAME_MAX_CHARS, TOOL_CONTRACT_MAX_CHARS, TOOL_KEY_MAX_CHARS, TOOL_KEYS_MAX, RENDER_HINT_MAX_CHARS, RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_LIST, type ToolFamily, type ToolMountTag, type ToolApprovalCard, type ToolPathAccess, type ToolPathBase, type ToolPathAbsence, type ToolPathTarget, type ToolCallPathTarget, type ToolRosterPathTarget, pathTargetSlotOnly, type ToolRuleFace, type ToolRenderHints, type ToolFaceDeclaration, type ToolCatalogEntry, } from "./core/tool-face.js";
130
- export { describeToolCatalog, catalogEntriesNamed, catalogToolNames, catalogRuleFaceOf, pathTargetOf, pathTargetValue, protectivePathTargetOf, declaredPathTargetOf, isProtectedWrite, isDeclaredWrite, fileOpAccessOf, type ToolCallLike, pathConfinableWriteToolNames, skillScopeWriteToolNames, pathTargetingToolNames, compactableToolNames, budgetExemptToolNames, handBandEffects, engineCardTypes, predictMountedToolNames, HANDS_BAND_TAGS, MOUNT_TAG_IN_HANDS_BAND, } from "./core/tool-registry.js";
131
- export { ToolRosterEntry, ToolRoster, ToolRosterDelta, ToolRosterRenderHints, ROSTER_SOURCES, type RosterSource, type ToolIdentity, toolIdentityOf, mintCapabilityId, diffToolRoster, judgeParkedToolIdentity, ruleFacesFromRoster, toolCallFaceOf, inputKeysOf, ruleFaceOfInstance, ToolRosterDeltaSeat, } from "./core/tool-roster.js";
129
+ export { TOOL_FAMILIES, TOOL_MOUNT_TAGS, TOOL_APPROVAL_CARDS, TOOL_PATH_ACCESSES, TOOL_PATH_BASES, TOOL_PATH_ABSENCES, TOOL_WIRE_NAME_MAX_CHARS, TOOL_CONTRACT_MAX_CHARS, TOOL_CARD_ID_MAX_CHARS, TOOL_KEY_MAX_CHARS, TOOL_KEYS_MAX, RENDER_HINT_MAX_CHARS, RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_LIST, type ToolFamily, type ToolMountTag, type ToolApprovalCard, type ToolPathAccess, type ToolPathBase, type ToolPathAbsence, type ToolPathTarget, type ToolCallPathTarget, type ToolRosterPathTarget, pathTargetSlotOnly, type ToolRuleFace, type ToolRenderHints, type ToolFaceDeclaration, type ToolCatalogEntry, } from "./core/tool-face.js";
130
+ export { describeToolCatalog, catalogEntriesNamed, catalogToolNames, catalogRuleFaceOf, pathTargetOf, pathTargetValue, protectivePathTargetOf, declaredPathTargetOf, BARE_REQUEST_CODE, isProtectedWrite, isDeclaredWrite, fileOpAccessOf, type ToolCallLike, pathConfinableWriteToolNames, skillScopeWriteToolNames, pathTargetingToolNames, compactableToolNames, budgetExemptToolNames, handBandEffects, engineCardTypes, predictMountedToolNames, HANDS_BAND_TAGS, MOUNT_TAG_IN_HANDS_BAND, } from "./core/tool-registry.js";
131
+ export { ToolRosterEntry, ToolRoster, ToolRosterDelta, ToolRosterRenderHints, ROSTER_SOURCES, type RosterSource, type ToolIdentity, toolIdentityOf, mintCapabilityId, diffToolRoster, judgeParkedToolIdentity, ruleFacesFromRoster, toolCallFaceOf, inputKeysOf, ruleFaceOfInstance, rosterMemberBoundProblem, type RosterBoundMembers, type RosterBoundCode, ToolRosterDeltaSeat, } from "./core/tool-roster.js";
132
132
  export { type ToolCallFace } from "./core/tool-policy.js";
133
133
  export { effectivePathTargetOf, pathTargetBaseOf, type ToolPathBases } from "./core/effective-path-target.js";
134
134
  export { TOOL_CONFORMANCE_VECTORS, describeToolConformanceVectors, type ToolConformanceVector } from "./core/tool-conformance.js";
@@ -138,7 +138,7 @@ export { parkedToolIdentity } from "./core/tool-roster.js";
138
138
  export { type ToolCallIdentity } from "./core/fs-write-gate-policy.js";
139
139
  export { type RuleFaceView, catalogRuleFaceView } from "./core/permission-rules.js";
140
140
  export { DURABLE_HISTORY_NAMES } from "./engine/compaction/utils.js";
141
- export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveSubagentTranscriptTier, type SubagentTranscriptTier, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, AUTO_MODE_ARM_REASONS, type AutoModeArmReason, type WiringManifest, type WiringManifestMcpEntry, type WiringFacts, type WiringLegKind, type AskSeamForm, type AskEffective, type QuestionChannelState, type SeamProvenance, type ParkLaneReason, type ManifestDurability, type StaticWiringDeps, type StaticWiringSpec, } from "./core/wiring-manifest.js";
141
+ export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveSubagentTranscriptTier, type SubagentTranscriptTier, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, AUTO_MODE_ARM_REASONS, type AutoModeArmReason, type AutoModeArmFact, type WiringManifest, type WiringManifestMcpEntry, type WiringFacts, type WiringLegKind, type AskSeamForm, type AskEffective, type QuestionChannelState, type SeamProvenance, type ParkLaneReason, type ManifestDurability, type StaticWiringDeps, type StaticWiringSpec, } from "./core/wiring-manifest.js";
142
142
  export { probeParkRoundTrip, durableParkGapOf, durableParkGapFor, PARK_SELFCHECK_SCOPE_PREFIX, type ParkSelfCheckResult, type ParkProbeFinding, type ParkProbeFindingCode, } from "./core/park-selfcheck.js";
143
143
  export { type StoreDurability } from "./core/checkpoint-store.js";
144
144
  export { type StoreFidelity } from "./core/checkpoint-store.js";
@@ -164,8 +164,9 @@ export { createSchedulerTools, type SchedulerToolContext, SCHEDULE_WAKEUP_TOOL_N
164
164
  export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, type AutonomousLoopPromptOptions, } from "./tools/loop-tick.js";
165
165
  export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
166
166
  export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicyProjection, type ToolPolicyProjectionComponent, type ConstraintChainEntry, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, type AskRuleEvidence, type AskEvidenceAbsence, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
167
- export { type AskOrigin, ASK_ORIGINS, isAskOrigin, classifierMayAnswer, ORIGIN_IMPLIES_REAL_APPROVAL } from "./core/ask-origin.js";
168
- export { SETTLEMENT_KINDS, type SettlementKind, isSettlementKind, type Settlement, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, type DeniedBy, isDeniedBy, DENIED_BY_MAY_VETO, type GateDisposition, type GateOutcome, screenGateOutcome } from "./core/gate-outcome.js";
167
+ export { type AskOrigin, ASK_ORIGINS, isAskOrigin, classifierMayAnswer, ORIGIN_IMPLIES_REAL_APPROVAL, RULE_STORE_UNREADABLE_KINDS, type RuleStoreUnreadable, isRuleStoreUnreadable } from "./core/ask-origin.js";
168
+ export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource, type AutoModeDenySource } from "./core/runtime-caps.js";
169
+ export { SETTLEMENT_KINDS, type SettlementKind, isSettlementKind, type Settlement, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, type DeniedBy, isDeniedBy, DENIED_BY_MAY_VETO, CLASSIFIER_DENY_CAUSES, type ClassifierDenyCause, isClassifierDenyCause, DENIED_BY_MAY_CARRY_CAUSE, type GateDisposition, type GateOutcome, screenGateOutcome } from "./core/gate-outcome.js";
169
170
  export { type AskCarry } from "./core/hooks.js";
170
171
  export { SWAPPABLE_DEP_SEATS, SWAP_SEAT_NOTICE, isSwappableDepSeat, screenSwappableDeps, type SwappableDepSeat, type SwappableDeps } from "./core/swappable-deps.js";
171
172
  export { parseAutoModeResponse, createAutoModeDecider, AUTO_MODE_UNAVAILABLE_CAUSES, isAutoModeUnavailableCause, classifierUnavailableDenyMessage, CLASSIFIER_PARSE_FAILURE_DENY_MESSAGE, type AutoModeUnavailableCause, type AutoModeVerdict, type AutoModeDecider, type AutoModeDeciderOptions, type AutoModeClassified, type AutoModeClassifyFn, type AutoModeClassifyInput, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, unarmedWindow, type AutoModeDenialTracker, type AutoModeDenialLimitOptions, type DenialLimitCounts, type DenialLimitFallbackFace, type UnarmedDenialLimitFallback, type DenialLimitFallback, type DenialLimitVerdict, } from "./core/auto-mode.js";
@@ -228,7 +229,7 @@ export { CenterPromptSource, FilePromptArtifactStore, FilePromptSourceStateStore
228
229
  export { stripEngineMetadata } from "./internal/llm.js";
229
230
  export { EVENT_PROMPT_REGISTRY, eventDefaultOn, type CompiledEventPrompt, type CompiledMessageInjection, type EventDedupe, type EventDefaultPolicy } from "./prompt-assembly/event-registry.js";
230
231
  export type { CompiledSection, ComposedPrompt, PackSectionDeclaration, PromptCacheClass, PromptCarrier, PromptMutability, PromptOwner, PromptPack, PromptRenderCadence, PromptRuntimeFacts, PromptSlot, PromptTrust, SectionRenderInputs, } from "./prompt-assembly/types.js";
231
- export { type ReasoningIntensity, type ReasoningResolution, type ResolvedReasoning, type ReasoningFormat, DEFAULT_EFFORT_LEVELS, REASONING_BUDGET_SHARE, isThinkingLevel, rankOf, resolveEffort, resolveBinary, reasoningBudgetShare, resolveReasoning, thinkingOffExpressible, declaredOffSpelling, type OffCapabilityModel, resolveReasoningProfile, type ReasoningTier, type ReasoningProfileFlags, } from "./brain/reasoning.js";
232
+ export { type ReasoningIntensity, type ReasoningResolution, type ResolvedReasoning, type ReasoningFormat, DEFAULT_EFFORT_LEVELS, REASONING_BUDGET_SHARE, isThinkingLevel, rankOf, resolveEffort, reasoningEffortLevelsOf, resolveBinary, reasoningBudgetShare, resolveReasoning, thinkingOffExpressible, declaredOffSpelling, type OffCapabilityModel, resolveReasoningProfile, type ReasoningTier, type ReasoningProfileFlags, } from "./brain/reasoning.js";
232
233
  export { DESIGN_REVIEW_PROMPTS, CODE_REVIEW_PROMPT, SCENARIO_REGISTRY, runScenario, type ScenarioId, type CodeReviewMode, type ScenarioProfile, type RunScenarioOptions, type RunScenarioResult, } from "./scenarios/scenario-registry.js";
233
234
  export { teacherMode, TEACHER_PROFILE, type TeacherModePair, type TeacherProfile, } from "./scenarios/teacher-quickstart.js";
234
235
  export { loadOrchestrationEnv, DEFAULT_REASONING_INTENSITY, type OrchestrationMode, type OrchestrationEnv, } from "./scenarios/env.js";
package/dist/index.js CHANGED
@@ -102,9 +102,9 @@ export { createFsWriteGatePolicy } from "./core/fs-write-gate-policy.js";
102
102
  export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
103
103
  export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
104
104
  export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, isTerminalTaskNotification, SystemInjectionQueue, SYSTEM_INJECTION_PRIORITIES, isSystemInjectionPriority, AGENT_MESSAGE_TAG, renderAgentMessageFrame, } from "./core/task-notification.js";
105
- export { TOOL_FAMILIES, TOOL_MOUNT_TAGS, TOOL_APPROVAL_CARDS, TOOL_PATH_ACCESSES, TOOL_PATH_BASES, TOOL_PATH_ABSENCES, TOOL_WIRE_NAME_MAX_CHARS, TOOL_CONTRACT_MAX_CHARS, TOOL_KEY_MAX_CHARS, TOOL_KEYS_MAX, RENDER_HINT_MAX_CHARS, RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_LIST, pathTargetSlotOnly, } from "./core/tool-face.js";
106
- export { describeToolCatalog, catalogEntriesNamed, catalogToolNames, catalogRuleFaceOf, pathTargetOf, pathTargetValue, protectivePathTargetOf, declaredPathTargetOf, isProtectedWrite, isDeclaredWrite, fileOpAccessOf, pathConfinableWriteToolNames, skillScopeWriteToolNames, pathTargetingToolNames, compactableToolNames, budgetExemptToolNames, handBandEffects, engineCardTypes, predictMountedToolNames, HANDS_BAND_TAGS, MOUNT_TAG_IN_HANDS_BAND, } from "./core/tool-registry.js";
107
- export { ToolRosterEntry, ToolRoster, ToolRosterDelta, ToolRosterRenderHints, ROSTER_SOURCES, toolIdentityOf, mintCapabilityId, diffToolRoster, judgeParkedToolIdentity, ruleFacesFromRoster, toolCallFaceOf, inputKeysOf, ruleFaceOfInstance, ToolRosterDeltaSeat, } from "./core/tool-roster.js";
105
+ export { TOOL_FAMILIES, TOOL_MOUNT_TAGS, TOOL_APPROVAL_CARDS, TOOL_PATH_ACCESSES, TOOL_PATH_BASES, TOOL_PATH_ABSENCES, TOOL_WIRE_NAME_MAX_CHARS, TOOL_CONTRACT_MAX_CHARS, TOOL_CARD_ID_MAX_CHARS, TOOL_KEY_MAX_CHARS, TOOL_KEYS_MAX, RENDER_HINT_MAX_CHARS, RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_LIST, pathTargetSlotOnly, } from "./core/tool-face.js";
106
+ export { describeToolCatalog, catalogEntriesNamed, catalogToolNames, catalogRuleFaceOf, pathTargetOf, pathTargetValue, protectivePathTargetOf, declaredPathTargetOf, BARE_REQUEST_CODE, isProtectedWrite, isDeclaredWrite, fileOpAccessOf, pathConfinableWriteToolNames, skillScopeWriteToolNames, pathTargetingToolNames, compactableToolNames, budgetExemptToolNames, handBandEffects, engineCardTypes, predictMountedToolNames, HANDS_BAND_TAGS, MOUNT_TAG_IN_HANDS_BAND, } from "./core/tool-registry.js";
107
+ export { ToolRosterEntry, ToolRoster, ToolRosterDelta, ToolRosterRenderHints, ROSTER_SOURCES, toolIdentityOf, mintCapabilityId, diffToolRoster, judgeParkedToolIdentity, ruleFacesFromRoster, toolCallFaceOf, inputKeysOf, ruleFaceOfInstance, rosterMemberBoundProblem, ToolRosterDeltaSeat, } from "./core/tool-roster.js";
108
108
  export {} from "./core/tool-policy.js";
109
109
  export { effectivePathTargetOf, pathTargetBaseOf } from "./core/effective-path-target.js";
110
110
  export { TOOL_CONFORMANCE_VECTORS, describeToolConformanceVectors } from "./core/tool-conformance.js";
@@ -138,8 +138,9 @@ export { createSchedulerTools, SCHEDULE_WAKEUP_TOOL_NAME, AUTONOMOUS_LOOP_SENTIN
138
138
  export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, } from "./tools/loop-tick.js";
139
139
  export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
140
140
  export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
141
- export { ASK_ORIGINS, isAskOrigin, classifierMayAnswer, ORIGIN_IMPLIES_REAL_APPROVAL } from "./core/ask-origin.js";
142
- export { SETTLEMENT_KINDS, isSettlementKind, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, isDeniedBy, DENIED_BY_MAY_VETO, screenGateOutcome } from "./core/gate-outcome.js";
141
+ export { ASK_ORIGINS, isAskOrigin, classifierMayAnswer, ORIGIN_IMPLIES_REAL_APPROVAL, RULE_STORE_UNREADABLE_KINDS, isRuleStoreUnreadable } from "./core/ask-origin.js";
142
+ export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./core/runtime-caps.js";
143
+ export { SETTLEMENT_KINDS, isSettlementKind, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, isDeniedBy, DENIED_BY_MAY_VETO, CLASSIFIER_DENY_CAUSES, isClassifierDenyCause, DENIED_BY_MAY_CARRY_CAUSE, screenGateOutcome } from "./core/gate-outcome.js";
143
144
  export {} from "./core/hooks.js";
144
145
  export { SWAPPABLE_DEP_SEATS, SWAP_SEAT_NOTICE, isSwappableDepSeat, screenSwappableDeps } from "./core/swappable-deps.js";
145
146
  export { parseAutoModeResponse, createAutoModeDecider, AUTO_MODE_UNAVAILABLE_CAUSES, isAutoModeUnavailableCause, classifierUnavailableDenyMessage, CLASSIFIER_PARSE_FAILURE_DENY_MESSAGE, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, unarmedWindow, } from "./core/auto-mode.js";
@@ -186,7 +187,7 @@ export { buildTurnPromptSnapshot, loweringRecordFor, LOWERING_VERSION, PREFIX_IN
186
187
  export { CenterPromptSource, FilePromptArtifactStore, FilePromptSourceStateStore, MemoryPromptArtifactStore, MemoryPromptSourceStateStore, } from "./prompt-assembly/artifact-store.js";
187
188
  export { stripEngineMetadata } from "./internal/llm.js";
188
189
  export { EVENT_PROMPT_REGISTRY, eventDefaultOn } from "./prompt-assembly/event-registry.js";
189
- export { DEFAULT_EFFORT_LEVELS, REASONING_BUDGET_SHARE, isThinkingLevel, rankOf, resolveEffort, resolveBinary, reasoningBudgetShare, resolveReasoning, thinkingOffExpressible, declaredOffSpelling, resolveReasoningProfile, } from "./brain/reasoning.js";
190
+ export { DEFAULT_EFFORT_LEVELS, REASONING_BUDGET_SHARE, isThinkingLevel, rankOf, resolveEffort, reasoningEffortLevelsOf, resolveBinary, reasoningBudgetShare, resolveReasoning, thinkingOffExpressible, declaredOffSpelling, resolveReasoningProfile, } from "./brain/reasoning.js";
190
191
  export { DESIGN_REVIEW_PROMPTS, CODE_REVIEW_PROMPT, SCENARIO_REGISTRY, runScenario, } from "./scenarios/scenario-registry.js";
191
192
  export { teacherMode, TEACHER_PROFILE, } from "./scenarios/teacher-quickstart.js";
192
193
  export { loadOrchestrationEnv, DEFAULT_REASONING_INTENSITY, } from "./scenarios/env.js";
@@ -277,7 +277,7 @@ export interface RunWorkflowToolDeps {
277
277
  * tighten-only ruling; the workflow lane was the remaining gap — a read-only host's workflow
278
278
  * children spawned with full write hands). Filled ONLY when the clamp is ON (`true`), exactly
279
279
  * like the ctx seat, so an unclamped deployment's mount gains no key. Known at prepare-time
280
- * (frozen TaskSpec snapshot — no lazy getter needed, the `parentCheckpointStoreDisabled` shape). */
280
+ * (frozen TaskSpec snapshot — no lazy getter needed, the `parentCheckpointStore` shape). */
281
281
  parentHandsReadOnly?: true;
282
282
  /** #345 ③ — the HOST task's hard-headless clamp (`interactiveTools: false`), same carriage rules
283
283
  * as `parentHandsReadOnly` above: only the DISABLING value travels, mirroring the subagent lane's
@@ -352,13 +352,13 @@ export interface RunWorkflowToolDeps {
352
352
  autoModeReview?: () => {
353
353
  decider: import("../core/auto-mode.js").AutoModeDecider;
354
354
  } | undefined;
355
- /** 5.30 merge-rescan round 2 — the HOST task's durable OFF SWITCH (`spec.checkpointStore === "disabled"`,
356
- * ruled 2026-08-04 to inherit into every spawned agent, see `buildWorkflowPrimitives`'s call
357
- * above). Known at prepare-time (no lazy getter needed `spec` is available at mount). A dep for
358
- * the same reason as `forwardEvent`/`inheritedGateForChildren`: the auto-mounted tool's execute
359
- * ctx is minimal `{toolCallId, signal}`, so `ctx.checkpointStoreDisabledForChildren` was a DEAD
360
- * read there — the off-switch never actually reached this lane's children. */
361
- parentCheckpointStoreDisabled?: boolean;
355
+ /** The HOST run's RESOLVED checkpoint seat — the store object the host parks in, else the word `"disabled"`
356
+ * (the same two-state value the delegation ctx carries as `ToolExecuteContext.checkpointStoreForChildren`;
357
+ * `ctx` wins on a rich-ctx mount). Every agent this workflow spawns inherits it verbatim unless its own
358
+ * spec (baseline / trusted script) pinned a store — and `"disabled"` lands unconditionally (a removal the
359
+ * baseline must not undo); see `buildWorkflowPrimitives`. A dep for the same reason as `forwardEvent`/
360
+ * `inheritedGateForChildren`: the auto-mounted tool's execute ctx is minimal `{toolCallId, signal}`. */
361
+ parentCheckpointStore?: import("../core/checkpoint-store.js").CheckpointStore | "disabled";
362
362
  /** #235 — the deployment's structured notice sink (`RunnerDeps.onNotice`), forwarded into the governed
363
363
  * build so the fields a script's agent spec wrote that did NOT reach the child are announced instead of
364
364
  * vanishing (`workflow.governance_key_stripped`). #505 ① — the same seat now also carries the OPTIONS
@@ -488,7 +488,7 @@ export async function createRunWorkflowTool(d) {
488
488
  }
489
489
  : governance;
490
490
  const scriptFn = (wfCtx) => {
491
- const primitives = buildWorkflowPrimitives(wfCtx, runGovernance, d.onAgentSpawn, principal, ctx.checkpointStoreDisabledForChildren === true || d.parentCheckpointStoreDisabled === true, d.parentReadFace, d.parentReadDenyPatterns, ctx.handsReadOnly === true || d.parentHandsReadOnly === true, ctx.interactiveTools === false || d.parentInteractiveTools === false);
491
+ const primitives = buildWorkflowPrimitives(wfCtx, runGovernance, d.onAgentSpawn, principal, ctx.checkpointStoreForChildren ?? d.parentCheckpointStore, d.parentReadFace, d.parentReadDenyPatterns, ctx.handsReadOnly === true || d.parentHandsReadOnly === true, ctx.interactiveTools === false || d.parentInteractiveTools === false);
492
492
  return d.scriptRunner.run({ scriptSource: script, primitives, scriptArgs: effectiveArgs, signal: wfCtx.signal }).then((r) => r.result);
493
493
  };
494
494
  if (ctx.signal?.aborted) {
@@ -14,6 +14,7 @@
14
14
  */
15
15
  import type { Model } from "../internal/llm.js";
16
16
  import type { TaskSpec, WorkflowGovernanceBaseline } from "../core/types.js";
17
+ import type { CheckpointStore } from "../core/checkpoint-store.js";
17
18
  import type { WorkflowAgentHandle, WorkflowRunContext } from "./workflow.js";
18
19
  import type { WorkflowPrimitives } from "./workflow-script-runner.js";
19
20
  import { type WorkflowChildCaps } from "./workflow-governance.js";
@@ -36,9 +37,9 @@ export interface WorkflowGovernance {
36
37
  * — filling them here ran ahead of the fold and made the fold's "when unset" guard permanently false.
37
38
  */
38
39
  export declare function buildWorkflowPrimitives(ctx: WorkflowRunContext, governance?: WorkflowGovernance, onAgentSpawn?: (handle: WorkflowAgentHandle) => void, parentPrincipal?: string,
39
- /** ruled 2026-08-04 — the host run set `TaskSpec.checkpointStore: "disabled"` (the per-run durable off
40
- * switch). Every agent this workflow spawns inherits it; see the injection below. */
41
- parentCheckpointStoreDisabled?: boolean,
40
+ /** The host run's RESOLVED checkpoint seat — the store object it parks in, else the word `"disabled"`.
41
+ * Every agent this workflow spawns inherits it; see the injection below. */
42
+ parentCheckpointStore?: CheckpointStore | "disabled",
42
43
  /** 5.30 merge-rescan (design/199 parity gap) — the host run's RESOLVED read-face containment
43
44
  * (post deps/org/mount resolution, not the raw spec value). `TaskSpec.readFace`'s own JSDoc
44
45
  * promises "roots pins this task, and via the tighten-only clamp, its whole delegation subtree" —
@@ -65,7 +65,7 @@ function formatResourceClampNote(notes) {
65
65
  const parts = notes.map((n) => `${n.field}: requested ${n.requested === undefined ? "unset" : n.requested} → applied ${n.applied}`);
66
66
  return `workflow governance tightened this agent's resource limits (${parts.join("; ")})`;
67
67
  }
68
- export function buildWorkflowPrimitives(ctx, governance, onAgentSpawn, parentPrincipal, parentCheckpointStoreDisabled, parentReadFace, parentReadDenyPatterns, parentHandsReadOnly, parentInteractiveToolsOff) {
68
+ export function buildWorkflowPrimitives(ctx, governance, onAgentSpawn, parentPrincipal, parentCheckpointStore, parentReadFace, parentReadDenyPatterns, parentHandsReadOnly, parentInteractiveToolsOff) {
69
69
  const agent = (spec, opts) => {
70
70
  if (typeof spec === "string")
71
71
  spec = { objective: spec };
@@ -77,8 +77,8 @@ export function buildWorkflowPrimitives(ctx, governance, onAgentSpawn, parentPri
77
77
  if (childSpec.principal === undefined && parentPrincipal !== undefined) {
78
78
  childSpec.principal = parentPrincipal;
79
79
  }
80
- if (parentCheckpointStoreDisabled === true) {
81
- childSpec.checkpointStore = "disabled";
80
+ if (parentCheckpointStore !== undefined && (parentCheckpointStore === "disabled" || childSpec.checkpointStore === undefined)) {
81
+ childSpec.checkpointStore = parentCheckpointStore;
82
82
  }
83
83
  if (parentReadFace) {
84
84
  const pf = parentReadFace();
@@ -328,6 +328,20 @@ export interface WorkflowRun {
328
328
  rev?: number;
329
329
  /** Set when `status === "failed"`: the error the script threw. */
330
330
  error?: string;
331
+ /** #688 C4 (additive) — the MACHINE code of the failure that ended the run, in the same vocabulary as
332
+ * {@link WorkflowAgentRun.errorCode} (`workflow.*` for the orchestration's own refusals, a task's terminal code
333
+ * for a child's failure), so a consumer branches here and never on `error`'s text. Attribution, in order:
334
+ * 1. the thrown value's own string `code` — the run SUSPENDED on a parked agent ⇒ `workflow.agent_parked`
335
+ * (whatever the body threw after the park), a refused spawn ⇒ `workflow.agent_blocked`, the budget /
336
+ * nesting / schema / stall / max-agents refusals ⇒ their `WorkflowBudgetExceededError`… codes, a body that
337
+ * re-threw a typed error ⇒ that error's code;
338
+ * 2. otherwise the FIRST failed agent record carrying a code (earliest `endedAt`, spawn order on a tie) —
339
+ * the body gave up after a child failed and threw in its own words; with several failed children the
340
+ * first to fail is the one taken (a body that threw for an unrelated reason beside a failed child still
341
+ * reads that child's code — `error` keeps the body's own sentence);
342
+ * 3. absent — a body that threw a codeless error with no failed child on the record.
343
+ * Absent on `completed` runs (their per-agent failures ride `agentFailures` + each `WorkflowAgentRun.errorCode`). */
344
+ errorCode?: string;
331
345
  /** The script's RETURN VALUE, bounded + redacted at completion time (same egress
332
346
  * discipline as the notifier `result`), so a terminal `TaskOutput` poll can hand the
333
347
  * model the result instead of sending it in poll circles. Absent on failed runs and runs recorded by
@@ -547,10 +547,47 @@ export interface WorkflowHandle<T> {
547
547
  }
548
548
  /** Cap on items a single `parallel`/`pipeline` call accepts (matches the CC Workflow tool). */
549
549
  export declare const MAX_WORKFLOW_ITEMS = 4096;
550
- /** CC `MTy` (206-pretty.js:17501680; 198 `F0m`): no tool activity for this long marks the attempt STALLED
551
- * (progress-based, unlike the governance perAgentTimeoutSec hard cap, which still bounds each attempt).
552
- * `<= 0` DISABLES the watchdog (CC arms only `if (ae > 0)`, :17477830). */
553
- export declare const WORKFLOW_AGENT_STALL_MS = 180000;
550
+ /**
551
+ * CC `MTy` (206-pretty.js:17501680; 198 `F0m`): no tool activity for this long marks the attempt STALLED
552
+ * (progress-based, unlike the governance perAgentTimeoutSec hard cap, which still bounds each attempt).
553
+ * `<= 0` DISABLES the watchdog (CC arms only `if (ae > 0)`, :17477830).
554
+ *
555
+ * DERIVED, not a number of its own (#682): an OUTER, COARSER clock must never fire before the engine's
556
+ * own. This watchdog does not re-send a model call — it aborts the attempt and restarts the whole agent
557
+ * on a FRESH session, so tool effects from before the stall can be repeated. The engine, one layer down,
558
+ * already answers a silent provider call the cheap way: it re-sends that ONE call. So the floor here is
559
+ * what a single provider call may legitimately occupy while producing NOTHING — the connect wait plus the
560
+ * first-token wait ({@link STALL_CONNECT_MS} + {@link STALL_FIRST_TOKEN_MS}). It was a hand-written
561
+ * 180_000 and CC-same until the first-token bound rose to 600s past it (measured: the same silent call
562
+ * then produced two agent runs on two sessions instead of one run with a re-sent call), which is the
563
+ * systematic inversion a derived value cannot re-open: raise or lower the engine's bound and this follows.
564
+ *
565
+ * WHAT THIS IS NOT (measured, adversarial round — do not read the sum as a proof of ordering). The two
566
+ * clocks do not start together and this one does not reset per inner retry, so it can still fire first:
567
+ * this timer starts before the Runner prepares the attempt (an execution-env factory, a hook, a store
568
+ * read all run inside this window and outside the engine's), and the first-token timer only starts once
569
+ * headers arrive — so the margin by which the inner deadline can land AFTER this one is preparation plus
570
+ * the connect time actually spent, less the {@link STALL_CONNECT_MS} this window already budgets for it
571
+ * (40s of preparation and instant headers puts the first inner deadline 10s past this timer). An engine retry does not re-arm this timer either, so two silent provider attempts
572
+ * exceed it by construction. The derivation removes the case that was guaranteed to invert; it does not
573
+ * make inversion impossible. A deployment that wants the old aggression passes `stallMs` explicitly, and
574
+ * a deployment that wants a real guarantee must set it from its own preparation budget.
575
+ */
576
+ export declare const WORKFLOW_AGENT_STALL_MS: number;
577
+ /**
578
+ * The "took suspiciously long" half of the throttle-shaped-degradation predicate (CC `Nt`,
579
+ * 206-pretty.js:17487400, which spells it `MTy * 0.5` = 90s). It used to be computed from the stall
580
+ * window here too, and that was safe only while the window was CC's own 180_000 — the moment the window
581
+ * became derived from the engine's bounds (#682/#693) the same expression silently moved the throttle
582
+ * threshold 90s → 315s, and a 2-minute empty answer that used to earn a 45s-sleep-and-retry was simply
583
+ * returned. Two unrelated meanings were riding one number. This is the second meaning, at CC's own
584
+ * value, so the progress window can move without touching it.
585
+ *
586
+ * Still a MINIMUM with the window, not a replacement for it: a deployment that shortens `stallMs` below
587
+ * this is asking for a tighter agent, and a throttle threshold above its own stall window could never
588
+ * fire. So the effective threshold is `min(stallMs / 2, this)` — one rule, both directions.
589
+ */
590
+ export declare const WORKFLOW_THROTTLE_SLOW_MS = 90000;
554
591
  /** CC `j_d` (206-pretty.js:17501680; 198 `Mxl`): max stalled-attempt retries per `ctx.agent` call
555
592
  * (initial + 5 = 6 attempts). */
556
593
  export declare const WORKFLOW_AGENT_MAX_RETRIES = 5;
@@ -21,6 +21,7 @@ import { OUTPUT_TOOL_NAME } from "../core/runner/synthetic-tools.js";
21
21
  import { compileOutputSchema } from "../core/runner/strict-output-schema.js";
22
22
  import { WorkflowBudgetExceededError, WorkflowNestingError, WorkflowAgentBlockedError, WorkflowAgentParkedError, WorkflowAgentSchemaError, WorkflowAgentStalledError, WorkflowMaxAgentsError, WORKFLOW_SPAWN_BLOCKED_ERROR_CODE } from "./workflow-types.js";
23
23
  import { WorkflowScriptError } from "./workflow-meta.js";
24
+ import { STALL_CONNECT_MS, STALL_FIRST_TOKEN_MS } from "../brain/timeout.js";
24
25
  export * from "./workflow-types.js";
25
26
  const MAX_TRANSCRIPT_CHARS = 4000;
26
27
  const WORKFLOW_RESULT_MAX = 4000;
@@ -149,6 +150,7 @@ export function workflowAgentCallKey(ordinal, spec, opts) {
149
150
  signal: undefined,
150
151
  taskId: undefined,
151
152
  sessionId: undefined,
153
+ checkpointStore: undefined,
152
154
  tools: spec.tools?.map((t) => t.name).slice().sort(),
153
155
  mcp: spec.mcp?.map((m) => m.name).slice().sort(),
154
156
  outputSchema: opts.schema ?? spec.outputSchema,
@@ -278,7 +280,8 @@ function assertFanOutOptionsShape(v, position) {
278
280
  `Nothing was run.`);
279
281
  }
280
282
  }
281
- export const WORKFLOW_AGENT_STALL_MS = 180_000;
283
+ export const WORKFLOW_AGENT_STALL_MS = STALL_CONNECT_MS + STALL_FIRST_TOKEN_MS;
284
+ export const WORKFLOW_THROTTLE_SLOW_MS = 90_000;
282
285
  export const WORKFLOW_AGENT_MAX_RETRIES = 5;
283
286
  export const WORKFLOW_AGENT_THROTTLE_BACKOFF_MS = 45_000;
284
287
  function readLegPendingApproval(result, announce) {
@@ -1604,7 +1607,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1604
1607
  result.structuredOutput === undefined &&
1605
1608
  (result.stats.outputTokens ?? Number.POSITIVE_INFINITY) < 50 &&
1606
1609
  lastStopReason === undefined &&
1607
- durationMs > stallMs * 0.5;
1610
+ durationMs > Math.min(stallMs * 0.5, WORKFLOW_THROTTLE_SLOW_MS);
1608
1611
  if (degraded && attempts === 1 && !throttleRetried && drive === undefined) {
1609
1612
  throttleRetried = true;
1610
1613
  lastAttemptReason = "throttled";
@@ -1689,7 +1692,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1689
1692
  ...(err instanceof WorkflowAgentBlockedError ? { code: WORKFLOW_SPAWN_BLOCKED_ERROR_CODE } : err instanceof WorkflowBudgetExceededError ? { code: err.code } : {}),
1690
1693
  message: boundedRedactedSummary(err instanceof Error ? err.message : String(err), 500),
1691
1694
  },
1692
- stats: { turns: 0, tokens: 0, costMicroUsd: 0 },
1695
+ stats: { turns: 0, tokens: 0, costMicroUsd: 0, usageMissing: true },
1693
1696
  }, label).catch(() => undefined);
1694
1697
  if (!((err instanceof WorkflowBudgetExceededError || err instanceof WorkflowAgentParkedError) && rec.startedAt === undefined))
1695
1698
  await journaled;
@@ -1801,7 +1804,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1801
1804
  sessionId: "",
1802
1805
  terminal: { kind: "failed", code: refusal.code, message: boundedRedactedSummary(refusal.message, 500) },
1803
1806
  result: boundedRedactedSummary(refusal.message, 500),
1804
- stats: { turns: 0, tokens: 0, costMicroUsd: 0 },
1807
+ stats: { turns: 0, tokens: 0, costMicroUsd: 0, usageMissing: true },
1805
1808
  }).catch(() => undefined);
1806
1809
  throw refusal;
1807
1810
  }
@@ -1875,7 +1878,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1875
1878
  sessionId: "",
1876
1879
  terminal: { kind: "failed", code: WORKFLOW_SPAWN_BLOCKED_ERROR_CODE, message: boundedRedactedSummary(err.message, 500) },
1877
1880
  result: boundedRedactedSummary(err.message, 500),
1878
- stats: { turns: 0, tokens: 0, costMicroUsd: 0 },
1881
+ stats: { turns: 0, tokens: 0, costMicroUsd: 0, usageMissing: true },
1879
1882
  }).catch(() => undefined);
1880
1883
  }
1881
1884
  recordFailed();
@@ -2224,6 +2227,21 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
2224
2227
  err = parkedError();
2225
2228
  run.error = err instanceof Error ? err.message : String(err);
2226
2229
  closeAbandonedAgents(run.endedAt);
2230
+ {
2231
+ let thrownCode;
2232
+ try {
2233
+ thrownCode = err !== null && (typeof err === "object" || typeof err === "function") ? err.code : undefined;
2234
+ }
2235
+ catch {
2236
+ thrownCode = undefined;
2237
+ }
2238
+ const firstFailed = run.agents
2239
+ .filter((a) => a.status === "failed" && typeof a.errorCode === "string" && a.errorCode !== "")
2240
+ .sort((a, b) => (a.endedAt ?? Number.POSITIVE_INFINITY) - (b.endedAt ?? Number.POSITIVE_INFINITY))[0];
2241
+ const code = typeof thrownCode === "string" && thrownCode !== "" ? thrownCode : firstFailed?.errorCode;
2242
+ if (code !== undefined)
2243
+ run.errorCode = code;
2244
+ }
2227
2245
  restampPhaseFailures();
2228
2246
  const failedRunFailures = countAgentFailures();
2229
2247
  if (failedRunFailures > 0)
@@ -97,7 +97,8 @@ export function createTaskServer(opts) {
97
97
  res.on("close", () => {
98
98
  if (!res.writableEnded) {
99
99
  clientGone = true;
100
- void stream.interrupt();
100
+ stream.interrupt().catch(() => {
101
+ });
101
102
  void stream.destroy();
102
103
  }
103
104
  });
@@ -6,6 +6,7 @@ import { shellQuote } from "./search.js";
6
6
  import { isNotebookPath } from "./notebook.js";
7
7
  import { sharpImageDownsampler } from "../../core/mcp.js";
8
8
  import { deliverEngineNotice } from "../../core/types.js";
9
+ import { announceEnvKnob } from "../../core/env-knob-announce.js";
9
10
  import { openSystemReminder } from "../../core/reminder-mint.js";
10
11
  export const MAX_READ_BYTES = 256 * 1024;
11
12
  export const SLICED_READ_MAX_BYTES = 64 * 1024 * 1024;
@@ -181,10 +182,15 @@ export function bashTimeoutCapsSec(caps) {
181
182
  const BASH_DEFAULT_MAX_OUTPUT_CHARS = 30_000;
182
183
  const BASH_MAX_OUTPUT_CHARS_CEILING = 150_000;
183
184
  export function bashMaxOutputChars() {
184
- const raw = Number(process.env.BASH_MAX_OUTPUT_LENGTH);
185
- if (!Number.isFinite(raw) || raw <= 0)
185
+ const rawEnv = process.env.BASH_MAX_OUTPUT_LENGTH;
186
+ if (rawEnv === undefined || rawEnv.trim() === "")
186
187
  return BASH_DEFAULT_MAX_OUTPUT_CHARS;
187
- return Math.min(Math.floor(raw), BASH_MAX_OUTPUT_CHARS_CEILING);
188
+ const raw = Number(rawEnv);
189
+ if (!Number.isInteger(raw) || raw <= 0) {
190
+ announceEnvKnob(`BASH_MAX_OUTPUT_LENGTH=${rawEnv} was ignored — it is not a positive whole number of characters. Using ${BASH_DEFAULT_MAX_OUTPUT_CHARS} instead.`);
191
+ return BASH_DEFAULT_MAX_OUTPUT_CHARS;
192
+ }
193
+ return Math.min(raw, BASH_MAX_OUTPUT_CHARS_CEILING);
188
194
  }
189
195
  export const FILE_PATH_PARAMS = {
190
196
  file_path: Type.String({ description: "File path (within the configured root)." }),
@@ -65,7 +65,7 @@ export interface ReadDenyMatcher {
65
65
  * exactly one. Unknown tier names refuse loudly (#123) — this array is the validation source and the
66
66
  * admin-face vocabulary, never re-spelled downstream.
67
67
  */
68
- export declare const READ_DENY_BUILTIN_TIERS: readonly ["credentials", "shell-history", "browser", "wallet"];
68
+ export declare const READ_DENY_BUILTIN_TIERS: readonly ["credentials", "shell-history", "browser", "wallet", "agent-config"];
69
69
  /** One member of {@link READ_DENY_BUILTIN_TIERS}. */
70
70
  export type ReadDenyBuiltinTier = (typeof READ_DENY_BUILTIN_TIERS)[number];
71
71
  /**
@@ -90,17 +90,33 @@ export interface ReadDenyBuiltinRow {
90
90
  * and a false refusal on a fixture is the cheap direction (deny errs strict).
91
91
  *
92
92
  * NOT listed, deliberately: `.env`/`.env.*` (ruled out — workspace material; the WRITE guard still
93
- * covers them); `*.pem`/`*.key`/`*.p12`/`*.pfx` (repo certificates/test keys are routinely READ
94
- * refusing them breaks ordinary work, the write guard covers the mutation direction); `.git/hooks`/
95
- * `.git/config` (reads are harmless; writes are the escalation and stay guarded); `.claude*`/
96
- * `.mcp.json` (agent config is routinely read for debugging); the engine's own data root
97
- * (transcript integrity policy owns it with an ask on the write face).
93
+ * covers them; the ENGINE's own `.env` under its data root is a different file and IS listed, see the
94
+ * agent-config tier); `*.pem`/`*.key`/`*.p12`/`*.pfx` (repo certificates/test keys are routinely
95
+ * READ refusing them breaks ordinary work, the write guard covers the mutation direction);
96
+ * `.git/hooks`/`.git/config` (reads are harmless; writes are the escalation and stay guarded); the
97
+ * engine's own data root as a WHOLE (transcript integrity policy owns it with an ask on the write
98
+ * face, and the memory library the model is told to read and edit lives under it).
99
+ *
100
+ * agent-config tier — the agent's OWN configuration files whose `env` / server blocks carry API keys
101
+ * in cleartext (the shell's `settings.json` env block, a project `.mcp.json` server env, the engine
102
+ * host's data-root `.env`). A structured read of one of these used to land the key verbatim in the
103
+ * tool result and therefore in the session record. Rows name FILES, not the config DIRECTORIES:
104
+ * `.sema`/`.claude` also hold agents, skills, hooks and (under the shell's layout,
105
+ * `~/.sema/engine-data`) the engine data root with the memory library — material the model reads
106
+ * and edits as ordinary work, and a directory-wide row would need a data-root passthrough (one more
107
+ * rule) to keep it readable. The write face guards the directories (`sensitive-path-policy.ts`): a
108
+ * write there is self-authorization; a read of a non-credential file there is debugging. Token
109
+ * STORES of the same tooling (`.credentials.json`, `.codex/auth.json`, …) are credentials rows, not
110
+ * agent-config rows: a deployment that switches this tier off to debug its agent configuration must
111
+ * not also expose the OAuth stores beside it.
98
112
  */
99
113
  export declare const READ_FACE_BUILTIN_DENY_TABLE: readonly ReadDenyBuiltinRow[];
100
114
  /**
101
115
  * The DEFAULT tier selection (#245 ruling ②): every tier EXCEPT `shell-history` — history files are
102
- * unrestricted unless a deployment opts the tier in. Derived, not re-listed: a future tier added to
103
- * the closed set is default-ON unless this expression is deliberately revisited.
116
+ * unrestricted unless a deployment opts the tier in. Derived, not re-listed: a tier added to the
117
+ * closed set is default-ON unless this expression is deliberately revisited (`agent-config` joined
118
+ * default-ON this way: an unconfigured deployment refuses the agent's own key-bearing settings files;
119
+ * a deployment that debugs agent configuration selects `tiers` without it).
104
120
  */
105
121
  export declare const READ_DENY_DEFAULT_TIERS: readonly ReadDenyBuiltinTier[];
106
122
  /**