@sema-agent/core 7.16.0 → 7.17.1

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 (207) hide show
  1. package/CHANGELOG.md +209 -0
  2. package/dist/agents/builtin-agents.d.ts +3 -3
  3. package/dist/agents/cross-session-envelope.d.ts +0 -1
  4. package/dist/agents/cross-session-envelope.js +0 -1
  5. package/dist/agents/peer-notices.d.ts +0 -9
  6. package/dist/agents/peer-notices.js +0 -63
  7. package/dist/agents/subagent.js +7 -15
  8. package/dist/brain/request-params.d.ts +2 -2
  9. package/dist/config/defaults.d.ts +0 -7
  10. package/dist/config/defaults.js +0 -2
  11. package/dist/core/artifact-host.d.ts +218 -0
  12. package/dist/core/artifact-host.js +92 -0
  13. package/dist/core/ask-origin.d.ts +29 -3
  14. package/dist/core/ask-origin.js +17 -9
  15. package/dist/core/ask-question.d.ts +25 -0
  16. package/dist/core/ask-question.js +4 -0
  17. package/dist/core/ask-unresolvable-notice.d.ts +30 -21
  18. package/dist/core/ask-unresolvable-notice.js +16 -8
  19. package/dist/core/auto-compaction.d.ts +10 -10
  20. package/dist/core/auto-mode-arming.d.ts +2 -2
  21. package/dist/core/background-agent-store.d.ts +5 -5
  22. package/dist/core/background-shell.d.ts +1 -1
  23. package/dist/core/checkpoint-execution-record.d.ts +0 -3
  24. package/dist/core/checkpoint-execution-record.js +0 -4
  25. package/dist/core/checkpoint-store.d.ts +71 -99
  26. package/dist/core/checkpoint-store.js +9 -16
  27. package/dist/core/context-edit.d.ts +36 -104
  28. package/dist/core/context-edit.js +37 -60
  29. package/dist/core/effective-path-target.d.ts +68 -8
  30. package/dist/core/effective-path-target.js +42 -30
  31. package/dist/core/engine-notice.d.ts +46 -32
  32. package/dist/core/env-budget.d.ts +31 -0
  33. package/dist/core/env-budget.js +39 -0
  34. package/dist/core/exec-output-tail.d.ts +5 -5
  35. package/dist/core/file-history-store.js +3 -6
  36. package/dist/core/fs-write-gate-policy.d.ts +1 -1
  37. package/dist/core/fs-write-gate-policy.js +3 -3
  38. package/dist/core/gate-lanes.js +9 -15
  39. package/dist/core/governance-codes.d.ts +1 -1
  40. package/dist/core/governance-codes.js +2 -0
  41. package/dist/core/hands-band.d.ts +1 -1
  42. package/dist/core/hooks.d.ts +18 -17
  43. package/dist/core/lsp-session.d.ts +1 -1
  44. package/dist/core/lsp.d.ts +1 -1
  45. package/dist/core/lsp.js +5 -4
  46. package/dist/core/mcp.d.ts +5 -5
  47. package/dist/core/mcp.js +4 -48
  48. package/dist/core/memory-engine/engine.js +2 -1
  49. package/dist/core/memory-engine/layout.d.ts +18 -6
  50. package/dist/core/memory-engine/layout.js +40 -21
  51. package/dist/core/memory-recall.d.ts +0 -8
  52. package/dist/core/memory.d.ts +1 -1
  53. package/dist/core/oracle-isolation.js +2 -2
  54. package/dist/core/parent-spec-seat.d.ts +66 -0
  55. package/dist/core/parent-spec-seat.js +47 -0
  56. package/dist/core/permission-rule-model.d.ts +11 -6
  57. package/dist/core/permission-rule-model.js +53 -37
  58. package/dist/core/permission-rules.js +8 -3
  59. package/dist/core/physical-path.d.ts +37 -0
  60. package/dist/core/physical-path.js +30 -0
  61. package/dist/core/protocol-naming.d.ts +2 -2
  62. package/dist/core/remote-env.d.ts +1 -1
  63. package/dist/core/retired-keys.d.ts +19 -0
  64. package/dist/core/retired-keys.js +15 -0
  65. package/dist/core/runner/active-skill-scope.js +3 -3
  66. package/dist/core/runner/assemble-result.d.ts +15 -13
  67. package/dist/core/runner/assemble-result.js +5 -4
  68. package/dist/core/runner/compaction-call-options.d.ts +25 -89
  69. package/dist/core/runner/contracts.d.ts +11 -10
  70. package/dist/core/runner/gate-exit.js +15 -1
  71. package/dist/core/runner/halt-attribution.d.ts +35 -0
  72. package/dist/core/runner/halt-attribution.js +16 -0
  73. package/dist/core/runner/memory-consolidation.d.ts +0 -2
  74. package/dist/core/runner/memory-consolidation.js +0 -1
  75. package/dist/core/runner/prepare-artifact.d.ts +52 -0
  76. package/dist/core/runner/prepare-artifact.js +63 -0
  77. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
  78. package/dist/core/runner/prepare-caps-and-workflow.js +2 -3
  79. package/dist/core/runner/prepare-config-doors.d.ts +3 -3
  80. package/dist/core/runner/prepare-config-doors.js +5 -2
  81. package/dist/core/runner/prepare-context-lane.d.ts +2 -2
  82. package/dist/core/runner/prepare-context-lane.js +2 -13
  83. package/dist/core/runner/prepare-file-history.d.ts +1 -1
  84. package/dist/core/runner/prepare-file-history.js +3 -52
  85. package/dist/core/runner/prepare-hands-readface.d.ts +4 -7
  86. package/dist/core/runner/prepare-hands-readface.js +3 -2
  87. package/dist/core/runner/prepare-inherited-gate.d.ts +5 -2
  88. package/dist/core/runner/prepare-inherited-gate.js +7 -8
  89. package/dist/core/runner/prepare-memory.d.ts +4 -6
  90. package/dist/core/runner/prepare-memory.js +4 -2
  91. package/dist/core/runner/prepare-policy-chain.d.ts +3 -3
  92. package/dist/core/runner/prepare-policy-chain.js +15 -30
  93. package/dist/core/runner/prepare-prompt-assembly.js +1 -1
  94. package/dist/core/runner/prepare-question-face.js +2 -1
  95. package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
  96. package/dist/core/runner/prepare-suspend-saga.js +0 -2
  97. package/dist/core/runner/prepare-task.js +12 -11
  98. package/dist/core/runner/prepare-wiring-manifest.js +9 -7
  99. package/dist/core/runner/resume-admission.js +2 -1
  100. package/dist/core/runner/resume-internals-and-config.js +4 -0
  101. package/dist/core/runner/retired-runner-deps.d.ts +3 -0
  102. package/dist/core/runner/retired-runner-deps.js +7 -0
  103. package/dist/core/runner/run-harness-handlers.js +5 -3
  104. package/dist/core/runner/run-settle-and-teardown.d.ts +1 -1
  105. package/dist/core/runner/run-settle-and-teardown.js +3 -1
  106. package/dist/core/runner/session-file-state-replay.js +3 -3
  107. package/dist/core/runner/session-rule-policy.js +3 -4
  108. package/dist/core/runner/stream-halt-verbs.js +1 -1
  109. package/dist/core/runner/stream-steer-verb.js +1 -4
  110. package/dist/core/runner/workspace-path.js +3 -0
  111. package/dist/core/runner-deps.d.ts +30 -49
  112. package/dist/core/safe-notify.d.ts +1 -1
  113. package/dist/core/sensitive-path-policy.d.ts +1 -1
  114. package/dist/core/sensitive-path-policy.js +11 -12
  115. package/dist/core/session.d.ts +2 -2
  116. package/dist/core/skill-tool-specifier.d.ts +1 -1
  117. package/dist/core/skills-directory.d.ts +1 -1
  118. package/dist/core/skills-directory.js +4 -3
  119. package/dist/core/spec-contract.js +5 -4
  120. package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
  121. package/dist/core/strategy-store.d.ts +8 -11
  122. package/dist/core/task-event.d.ts +20 -17
  123. package/dist/core/task-limits.d.ts +1 -1
  124. package/dist/core/task-notification.d.ts +5 -5
  125. package/dist/core/task-registry-agent.d.ts +5 -5
  126. package/dist/core/task-registry-monitor.d.ts +2 -2
  127. package/dist/core/task-registry-shared.d.ts +32 -28
  128. package/dist/core/task-registry-shared.js +4 -13
  129. package/dist/core/task-registry.d.ts +8 -8
  130. package/dist/core/task-result.d.ts +37 -37
  131. package/dist/core/task-spec.d.ts +5 -5
  132. package/dist/core/task-stream.d.ts +16 -11
  133. package/dist/core/tool-catalog-entries.d.ts +1 -0
  134. package/dist/core/tool-catalog-entries.js +6 -1
  135. package/dist/core/tool-conformance.d.ts +0 -2
  136. package/dist/core/tool-conformance.js +1 -3
  137. package/dist/core/tool-errors.d.ts +5 -0
  138. package/dist/core/tool-errors.js +1 -0
  139. package/dist/core/tool-policy.d.ts +66 -22
  140. package/dist/core/tool-policy.js +31 -5
  141. package/dist/core/tool-result-budget.d.ts +1 -1
  142. package/dist/core/tool-result-store.d.ts +11 -11
  143. package/dist/core/tool-spec.d.ts +34 -38
  144. package/dist/core/tools.d.ts +2 -2
  145. package/dist/core/trace.d.ts +12 -15
  146. package/dist/core/untrusted-text.d.ts +1 -1
  147. package/dist/core/wiring-manifest.d.ts +4 -3
  148. package/dist/core/workflow-journal-store.d.ts +11 -11
  149. package/dist/engine/execution-env/node-execution-env.d.ts +3 -2
  150. package/dist/engine/execution-env/node-execution-env.js +8 -5
  151. package/dist/engine/harness/agent-harness.d.ts +47 -4
  152. package/dist/engine/harness/agent-harness.js +52 -9
  153. package/dist/engine/loop/agent-loop.js +15 -8
  154. package/dist/engine/loop/types.d.ts +5 -0
  155. package/dist/index.d.ts +20 -14
  156. package/dist/index.js +16 -10
  157. package/dist/internal/harness.d.ts +1 -0
  158. package/dist/orchestration/run-spec.d.ts +3 -0
  159. package/dist/orchestration/run-spec.js +3 -3
  160. package/dist/orchestration/run-workflow-tool.d.ts +12 -6
  161. package/dist/orchestration/run-workflow-tool.js +23 -5
  162. package/dist/orchestration/workflow-primitives.d.ts +4 -2
  163. package/dist/orchestration/workflow-primitives.js +1 -4
  164. package/dist/orchestration/workflow-script-store.js +9 -25
  165. package/dist/orchestration/workflow-types.d.ts +34 -4
  166. package/dist/orchestration/workflow-types.js +2 -0
  167. package/dist/orchestration/workflow.d.ts +39 -7
  168. package/dist/orchestration/workflow.js +237 -113
  169. package/dist/prompt-assembly/assemble.d.ts +2 -2
  170. package/dist/prompt-assembly/assemble.js +9 -9
  171. package/dist/prompt-assembly/composer.d.ts +6 -7
  172. package/dist/prompt-assembly/composer.js +10 -10
  173. package/dist/prompt-assembly/epoch.js +1 -1
  174. package/dist/prompt-assembly/event-registry.d.ts +0 -21
  175. package/dist/prompt-assembly/packs/sema-default.js +30 -30
  176. package/dist/prompt-assembly/types.d.ts +14 -14
  177. package/dist/prompts/coordinator.d.ts +0 -13
  178. package/dist/prompts/coordinator.js +0 -3
  179. package/dist/stores/cc/task-list-store.js +2 -10
  180. package/dist/stores/file/checkpoint-store.js +1 -5
  181. package/dist/stores/file/file-history-store.js +2 -2
  182. package/dist/stores/file/fs-atomic.d.ts +15 -18
  183. package/dist/stores/file/fs-atomic.js +4 -14
  184. package/dist/stores/file/mailbox-store.d.ts +7 -11
  185. package/dist/stores/file/mailbox-store.js +4 -11
  186. package/dist/stores/file/permission-rule-store.d.ts +4 -35
  187. package/dist/stores/file/permission-rule-store.js +4 -148
  188. package/dist/tools/artifact/artifact-text.d.ts +62 -0
  189. package/dist/tools/artifact/artifact-text.js +68 -0
  190. package/dist/tools/artifact/artifact-tool.d.ts +52 -0
  191. package/dist/tools/artifact/artifact-tool.js +351 -0
  192. package/dist/tools/artifact/local-stub.d.ts +26 -0
  193. package/dist/tools/artifact/local-stub.js +297 -0
  194. package/dist/tools/fs/bash-readonly-classifier.d.ts +90 -8
  195. package/dist/tools/fs/bash-readonly-classifier.js +294 -70
  196. package/dist/tools/fs/fs-bash.d.ts +14 -10
  197. package/dist/tools/fs/fs-bash.js +50 -33
  198. package/dist/tools/fs/fs-search-tools.js +5 -2
  199. package/dist/tools/fs/fs-shared.d.ts +2 -0
  200. package/dist/tools/fs/fs-shared.js +3 -11
  201. package/dist/tools/fs/index.d.ts +4 -0
  202. package/dist/tools/fs/index.js +4 -1
  203. package/dist/tools/fs/safety.d.ts +117 -1
  204. package/dist/tools/fs/safety.js +131 -31
  205. package/dist/tools/fs/search.js +8 -6
  206. package/package.json +2 -1
  207. package/test/export-surface.snapshot.json +119 -39
@@ -1,5 +1,7 @@
1
1
  import { Type } from "typebox";
2
2
  import { isAbsolute } from "node:path";
3
+ import { applyParentSpecSeat, parentSpecSeatOfCtx } from "../core/parent-spec-seat.js";
4
+ import { stripSpawnTurnQuestionFace } from "../core/ask-question.js";
3
5
  import { withDelegationProvenance } from "../core/tool-policy.js";
4
6
  import { errorResult } from "../core/tools.js";
5
7
  import { isHighSurrogate, isLowSurrogate } from "../core/surrogate-safe-slice.js";
@@ -448,10 +450,6 @@ function unparkedDurablePauseReason(d) {
448
450
  function errorKindClause(c) {
449
451
  return c !== undefined ? ` (error_kind: ${c.errorKind}, retryable: ${c.retryable})` : "";
450
452
  }
451
- function stripSpawnTurnQuestionFace(spec) {
452
- const { onQuestion: spawnTurnQuestionFace, ...rest } = spec;
453
- return { spec: rest, internalsFlag: spawnTurnQuestionFace !== undefined ? { questionFaceStripped: true } : {} };
454
- }
455
453
  const FAILED_SESSION_RETAIN_TTL_MS = 15 * 60 * 1000;
456
454
  const PARTIAL_FINDINGS_MAX_CHARS = 1200;
457
455
  const BG_NOTIFY_DRAIN_WINDOW_MS = 2_000;
@@ -1487,6 +1485,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
1487
1485
  prepareArguments: (args) => foldGeneralPurposeAlias(args, generalPurposeShadowed),
1488
1486
  execute: async (args, ctx) => {
1489
1487
  const a = foldGeneralPurposeAlias(args, generalPurposeShadowed);
1488
+ const parentSpecSeat = parentSpecSeatOfCtx(ctx);
1490
1489
  const reviveClaim = ctx.reviveClaim;
1491
1490
  if (reviveClaim !== undefined && opts.background === undefined) {
1492
1491
  return {
@@ -2074,6 +2073,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2074
2073
  ...(childProvenanceRef !== undefined && provenanceRequest !== undefined ? { delegationProvenance: { ref: childProvenanceRef, contentSafety: provenanceRequest } } : {}),
2075
2074
  ...(treeScope !== undefined ? { registryScope: treeScope } : {}),
2076
2075
  ...(ctx.inheritedGateForChildren ? { inheritedGate: ctx.inheritedGateForChildren() } : {}),
2076
+ ...(ctx.questionFaceStripped === true ? { questionFaceStripped: true } : {}),
2077
2077
  ownOrgAdmissionRef: { current: reviveClaim !== undefined ? readDurableOrgAdmission(reviveClaim.row) : undefined },
2078
2078
  peerSelfRef: (() => {
2079
2079
  const ref = createPeerSelfRef(treeScope ?? "default");
@@ -2138,7 +2138,6 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2138
2138
  ? ctx.thinkingLevel !== undefined ? { thinking: ctx.thinkingLevel } : {}
2139
2139
  : childThinkingSeat(def?.thinking, deploymentSubagentThinking(runnerRoleMap(opts.runner)), ctx.thinkingLevel);
2140
2140
  const provenanceAgentName = agentName ?? def?.name;
2141
- const childMemoryPersistenceCapable = ctx.memoryPersistenceCapable === false ? false : (def?.memoryPersistenceCapable ?? ctx.memoryPersistenceCapable);
2142
2141
  const childOnAsk = ctx.onAsk !== undefined
2143
2142
  ? withDelegationProvenance(ctx.onAsk, {
2144
2143
  parentToolCallId: ctx.toolCallId,
@@ -2146,7 +2145,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2146
2145
  ...(provenanceAgentName !== undefined ? { agentName: provenanceAgentName } : {}),
2147
2146
  })
2148
2147
  : undefined;
2149
- const buildChildSpec = (signal) => ({
2148
+ const buildChildSpec = (signal) => applyParentSpecSeat({
2150
2149
  objective: prompt,
2151
2150
  ...childSeat,
2152
2151
  ...childThinking,
@@ -2154,30 +2153,23 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2154
2153
  systemPrompt: childSystemPrompt,
2155
2154
  ...(def?.memory ? { memory: def.memory } : {}),
2156
2155
  ...(def?.skills?.length ? { skills: def.skills } : {}),
2157
- ...(ctx.principal !== undefined ? { principal: ctx.principal } : {}),
2156
+ ...(def?.memoryPersistenceCapable !== undefined ? { memoryPersistenceCapable: def.memoryPersistenceCapable } : {}),
2158
2157
  ...(ctx.checkpointStoreForChildren !== undefined ? { checkpointStore: ctx.checkpointStoreForChildren } : {}),
2159
2158
  ...(childOnAsk !== undefined ? { onAsk: childOnAsk } : {}),
2160
- ...(ctx.onQuestion !== undefined ? { onQuestion: ctx.onQuestion } : {}),
2161
2159
  ...(ctx.interactiveTools === false ? { interactiveTools: false } : {}),
2162
2160
  ...(ctx.handsReadOnly === true ? { handsReadOnly: true } : {}),
2163
2161
  ...(ctx.readFace === "roots" ? { readFace: "roots" } : {}),
2164
2162
  ...(ctx.readDenyPatterns !== undefined ? { readDenyPatterns: [...ctx.readDenyPatterns] } : {}),
2165
- ...(childMemoryPersistenceCapable !== undefined ? { memoryPersistenceCapable: childMemoryPersistenceCapable } : {}),
2166
- ...(ctx.oneShot === true ? { oneShot: true } : {}),
2167
- ...(ctx.clientContext !== undefined ? { clientContext: ctx.clientContext } : {}),
2168
2163
  ...(ctx.excludeTools !== undefined ? { excludeTools: [...ctx.excludeTools] } : {}),
2169
2164
  ...(ctx.deferTools !== undefined ? { deferTools: [...ctx.deferTools] } : {}),
2170
2165
  ...(ctx.alwaysLoadTools !== undefined ? { alwaysLoadTools: [...ctx.alwaysLoadTools] } : {}),
2171
2166
  ...(ctx.restoreGatedTools !== undefined ? { restoreGatedTools: ctx.restoreGatedTools === true ? true : [...ctx.restoreGatedTools] } : {}),
2172
2167
  ...(ctx.promptProfile !== undefined ? { promptProfile: ctx.promptProfile } : {}),
2173
- ...(ctx.additionalDirectories !== undefined ? { additionalDirectories: [...ctx.additionalDirectories] } : {}),
2174
- ...(ctx.additionalReadDirectories !== undefined ? { additionalReadDirectories: [...ctx.additionalReadDirectories] } : {}),
2175
- ...(ctx.envFacts !== undefined ? { envFacts: { ...ctx.envFacts } } : {}),
2176
2168
  ...(ctx.getApiKeyAndHeaders !== undefined ? { getApiKeyAndHeaders: ctx.getApiKeyAndHeaders } : {}),
2177
2169
  enableBlockedReport: true,
2178
2170
  limits: childLimits,
2179
2171
  signal,
2180
- });
2172
+ }, parentSpecSeat);
2181
2173
  const tryRetainChild = async (ledger, entryTtlMs, preMintedSessionId, durableProbe, releaseAnchor, internalsExtra) => {
2182
2174
  if (!ledger)
2183
2175
  return undefined;
@@ -43,8 +43,8 @@ export declare const AUTH_CARRIER_NAMES: ReadonlySet<string>;
43
43
  * case-variant; the WINNER'S spelling and value survive (a single-spelling config — every existing
44
44
  * deployment — is byte-identical on the wire).
45
45
  *
46
- * EXEMPT: the auth carriers (`authorization` / `x-api-key`, any case) pass through with the exact
47
- * legacy spread semantics (same-spelling override only, no case-fold dedup), so that the credential
46
+ * EXEMPT: the auth carriers (`authorization` / `x-api-key`, any case) pass through with plain
47
+ * spread semantics (same-spelling override only, no case-fold dedup), so that the credential
48
48
  * enforcement (`applyRouteCredentialHeaders`, route-adjudicator.ts) stays the ONE authority over
49
49
  * auth spelling and this layer never becomes a second, subtly different one.
50
50
  *
@@ -29,13 +29,6 @@ export declare const RETAIN_DEFAULT_MAX = 16;
29
29
  * the same key (see the enforcement site for the exact retained-window semantics). */
30
30
  export declare const DELEGATION_MAX_CONCURRENT_DEFAULT = 20;
31
31
  export declare const DELEGATION_MAX_PER_SESSION_DEFAULT = 200;
32
- /** Subagent transcript persistence — orphan adoption (CC parity: after a restart a deployment
33
- * auto-adopts at most this many stale rows whose transcript mtime is inside the window; older ones
34
- * stay MANUALLY continuable for the whole retention period — never deleted by the window). The
35
- * TRIGGER is deployment-owned (core has no daemon and never revives runs nobody asked for); these
36
- * constants are the shared vocabulary so every deployment adopts by the same numbers. */
37
- export declare const ORPHAN_ADOPT_WINDOW_MS_DEFAULT: number;
38
- export declare const ORPHAN_ADOPT_MAX_DEFAULT = 20;
39
32
  /** Subagent transcript persistence — the default transcript retention period (CC
40
33
  * cleanupPeriodDays parity). Core ships NO sweeper (design/151 ruling 2): the blessed path is the
41
34
  * deployment calling `TaskRegistry.reapDurableAgents` with `maxAgeMs` derived from this (boot +
@@ -2,8 +2,6 @@ export const RETAIN_DEFAULT_TTL_MS = 30 * 60 * 1000;
2
2
  export const RETAIN_DEFAULT_MAX = 16;
3
3
  export const DELEGATION_MAX_CONCURRENT_DEFAULT = 20;
4
4
  export const DELEGATION_MAX_PER_SESSION_DEFAULT = 200;
5
- export const ORPHAN_ADOPT_WINDOW_MS_DEFAULT = 48 * 60 * 60 * 1000;
6
- export const ORPHAN_ADOPT_MAX_DEFAULT = 20;
7
5
  export const SUBAGENT_TRANSCRIPT_RETENTION_DAYS_DEFAULT = 30;
8
6
  export const SESSION_DEFAULT_TTL_DAYS = 7;
9
7
  export const RUNNING_AGENT_OBSERVE_EVERY_BEATS = 4;
@@ -0,0 +1,218 @@
1
+ import type { AssertAllKeysHandled } from "./ask-origin.js";
2
+ /** The model-facing action words. `publish` is the default action and the one every host must declare. */
3
+ export declare const ARTIFACT_ACTIONS: readonly ["publish", "list", "read", "list_files", "read_file"];
4
+ export type ArtifactAction = (typeof ARTIFACT_ACTIONS)[number];
5
+ /** Which face the host serves. `"local-stub"` = publish-only local storage with `eval-stub://` links;
6
+ * `"hosted"` = a hosting plane that mints its own URLs. Declared by the host, never detected. */
7
+ export declare const ARTIFACT_PLANES: readonly ["hosted", "local-stub"];
8
+ export type ArtifactPlane = (typeof ARTIFACT_PLANES)[number];
9
+ /** The `publishContext` word a publish carries (default `"plain"`). */
10
+ export declare const ARTIFACT_PUBLISH_CONTEXTS: readonly ["pr_review", "workshop", "whiteboard", "prototype", "plain"];
11
+ export type ArtifactPublishContext = (typeof ARTIFACT_PUBLISH_CONTEXTS)[number];
12
+ /** The `list` scope words. */
13
+ export declare const ARTIFACT_LIST_SCOPES: readonly ["mine", "shared", "all"];
14
+ export type ArtifactListScope = (typeof ARTIFACT_LIST_SCOPES)[number];
15
+ /**
16
+ * The content-origin class of the mounted tool, derived from the declared plane: a hosted plane hands
17
+ * back host-controlled bytes (URLs, titles, page content) and is classified external; the local stub
18
+ * reads nothing back from outside the process. Plane-keyed, never implementation-keyed.
19
+ */
20
+ export declare const ARTIFACT_PLANE_CONTENT_ORIGIN: {
21
+ readonly hosted: "external";
22
+ readonly "local-stub": "local";
23
+ };
24
+ export type ContentOriginTableCoversEveryPlane = AssertAllKeysHandled<Exclude<ArtifactPlane, keyof typeof ARTIFACT_PLANE_CONTENT_ORIGIN>>;
25
+ /** The host method each declared action is served by; the mount-time validator probes the method's presence. */
26
+ export declare const ARTIFACT_ACTION_HOST_METHOD: {
27
+ readonly publish: "publish";
28
+ readonly list: "list";
29
+ readonly read: "read";
30
+ readonly list_files: "listFiles";
31
+ readonly read_file: "readFile";
32
+ };
33
+ export type HostMethodTableCoversEveryAction = AssertAllKeysHandled<Exclude<ArtifactAction, keyof typeof ARTIFACT_ACTION_HOST_METHOD>>;
34
+ /** The typed failure words a host outcome may carry (`ok: false`). */
35
+ export declare const ARTIFACT_HOST_FAILURE_CODES: readonly ["conflict", "url_foreign", "forbidden", "payload_too_large", "not_found", "force_refused", "refused"];
36
+ export type ArtifactHostFailureCode = (typeof ARTIFACT_HOST_FAILURE_CODES)[number];
37
+ /** The tool-result `details.code` (and `tool_end.errorCode`) each host failure word is projected to. */
38
+ export declare const ARTIFACT_FAILURE_RESULT_CODE: {
39
+ readonly conflict: "artifact.publish_conflict";
40
+ readonly url_foreign: "artifact.url_foreign";
41
+ readonly forbidden: "artifact.forbidden";
42
+ readonly payload_too_large: "artifact.payload_too_large";
43
+ readonly not_found: "artifact.not_found";
44
+ readonly force_refused: "artifact.force_refused";
45
+ readonly refused: "artifact.refused";
46
+ };
47
+ export type ResultCodeTableCoversEveryHostFailure = AssertAllKeysHandled<Exclude<ArtifactHostFailureCode, keyof typeof ARTIFACT_FAILURE_RESULT_CODE>>;
48
+ /** The one result code that is not a host word: the host threw, timed out, or answered outside its contract. */
49
+ export declare const ARTIFACT_HOST_ERROR_CODE = "artifact.host_error";
50
+ export type ArtifactResultCode = (typeof ARTIFACT_FAILURE_RESULT_CODE)[ArtifactHostFailureCode] | typeof ARTIFACT_HOST_ERROR_CODE;
51
+ /** The `details.type` card the Artifact tool mints on every result. */
52
+ export declare const ARTIFACT_CARD = "artifact";
53
+ /**
54
+ * What a host declares at mount time. Invariants (a violation refuses the mount and announces
55
+ * `config.artifact_host_invalid`): `actions` non-empty, duplicate-free, within {@link ARTIFACT_ACTIONS},
56
+ * containing `"publish"`; `plane: "local-stub"` ⇒ `actions` is exactly `["publish"]`;
57
+ * `defaultVisibility` is `"private"`; both byte limits are positive finite integers; `multiFile` is a
58
+ * boolean; every declared action's host method exists; `plane: "local-stub"` ⇒ `storageDir` is a non-empty
59
+ * absolute path.
60
+ */
61
+ export interface ArtifactHostCapabilities {
62
+ plane: ArtifactPlane;
63
+ actions: ReadonlyArray<ArtifactAction>;
64
+ /** The on-disk store of a `"local-stub"` plane (required there; ignored on `"hosted"`). Contract: the store
65
+ * must lie OUTSIDE every root the run may write or read (the task root, `additionalDirectories`,
66
+ * `additionalReadDirectories`) — a store the model can reach through the fs tools is not a trust boundary;
67
+ * the engine refuses the mount (`config.artifact_host_invalid`) when it is inside one. */
68
+ storageDir?: string;
69
+ /** The only value this version mounts: a page is private until its owner shares it. */
70
+ defaultVisibility: "private";
71
+ maxFileBytes: number;
72
+ maxTotalBytes: number;
73
+ /** `false` ⇒ the publish arm takes one `file_path` only (`files[]` leaves the schema). */
74
+ multiFile: boolean;
75
+ }
76
+ /**
77
+ * Engine-minted call attribution and idempotency keys — closure-supplied, never a tool parameter.
78
+ * `taskId` / `sessionId` / `agentName` are audit attribution, never an authorization subject: a host
79
+ * instance is bound to one principal at construction. `runId` and `attemptId` are engine-minted trusted
80
+ * keys: `runId` is the same value `onMount` received; `attemptId` is one opaque value per tool call,
81
+ * stable across any host retry of that call, so a host MUST replay the first outcome for a repeated
82
+ * `attemptId` (a fresh publish never mints a second page, an update never advances twice).
83
+ */
84
+ export interface ArtifactCallContext {
85
+ taskId: string;
86
+ sessionId?: string;
87
+ agentName?: string;
88
+ runId: string;
89
+ attemptId: string;
90
+ }
91
+ export interface ArtifactFileContent {
92
+ path: string;
93
+ content: Uint8Array;
94
+ mediaType?: string;
95
+ }
96
+ export interface ArtifactPublishRequest {
97
+ slug?: string;
98
+ /** Update locator. Present without `baseVersion` ⇒ the host applies last-write-wins. */
99
+ url?: string;
100
+ files: ReadonlyArray<ArtifactFileContent>;
101
+ /** Reserved on the seam; the model face does not expose it in this version. A host MAY refuse it. */
102
+ removeFiles?: ReadonlyArray<string>;
103
+ title?: string;
104
+ description?: string;
105
+ favicon?: string;
106
+ label?: string;
107
+ baseVersion?: string;
108
+ force?: boolean;
109
+ publishContext: ArtifactPublishContext;
110
+ }
111
+ /** The reference shape every publish / list / read result carries. */
112
+ export interface ArtifactRef {
113
+ url: string;
114
+ slug: string;
115
+ version: string;
116
+ title?: string;
117
+ }
118
+ export interface ArtifactListRow extends ArtifactRef {
119
+ owner: "mine" | "shared";
120
+ updatedAt: string;
121
+ }
122
+ export interface ArtifactConflictSnapshot {
123
+ files: ReadonlyArray<ArtifactFileContent>;
124
+ title?: string;
125
+ description?: string;
126
+ favicon?: string;
127
+ label?: string;
128
+ }
129
+ export type ArtifactPublishOutcome = {
130
+ ok: true;
131
+ ref: ArtifactRef;
132
+ } | {
133
+ ok: false;
134
+ code: "conflict";
135
+ currentVersion: string;
136
+ current: ArtifactConflictSnapshot | "reread";
137
+ } | {
138
+ ok: false;
139
+ code: Exclude<ArtifactHostFailureCode, "conflict">;
140
+ message: string;
141
+ };
142
+ export type ArtifactListOutcome = {
143
+ ok: true;
144
+ rows: ReadonlyArray<ArtifactListRow>;
145
+ truncated: boolean;
146
+ } | {
147
+ ok: false;
148
+ code: "refused";
149
+ message: string;
150
+ };
151
+ export type ArtifactReadOutcome = {
152
+ ok: true;
153
+ ref: ArtifactRef;
154
+ content: string;
155
+ isolatedSummary: boolean;
156
+ } | {
157
+ ok: false;
158
+ code: "not_found" | "forbidden";
159
+ message: string;
160
+ };
161
+ export type ArtifactFilesOutcome = {
162
+ ok: true;
163
+ entries: ReadonlyArray<{
164
+ path: string;
165
+ bytes: number;
166
+ mediaType?: string;
167
+ }>;
168
+ } | {
169
+ ok: false;
170
+ code: "not_found" | "forbidden";
171
+ message: string;
172
+ };
173
+ export type ArtifactFileOutcome = {
174
+ ok: true;
175
+ file: ArtifactFileContent;
176
+ } | {
177
+ ok: false;
178
+ code: "not_found" | "forbidden";
179
+ message: string;
180
+ };
181
+ /**
182
+ * The hosting seat. Atomicity contract: no half version is ever observable — a reader sees the old
183
+ * complete version or the new complete version, never a mix of files / metadata / version number.
184
+ * The commit itself may be uncertain from the engine's side (the answer was lost after the host
185
+ * committed); the `attemptId` replay rule above is what makes a retry of that call converge.
186
+ */
187
+ export interface ArtifactHost {
188
+ capabilities(): ArtifactHostCapabilities;
189
+ /** Called at every materialization of a run (prepare and resume) with the engine-minted run id. */
190
+ onMount?(runId: string): void;
191
+ publish(req: ArtifactPublishRequest, ctx: ArtifactCallContext): Promise<ArtifactPublishOutcome>;
192
+ list?(req: {
193
+ scope: ArtifactListScope;
194
+ limit: number;
195
+ }, ctx: ArtifactCallContext): Promise<ArtifactListOutcome>;
196
+ read?(req: {
197
+ url: string;
198
+ }, ctx: ArtifactCallContext): Promise<ArtifactReadOutcome>;
199
+ listFiles?(req: {
200
+ url: string;
201
+ }, ctx: ArtifactCallContext): Promise<ArtifactFilesOutcome>;
202
+ readFile?(req: {
203
+ url: string;
204
+ path: string;
205
+ }, ctx: ArtifactCallContext): Promise<ArtifactFileOutcome>;
206
+ }
207
+ export type ArtifactHostCapabilitiesVerdict = {
208
+ ok: true;
209
+ capabilities: ArtifactHostCapabilities;
210
+ } | {
211
+ ok: false;
212
+ problem: string;
213
+ };
214
+ /**
215
+ * Read and validate a host's declaration. Every invariant of {@link ArtifactHostCapabilities} is judged
216
+ * here and nowhere else; a `problem` names the first violated one in operator-facing words.
217
+ */
218
+ export declare function readArtifactHostCapabilities(host: unknown): ArtifactHostCapabilitiesVerdict;
@@ -0,0 +1,92 @@
1
+ import { isAbsolute } from "node:path";
2
+ export const ARTIFACT_ACTIONS = ["publish", "list", "read", "list_files", "read_file"];
3
+ export const ARTIFACT_PLANES = ["hosted", "local-stub"];
4
+ export const ARTIFACT_PUBLISH_CONTEXTS = ["pr_review", "workshop", "whiteboard", "prototype", "plain"];
5
+ export const ARTIFACT_LIST_SCOPES = ["mine", "shared", "all"];
6
+ export const ARTIFACT_PLANE_CONTENT_ORIGIN = {
7
+ hosted: "external",
8
+ "local-stub": "local",
9
+ };
10
+ export const ARTIFACT_ACTION_HOST_METHOD = {
11
+ publish: "publish",
12
+ list: "list",
13
+ read: "read",
14
+ list_files: "listFiles",
15
+ read_file: "readFile",
16
+ };
17
+ export const ARTIFACT_HOST_FAILURE_CODES = ["conflict", "url_foreign", "forbidden", "payload_too_large", "not_found", "force_refused", "refused"];
18
+ export const ARTIFACT_FAILURE_RESULT_CODE = {
19
+ conflict: "artifact.publish_conflict",
20
+ url_foreign: "artifact.url_foreign",
21
+ forbidden: "artifact.forbidden",
22
+ payload_too_large: "artifact.payload_too_large",
23
+ not_found: "artifact.not_found",
24
+ force_refused: "artifact.force_refused",
25
+ refused: "artifact.refused",
26
+ };
27
+ export const ARTIFACT_HOST_ERROR_CODE = "artifact.host_error";
28
+ export const ARTIFACT_CARD = "artifact";
29
+ const isPositiveInt = (v) => typeof v === "number" && Number.isSafeInteger(v) && v > 0;
30
+ const wordOf = (set, v) => typeof v === "string" && set.includes(v);
31
+ export function readArtifactHostCapabilities(host) {
32
+ if (host === null || typeof host !== "object")
33
+ return { ok: false, problem: "artifactHost is not an object" };
34
+ const h = host;
35
+ if (typeof h.capabilities !== "function")
36
+ return { ok: false, problem: "artifactHost.capabilities is not a function" };
37
+ let raw;
38
+ try {
39
+ raw = h.capabilities.call(host);
40
+ }
41
+ catch (err) {
42
+ return { ok: false, problem: `artifactHost.capabilities() threw: ${err instanceof Error ? err.message : String(err)}` };
43
+ }
44
+ if (raw === null || typeof raw !== "object")
45
+ return { ok: false, problem: "artifactHost.capabilities() did not return an object" };
46
+ const c = raw;
47
+ const plane = c["plane"];
48
+ if (!wordOf(ARTIFACT_PLANES, plane))
49
+ return { ok: false, problem: `capabilities.plane must be one of ${ARTIFACT_PLANES.join(" | ")}` };
50
+ const actions = c["actions"];
51
+ if (!Array.isArray(actions) || actions.length === 0)
52
+ return { ok: false, problem: "capabilities.actions must be a non-empty array" };
53
+ for (const a of actions)
54
+ if (!wordOf(ARTIFACT_ACTIONS, a))
55
+ return { ok: false, problem: `capabilities.actions carries a word outside ${ARTIFACT_ACTIONS.join(" | ")}` };
56
+ if (new Set(actions).size !== actions.length)
57
+ return { ok: false, problem: "capabilities.actions carries a duplicate" };
58
+ if (!actions.includes("publish"))
59
+ return { ok: false, problem: 'capabilities.actions must include "publish"' };
60
+ if (plane === "local-stub" && (actions.length !== 1 || actions[0] !== "publish"))
61
+ return { ok: false, problem: 'a "local-stub" plane must declare exactly ["publish"]' };
62
+ if (c["defaultVisibility"] !== "private")
63
+ return { ok: false, problem: 'capabilities.defaultVisibility must be "private"' };
64
+ if (!isPositiveInt(c["maxFileBytes"]))
65
+ return { ok: false, problem: "capabilities.maxFileBytes must be a positive integer" };
66
+ if (!isPositiveInt(c["maxTotalBytes"]))
67
+ return { ok: false, problem: "capabilities.maxTotalBytes must be a positive integer" };
68
+ if (typeof c["multiFile"] !== "boolean")
69
+ return { ok: false, problem: "capabilities.multiFile must be a boolean" };
70
+ const storageDir = c["storageDir"];
71
+ if (plane === "local-stub" && (typeof storageDir !== "string" || storageDir.length === 0 || !isAbsolute(storageDir)))
72
+ return { ok: false, problem: 'a "local-stub" plane must declare its absolute storageDir' };
73
+ if (storageDir !== undefined && typeof storageDir !== "string")
74
+ return { ok: false, problem: "capabilities.storageDir must be a string when present" };
75
+ for (const a of actions) {
76
+ const method = ARTIFACT_ACTION_HOST_METHOD[a];
77
+ if (typeof host[method] !== "function")
78
+ return { ok: false, problem: `capabilities.actions declares "${a}" but artifactHost.${method} is not a function` };
79
+ }
80
+ return {
81
+ ok: true,
82
+ capabilities: Object.freeze({
83
+ plane,
84
+ actions: Object.freeze([...actions]),
85
+ defaultVisibility: "private",
86
+ maxFileBytes: c["maxFileBytes"],
87
+ maxTotalBytes: c["maxTotalBytes"],
88
+ multiFile: c["multiFile"],
89
+ ...(typeof storageDir === "string" ? { storageDir } : {}),
90
+ }),
91
+ };
92
+ }
@@ -246,11 +246,14 @@ export declare function classifierMayAnswer(origin: AskOrigin): boolean;
246
246
  * the same way every other per-call fact does: the gate stamps it on the surviving ask, the
247
247
  * synchronous sites read it off the decision they spread, and the park leg threads its own parameter.
248
248
  */
249
- export declare function persistedRuleMandateOf(marks: {
249
+ export declare function persistedRuleMandateOf(marks: MandateMarks): "operator_always" | "tool_marks" | "probe_mandate" | "probe_unanswered" | undefined;
250
+ /** The mark inputs of the two mandate derivations — properties of the tool SEAT (`egress`, `shellGated`,
251
+ * `irreversibility`) beside the two per-call facts the gate stamps. */
252
+ export interface MandateMarks {
250
253
  egress?: boolean;
251
254
  shellGated?: boolean;
252
255
  irreversibility?: "never" | "maybe" | "always";
253
- /** #502: the surviving ask's engine-stamped `probeMandated` — see
256
+ /** The surviving ask's engine-stamped `probeMandated` — see
254
257
  * {@link import("./types.js").ReversibilityVerdict.mandated}. */
255
258
  probeMandated?: boolean;
256
259
  /** The pass's own record that the probe was consulted and did not answer (`GatePass.probeUnanswered`):
@@ -258,7 +261,30 @@ export declare function persistedRuleMandateOf(marks: {
258
261
  * not see the pass and never passes it — a card may offer a rule the lane then discloses as shadowed
259
262
  * (loud, never a silent override), the same drift window the `matchedAskRule` conjunct already has. */
260
263
  probeUnanswered?: boolean;
261
- }): "operator_always" | "tool_marks" | "probe_mandate" | "probe_unanswered" | undefined;
264
+ }
265
+ /**
266
+ * The SAFETY AXIS of one ask — what makes its durable park NON-BUDGETABLE (`irreversible_ask`), judged
267
+ * from the same mark inputs as the rule-lane mandate plus the gate's own record of which tighten minted the
268
+ * ask. `egress` is the tool's static external-write mark, as it always was. `irreversible` is a mandate of
269
+ * THIS call: the tool's own tier (`tool_marks` — the deployment declared the tool may be irreversible,
270
+ * and a policy or hook asking first does not un-declare it), the operator's per-call doctrine
271
+ * (`operator_always`), the probe's structural verdict (`probe_mandate` / `probe_unanswered`), OR — under a
272
+ * shell-seat-installed `"maybe"` tier — the shell gate's own tighten having minted this ask (the doctrine's
273
+ * probe judged THIS command not reversible). A policy ask on a shell-gated tool that the doctrine's probe
274
+ * did not tighten and did not mandate is a plain ask: the tier was the probe's instrument, not a risk
275
+ * label, and the park stays the budgetable kind. Before this derivation the static tier alone set the bit,
276
+ * so every policy ask on Bash under `shellGate:"classify"` parked non-budgetable.
277
+ *
278
+ * Deliberately NOT the rule-lane predicate verbatim: a shell tighten's ask is rule-clearable (a person's
279
+ * past yes to this command shape stands) yet non-budgetable (a budget's mechanical yes is not a person's).
280
+ * The two derivations share every other row through {@link irreversibilityMandateOf}.
281
+ */
282
+ export declare function safetyAxisOf(marks: MandateMarks & {
283
+ readonly tightened: AskOriginFacts["tightened"];
284
+ }): {
285
+ egress: boolean;
286
+ irreversible: boolean;
287
+ } | undefined;
262
288
  /** The PROBE half of the mandated family (the deployment's read boundary, judged per call): neither a standing rule
263
289
  * nor the classifier clears these two words; `tool_marks`/`operator_always` bind the rule lane only (`safety_tighten`). */
264
290
  export declare function probeBoundaryMandate(word: ReturnType<typeof persistedRuleMandateOf>): boolean;
@@ -68,19 +68,27 @@ export function classifierMayAnswer(origin) {
68
68
  return CLASSIFIER_MAY_ANSWER[origin];
69
69
  }
70
70
  export function persistedRuleMandateOf(marks) {
71
+ const word = irreversibilityMandateOf(marks);
72
+ return probeBoundaryMandate(word) ? word : marks.egress === true ? "tool_marks" : word;
73
+ }
74
+ function irreversibilityMandateOf(marks) {
71
75
  return marks.probeMandated === true
72
76
  ? "probe_mandate"
73
77
  : marks.probeUnanswered === true
74
78
  ? "probe_unanswered"
75
- : marks.egress === true
76
- ? "tool_marks"
77
- : marks.shellGated === true
78
- ? marks.irreversibility === "always"
79
- ? "operator_always"
80
- : undefined
81
- : marks.irreversibility === "always" || marks.irreversibility === "maybe"
82
- ? "tool_marks"
83
- : undefined;
79
+ : marks.shellGated === true
80
+ ? marks.irreversibility === "always"
81
+ ? "operator_always"
82
+ : undefined
83
+ : marks.irreversibility === "always" || marks.irreversibility === "maybe"
84
+ ? "tool_marks"
85
+ : undefined;
86
+ }
87
+ export function safetyAxisOf(marks) {
88
+ const egress = marks.egress === true;
89
+ const irreversible = irreversibilityMandateOf(marks) !== undefined ||
90
+ (marks.shellGated === true && marks.irreversibility === "maybe" && marks.tightened === "shell_gate");
91
+ return egress || irreversible ? { egress, irreversible } : undefined;
84
92
  }
85
93
  export function probeBoundaryMandate(word) {
86
94
  return word === "probe_mandate" || word === "probe_unanswered";
@@ -348,3 +348,28 @@ source?: {
348
348
  principal?: string;
349
349
  sourceTaskId?: string;
350
350
  }, opts?: AskUserQuestionToolOptions): AgentTool;
351
+ /**
352
+ * The ONE producer of the "engine stripped the spawn turn's question face" fact. The strip (removing `onQuestion`
353
+ * from a child spec — or from the parent SPEC seat a child is built from — when the child OUTLIVES the request that
354
+ * spawned it) and the disclosure flag (`RunInternals.questionFaceStripped` → the wiring manifest's
355
+ * `question.wired: "stripped_bg_lane"` + the posture door's exemption) are PAIR-PRODUCED from this single expression —
356
+ * never inferred from spec shape, never minted anywhere else. Three families ride it: the Agent tool's session-scoped
357
+ * background lanes (spec-override form), its retain snapshots (destructure-drop form; the flag then rides
358
+ * `internalsSnapshot`, so a revived leg keeps the exemption), and the Workflow tool (submit-and-return: every agent it
359
+ * spawns outlives the launching request, so the host's seat is stripped before it reaches the launch site). WHY:
360
+ * `spec.onQuestion` is by contract a per-REQUEST lane torn down with its turn; handing it to a leg that runs hours later
361
+ * would strand the child asking into a closed surface (`AskUserQuestion` awaits the answer). A key REMOVED and a key set
362
+ * `undefined` resolve identically through the engine's `spec.onQuestion ?? deps.onQuestion` reads — a process-level
363
+ * `RunnerDeps.onQuestion` still reaches the child; only the turn-bound value drops. Trust posture: RunInternals is a
364
+ * PUBLIC trusted-caller parameter, so the flag is an HONESTY device (the engine disclosing its own strip), not a
365
+ * security gate. The flag states "a face WAS removed here" — minted only when one actually existed (an inherently
366
+ * faceless lane reads `absent`, not `stripped_bg_lane`, and collects no exemption).
367
+ */
368
+ export declare function stripSpawnTurnQuestionFace<S extends {
369
+ onQuestion?: OnQuestion;
370
+ }>(spec: S): {
371
+ spec: Omit<S, "onQuestion">;
372
+ internalsFlag: {
373
+ questionFaceStripped?: true;
374
+ };
375
+ };
@@ -378,3 +378,7 @@ export function createAskUserQuestionTool(onQuestion, source, opts) {
378
378
  },
379
379
  });
380
380
  }
381
+ export function stripSpawnTurnQuestionFace(spec) {
382
+ const { onQuestion: spawnTurnQuestionFace, ...rest } = spec;
383
+ return { spec: rest, internalsFlag: spawnTurnQuestionFace !== undefined ? { questionFaceStripped: true } : {} };
384
+ }
@@ -1,17 +1,22 @@
1
1
  /**
2
2
  * `delegation.ask_unresolvable` — the one mint of the notice the gate raises when an `ask` reached its
3
- * final deny with NOBODY having judged it: the approver consulted for the call (the run's own seat, or an
4
- * ancestor's frozen seat on a delegated child) answered the ROUTING question `unavailable` — no operator
5
- * is reachable for this ask and no durable approval park took the call afterwards. The deny itself is
6
- * older than this notice and stands unchanged (the model reads the sentence as its tool result, the wire
7
- * carries `tool_end.gate.settlement.kind:"approver_unavailable"`); what was missing was any face a PERSON
8
- * watching notices could see. An operator reading notices alone saw nothing, and the person whose
9
- * delegated work was refused learned it only by reading the child's transcript.
3
+ * final deny with NOBODY having judged it. Three ways that happens, one event class ("a call was denied
4
+ * fail-closed because its approval could not be resolved"), one closed cause set:
5
+ * · `approver_unavailable`the approver consulted for the call (the run's own seat, or an ancestor's
6
+ * frozen seat on a delegated child) answered the ROUTING question `unavailable` and no durable park
7
+ * took the call afterwards (`tool_end.gate.settlement.kind:"approver_unavailable"`);
8
+ * · `no_approver_wired` no approver (or, for a question, no question face) is wired for this run, or
9
+ * the seat is the deny posture string: the headless auto-deny (`settlement.kind:"no_approver"`);
10
+ * · `mandate_unreconstructible` — an inherited durable-approval mandate binds the ask and THIS leg has no
11
+ * park facility to honor it, so the delegated child refuses in-fold rather than let a synchronous layer
12
+ * resolve what its ancestor bound to a park (an in-fold deny; no settlement rides the frame).
13
+ * The deny itself is older than this notice and stands unchanged (the model reads the sentence as its
14
+ * tool result); what was missing was any face a PERSON watching notices could see — an operator reading
15
+ * notices alone saw nothing, and the person whose work was refused learned it only from a transcript.
10
16
  *
11
- * Two deny arms compose this settlement the marked-call intercept (an ancestor's seat answered
12
- * unavailable and the float found no park) and the resolver's own unavailable arm and both reach ONE
13
- * exit in the gate lanes, after the park re-route has had its one attempt. That exit is the only caller of
14
- * this factory: one call, one notice, once per tool call.
17
+ * ONE exit composes it: the gate's single deny exit (`exitGate`), which reads the settlement the deny
18
+ * carries or the cause an in-fold arm attested, after every park re-route has had its attempt. That exit is
19
+ * the only caller of this factory: one call, one notice, once per tool call.
15
20
  *
16
21
  * Audience `"user"`: the refused work is the user's own (their run, or a delegation their run made), and
17
22
  * the recovery — a checkpoint store with a durable approval opt-in so such asks can wait for a person, or
@@ -22,17 +27,21 @@
22
27
  * is pure.
23
28
  */
24
29
  import type { EngineNotice } from "./engine-notice.js";
25
- import type { SettlementKind } from "./gate-outcome.js";
30
+ /** WHY the ask could not be resolved — the closed set (see the module note for each word). */
31
+ export declare const ASK_UNRESOLVABLE_CAUSES: readonly ["approver_unavailable", "no_approver_wired", "mandate_unreconstructible"];
32
+ export type AskUnresolvableCause = (typeof ASK_UNRESOLVABLE_CAUSES)[number];
33
+ /** Membership test for {@link AskUnresolvableCause} — the word rides a notice detail across process boundaries. */
34
+ export declare function isAskUnresolvableCause(v: unknown): v is AskUnresolvableCause;
26
35
  /** What the gate knows at the deny exit — facts only, no words of the engine's vocabulary composed. */
27
36
  export interface AskUnresolvableFacts {
28
37
  /** The session whose call was refused — the notice's routing key, required (see module note). */
29
38
  readonly sessionId: string;
30
39
  /** The tool the ask was for (the message quotes it verbatim). */
31
40
  readonly toolName: string;
32
- /** The refused call's id — the correlation key to the `tool_end` frame carrying the same settlement. */
41
+ /** The refused call's id — the correlation key to the `tool_end` frame carrying the same deny. */
33
42
  readonly toolCallId: string;
34
- /** The settlement the deny composed always `approver_unavailable` today; carried as a fact, not re-derived. */
35
- readonly settlementKind: Extract<SettlementKind, "approver_unavailable">;
43
+ /** Why the ask could not be resolved carried as a fact from the exit, not re-derived here. */
44
+ readonly cause: AskUnresolvableCause;
36
45
  /** Whether a durable approval park lane was ARMED for this run (`true`: the park had its one attempt and
37
46
  * did not take the call — it declined or failed; `false`: no park lane at all, the usual shape). */
38
47
  readonly parkLaneExisted: boolean;
@@ -41,12 +50,12 @@ export interface AskUnresolvableFacts {
41
50
  * Compose the `delegation.ask_unresolvable` notice from the gate's deny-exit facts.
42
51
  *
43
52
  * @contract delegation.ask_unresolvable.single_mint — the `delegation.ask_unresolvable` notice is composed
44
- * by CORE from the gate's deny-exit FACTS (session, tool, call id, the composed settlement, whether a park
45
- * lane existed) at the ONE exit both unavailable deny arms reach; no other site spells the code or the
46
- * sentence, and an empty session or tool name is refused at the mint with a `TypeError` rather than
47
- * shaped into a notice that would route nowhere.
53
+ * by CORE from the gate's deny-exit FACTS (session, tool, call id, the cause, whether a park lane existed)
54
+ * at the ONE deny exit every unresolvable arm reaches; no other site spells the code or the sentence, and
55
+ * an empty session or tool name, or a cause outside the closed set, is refused at the mint with a
56
+ * `TypeError` rather than shaped into a notice that would route nowhere or say nothing.
48
57
  *
49
- * `detail: { sessionId, toolName, toolCallId, settlementKind, parkLaneExisted }` — `sessionId` is lifted
50
- * onto the typed key by {@link import("./engine-notice.js").deliverEngineNotice}, the one throat.
58
+ * `detail: { sessionId, toolName, toolCallId, cause, parkLaneExisted }` — `sessionId` is lifted onto the
59
+ * typed key by {@link import("./engine-notice.js").deliverEngineNotice}, the one throat.
51
60
  */
52
61
  export declare function askUnresolvableNotice(facts: AskUnresolvableFacts): EngineNotice;