@sema-agent/core 7.53.0 → 7.54.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 (244) hide show
  1. package/CHANGELOG.md +277 -1
  2. package/LICENSE +3 -1
  3. package/dist/agents/peer-session-drain.d.ts +0 -1
  4. package/dist/agents/peer-session-drain.js +0 -2
  5. package/dist/agents/send-message-tool.js +3 -4
  6. package/dist/agents/subagent.d.ts +3 -5
  7. package/dist/agents/subagent.js +18 -11
  8. package/dist/agents/tool-filter.d.ts +7 -4
  9. package/dist/agents/tool-filter.js +7 -3
  10. package/dist/core/a2a.js +4 -2
  11. package/dist/core/allow-entry.d.ts +1 -0
  12. package/dist/core/allow-entry.js +1 -0
  13. package/dist/core/approval-object.d.ts +87 -0
  14. package/dist/core/approval-object.js +85 -0
  15. package/dist/core/arg-summary.d.ts +3 -2
  16. package/dist/core/arg-summary.js +17 -11
  17. package/dist/core/ask-origin.d.ts +43 -27
  18. package/dist/core/ask-origin.js +20 -11
  19. package/dist/core/ask-question.d.ts +0 -1
  20. package/dist/core/ask-question.js +4 -67
  21. package/dist/core/ask-unresolvable-notice.d.ts +3 -2
  22. package/dist/core/auto-compaction.d.ts +49 -50
  23. package/dist/core/auto-compaction.js +41 -51
  24. package/dist/core/auto-mode-prompt.d.ts +4 -2
  25. package/dist/core/auto-mode-prompt.js +4 -3
  26. package/dist/core/canonical-json.d.ts +69 -0
  27. package/dist/core/canonical-json.js +130 -8
  28. package/dist/core/checkpoint-requirements.d.ts +13 -7
  29. package/dist/core/checkpoint-requirements.js +4 -1
  30. package/dist/core/checkpoint-store.d.ts +33 -18
  31. package/dist/core/checkpoint-store.js +5 -4
  32. package/dist/core/child-clamp-seats.d.ts +45 -6
  33. package/dist/core/child-clamp-seats.js +56 -4
  34. package/dist/core/consolidate-scope.d.ts +1 -1
  35. package/dist/core/cross-boundary-taint.d.ts +213 -0
  36. package/dist/core/cross-boundary-taint.js +196 -0
  37. package/dist/core/decision-receipt.d.ts +44 -1
  38. package/dist/core/decision-receipt.js +16 -0
  39. package/dist/core/effective-path-target.d.ts +3 -0
  40. package/dist/core/effective-path-target.js +4 -0
  41. package/dist/core/engine-notice.d.ts +34 -2
  42. package/dist/core/engine-notice.js +17 -1
  43. package/dist/core/engine-storage-backend.d.ts +73 -0
  44. package/dist/core/engine-storage-backend.js +1 -0
  45. package/dist/core/file-history-store.d.ts +6 -2
  46. package/dist/core/file-history-store.js +1 -0
  47. package/dist/core/gate-fold.js +10 -2
  48. package/dist/core/gate-lanes.js +30 -21
  49. package/dist/core/gate-outcome.d.ts +7 -3
  50. package/dist/core/governance-codes.d.ts +7 -2
  51. package/dist/core/governance-codes.js +6 -0
  52. package/dist/core/hooks.d.ts +14 -14
  53. package/dist/core/hooks.js +1 -2
  54. package/dist/core/locked-config.d.ts +28 -1
  55. package/dist/core/locked-config.js +30 -3
  56. package/dist/core/lsp.d.ts +1 -1
  57. package/dist/core/mcp-server-spec.d.ts +66 -9
  58. package/dist/core/mcp-server-spec.js +52 -19
  59. package/dist/core/mcp.js +5 -4
  60. package/dist/core/memory-engine/engine.js +2 -1
  61. package/dist/core/memory-engine/export-bundle.d.ts +1 -1
  62. package/dist/core/memory-engine/export-bundle.js +1 -1
  63. package/dist/core/memory-engine/file-backend.d.ts +2 -6
  64. package/dist/core/memory-engine/file-backend.js +1 -12
  65. package/dist/core/memory.d.ts +3 -3
  66. package/dist/core/mode-policy.d.ts +13 -8
  67. package/dist/core/mode-policy.js +3 -2
  68. package/dist/core/mods/kernel/receive.d.ts +2 -1
  69. package/dist/core/mods/kernel/receive.js +7 -2
  70. package/dist/core/parent-spec-seat.d.ts +10 -13
  71. package/dist/core/parent-spec-seat.js +14 -6
  72. package/dist/core/pause-registry.d.ts +35 -1
  73. package/dist/core/pause-registry.js +10 -0
  74. package/dist/core/permission-rule-consent.d.ts +18 -5
  75. package/dist/core/permission-rule-consent.js +13 -8
  76. package/dist/core/permission-rule-model.d.ts +29 -6
  77. package/dist/core/permission-rule-model.js +6 -1
  78. package/dist/core/permission-rule-org.d.ts +37 -0
  79. package/dist/core/permission-rule-org.js +34 -42
  80. package/dist/core/permission-rule-session.js +2 -2
  81. package/dist/core/permission-rule-store.d.ts +8 -0
  82. package/dist/core/permission-rule-store.js +1 -0
  83. package/dist/core/permission-rule-sync.js +3 -2
  84. package/dist/core/read-only-shell.d.ts +1 -1
  85. package/dist/core/read-only-shell.js +30 -9
  86. package/dist/core/remote-env.d.ts +41 -1
  87. package/dist/core/remote-env.js +18 -0
  88. package/dist/core/resume-startup-cause.d.ts +18 -0
  89. package/dist/core/resume-startup-cause.js +12 -0
  90. package/dist/core/runner/approval-feedback.d.ts +46 -0
  91. package/dist/core/runner/approval-feedback.js +41 -0
  92. package/dist/core/runner/compaction-call-options.d.ts +3 -3
  93. package/dist/core/runner/compaction-call-options.js +2 -2
  94. package/dist/core/runner/contracts.d.ts +33 -41
  95. package/dist/core/runner/cross-boundary-lane.d.ts +87 -0
  96. package/dist/core/runner/cross-boundary-lane.js +190 -0
  97. package/dist/core/runner/decide-continuation.d.ts +4 -4
  98. package/dist/core/runner/decide-continuation.js +3 -1
  99. package/dist/core/runner/gate-exit.d.ts +32 -12
  100. package/dist/core/runner/gate-exit.js +75 -33
  101. package/dist/core/runner/inherited-ask-grants.js +2 -2
  102. package/dist/core/runner/person-path-rows.d.ts +2 -2
  103. package/dist/core/runner/prepare-acquire-reconcile.d.ts +3 -4
  104. package/dist/core/runner/prepare-acquire-reconcile.js +3 -3
  105. package/dist/core/runner/prepare-ask-lane.js +3 -3
  106. package/dist/core/runner/prepare-caps-and-workflow.d.ts +6 -5
  107. package/dist/core/runner/prepare-caps-and-workflow.js +3 -3
  108. package/dist/core/runner/prepare-config-doors.d.ts +12 -15
  109. package/dist/core/runner/prepare-config-doors.js +8 -3
  110. package/dist/core/runner/prepare-context-lane.js +6 -3
  111. package/dist/core/runner/prepare-delegation-surface.d.ts +3 -2
  112. package/dist/core/runner/prepare-delegation-surface.js +2 -3
  113. package/dist/core/runner/prepare-execution-env.d.ts +1 -2
  114. package/dist/core/runner/prepare-execution-env.js +2 -1
  115. package/dist/core/runner/prepare-gate-stations.d.ts +2 -0
  116. package/dist/core/runner/prepare-gate-stations.js +4 -3
  117. package/dist/core/runner/prepare-hands-readface.d.ts +2 -2
  118. package/dist/core/runner/prepare-hands-readface.js +1 -1
  119. package/dist/core/runner/prepare-inherited-gate.d.ts +2 -0
  120. package/dist/core/runner/prepare-inherited-gate.js +1 -0
  121. package/dist/core/runner/prepare-park-ask.d.ts +1 -1
  122. package/dist/core/runner/prepare-park-ask.js +9 -3
  123. package/dist/core/runner/prepare-policy-chain.js +2 -7
  124. package/dist/core/runner/prepare-safety-scan.d.ts +7 -2
  125. package/dist/core/runner/prepare-safety-scan.js +34 -27
  126. package/dist/core/runner/prepare-suspend-saga.d.ts +2 -2
  127. package/dist/core/runner/prepare-suspend-saga.js +6 -2
  128. package/dist/core/runner/prepare-task.js +13 -10
  129. package/dist/core/runner/prepare-turn-wiring.d.ts +10 -5
  130. package/dist/core/runner/prepare-turn-wiring.js +14 -49
  131. package/dist/core/runner/prepare-wiring-manifest.d.ts +2 -2
  132. package/dist/core/runner/prepare-wiring-manifest.js +2 -2
  133. package/dist/core/runner/prepare-workspace-restore.js +2 -2
  134. package/dist/core/runner/resume-admission.js +9 -9
  135. package/dist/core/runner/resume-apply.js +4 -4
  136. package/dist/core/runner/resume-policy-outcome.d.ts +2 -2
  137. package/dist/core/runner/resume-policy-outcome.js +18 -15
  138. package/dist/core/runner/resume-preflight.js +2 -2
  139. package/dist/core/runner/resume-review-outcome.js +3 -3
  140. package/dist/core/runner/run-leg.js +57 -33
  141. package/dist/core/runner/run-settle-and-teardown.js +2 -0
  142. package/dist/core/runner/run-terminal-adoption.js +15 -4
  143. package/dist/core/runner/session-file-state-replay.d.ts +5 -2
  144. package/dist/core/runner/session-file-state-replay.js +3 -2
  145. package/dist/core/runner/stream-steer-verb.js +2 -2
  146. package/dist/core/runner/tool-face-overlay.d.ts +24 -6
  147. package/dist/core/runner/tool-face-overlay.js +24 -15
  148. package/dist/core/runner-deps.d.ts +9 -9
  149. package/dist/core/session-store.d.ts +35 -16
  150. package/dist/core/session-store.js +20 -12
  151. package/dist/core/{shell-lexer.d.ts → shell-run-candidates.d.ts} +36 -12
  152. package/dist/core/{shell-lexer.js → shell-run-candidates.js} +145 -225
  153. package/dist/core/shell-wrapper-table.d.ts +5 -3
  154. package/dist/core/shell-wrapper-table.js +1 -1
  155. package/dist/core/store-contracts/checkpoint-store-contract.js +2 -2
  156. package/dist/core/store-contracts/session-store-contract.d.ts +49 -0
  157. package/dist/core/store-contracts/session-store-contract.js +639 -0
  158. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +12 -0
  159. package/dist/core/store-contracts/session-store-incarnation-contract.js +5 -2
  160. package/dist/core/task-event.d.ts +6 -4
  161. package/dist/core/task-registry-agent.js +1 -0
  162. package/dist/core/task-registry-shared.d.ts +5 -0
  163. package/dist/core/task-registry.d.ts +7 -0
  164. package/dist/core/task-registry.js +17 -0
  165. package/dist/core/task-result.d.ts +1 -1
  166. package/dist/core/task-spec.d.ts +6 -6
  167. package/dist/core/tighten-task-spec.d.ts +6 -4
  168. package/dist/core/tighten-task-spec.js +2 -2
  169. package/dist/core/tool-catalog-entries.d.ts +1 -1
  170. package/dist/core/tool-catalog-entries.js +52 -52
  171. package/dist/core/tool-face-reader.d.ts +49 -0
  172. package/dist/core/tool-face-reader.js +94 -0
  173. package/dist/core/tool-face.d.ts +17 -8
  174. package/dist/core/tool-face.js +1 -0
  175. package/dist/core/tool-policy.d.ts +64 -23
  176. package/dist/core/tool-policy.js +62 -56
  177. package/dist/core/tool-roster.d.ts +46 -28
  178. package/dist/core/tool-roster.js +91 -14
  179. package/dist/core/tool-spec-members.js +2 -2
  180. package/dist/core/tool-spec.d.ts +20 -4
  181. package/dist/core/tools.js +2 -0
  182. package/dist/core/trace.d.ts +24 -2
  183. package/dist/core/types.d.ts +2 -2
  184. package/dist/core/types.js +1 -1
  185. package/dist/core/untrusted-text.d.ts +7 -0
  186. package/dist/core/untrusted-text.js +3 -8
  187. package/dist/core/user-attachment.d.ts +43 -0
  188. package/dist/core/user-attachment.js +92 -15
  189. package/dist/engine/compaction/compaction.d.ts +2 -2
  190. package/dist/engine/compaction/utils.d.ts +2 -2
  191. package/dist/engine/harness/types.d.ts +7 -4
  192. package/dist/engine/llm/types.d.ts +7 -2
  193. package/dist/engine/llm/types.js +2 -1
  194. package/dist/engine/session/repo-utils.d.ts +5 -0
  195. package/dist/engine/session/repo-utils.js +9 -0
  196. package/dist/engine/session/storage-base.js +2 -2
  197. package/dist/index.d.ts +23 -9
  198. package/dist/index.js +19 -8
  199. package/dist/internal/llm.d.ts +1 -1
  200. package/dist/internal/llm.js +1 -1
  201. package/dist/orchestration/run-workflow-tool.d.ts +3 -0
  202. package/dist/orchestration/run-workflow-tool.js +1 -0
  203. package/dist/orchestration/workflow-governance.d.ts +1 -1
  204. package/dist/orchestration/workflow.d.ts +3 -0
  205. package/dist/orchestration/workflow.js +2 -1
  206. package/dist/prompt-assembly/catalog/schema.d.ts +3 -0
  207. package/dist/prompt-assembly/catalog/schema.js +3 -0
  208. package/dist/prompt-assembly/overlay.js +2 -2
  209. package/dist/prompt-assembly/select.js +2 -2
  210. package/dist/prompts/default.d.ts +1 -1
  211. package/dist/shell/lex.d.ts +87 -8
  212. package/dist/shell/lex.js +283 -49
  213. package/dist/stores/file/index.d.ts +8 -6
  214. package/dist/tools/artifact/artifact-tool.js +10 -0
  215. package/dist/tools/fs/bash-program-position.d.ts +3 -15
  216. package/dist/tools/fs/bash-program-position.js +3 -10
  217. package/dist/tools/fs/bash-readonly-classifier.js +2 -0
  218. package/dist/tools/fs/fs-bash.d.ts +3 -3
  219. package/dist/tools/fs/fs-bash.js +72 -101
  220. package/dist/tools/fs/fs-read.d.ts +12 -0
  221. package/dist/tools/fs/fs-read.js +10 -9
  222. package/dist/tools/fs/fs-shared.d.ts +27 -31
  223. package/dist/tools/fs/fs-shared.js +12 -15
  224. package/dist/tools/fs/fs-write.js +1 -1
  225. package/dist/tools/fs/index.js +2 -2
  226. package/dist/tools/fs/read-deny-home.d.ts +1 -1
  227. package/dist/tools/fs/read-deny-home.js +5 -4
  228. package/dist/tools/fs/read-deny.d.ts +16 -3
  229. package/dist/tools/fs/read-deny.js +12 -9
  230. package/dist/tools/fs/safety.d.ts +7 -6
  231. package/dist/tools/fs/shell-read-bindings.d.ts +5 -3
  232. package/dist/tools/fs/shell-read-bindings.js +15 -16
  233. package/dist/tools/fs/shell-read-device.js +1 -1
  234. package/dist/tools/fs/shell-read-plan.js +36 -47
  235. package/dist/tools/fs/shell-read-word.d.ts +1 -6
  236. package/dist/tools/fs/shell-read-word.js +2 -20
  237. package/dist/tools/fs/shell-reentry-text.js +1 -1
  238. package/dist/tools/web.d.ts +2 -2
  239. package/package.json +4 -1
  240. package/test/export-surface.snapshot.json +5654 -5520
  241. package/wire-changes/core@7.53.0.json +30 -8
  242. package/wire-changes/core@7.54.0.json +2870 -0
  243. package/dist/core/shell-scan.d.ts +0 -60
  244. package/dist/core/shell-scan.js +0 -183
@@ -1 +1 @@
1
- export { createAssistantMessageEventStream, DATA_LABEL_UNSAFE, ensureToolTurnInvariants, isTextBlock, MODEL_OUTPUT_INVALID_CAUSES, quoteDataLabel, renderContentBlock, renderUserAttachmentView, snapshotActorAssertion, stampStreamDrops, streamDropsOf, stripEngineMetadata, TOOL_TURN_INCOMPLETE_CAUSES, TOOL_TURN_REPAIR_DROPS, userAttachmentOf } from "../engine/llm/index.js";
1
+ export { createAssistantMessageEventStream, DATA_LABEL_UNSAFE, ensureToolTurnInvariants, findToolByName, isTextBlock, MODEL_OUTPUT_INVALID_CAUSES, quoteDataLabel, renderContentBlock, renderUserAttachmentView, snapshotActorAssertion, stampStreamDrops, streamDropsOf, stripEngineMetadata, TOOL_TURN_INCOMPLETE_CAUSES, TOOL_TURN_REPAIR_DROPS, userAttachmentOf } from "../engine/llm/index.js";
@@ -192,6 +192,9 @@ export interface RunWorkflowToolDeps {
192
192
  * the ladder/gate placement. Absent ⇒ nothing extra travels (the rootSessionId chain is the
193
193
  * placement root through prepare's mint middle segment). */
194
194
  placementRoot?: string;
195
+ /** The host run tree's cross-boundary token (`RunInternals.crossBoundaryTree`), riding deps for the same auto-mounted-ctx reason as
196
+ * `rootSessionId` above; handed to the workflow run so every workflow-spawned agent joins the host tree's state. */
197
+ crossBoundaryTree?: import("../core/cross-boundary-taint.js").CrossBoundaryTreeToken;
195
198
  /** Process-local unified task registry. When present, RunWorkflow returns `task_id === runId` with a `w*` id. */
196
199
  taskRegistry?: TaskRegistry;
197
200
  /** design/115 P2 core slice: run-local task-notification sink for SDK event + live model XML injection.
@@ -494,6 +494,7 @@ async function mountRunWorkflowTool(d) {
494
494
  ...(d.originatingSessionId !== undefined ? { originatingSessionId: d.originatingSessionId } : {}),
495
495
  ...((ctx.rootSessionId ?? d.rootSessionId ?? d.originatingSessionId) !== undefined ? { rootSessionId: ctx.rootSessionId ?? d.rootSessionId ?? d.originatingSessionId } : {}),
496
496
  ...((ctx.placementRoot ?? d.placementRoot) !== undefined ? { placementRoot: ctx.placementRoot ?? d.placementRoot } : {}),
497
+ ...((ctx.crossBoundaryTreeForChildren ?? d.crossBoundaryTree) !== undefined ? { crossBoundaryTree: ctx.crossBoundaryTreeForChildren ?? d.crossBoundaryTree } : {}),
497
498
  ...(workflowTaskId !== undefined ? { runId: workflowTaskId } : {}),
498
499
  journalStore: d.journalStore,
499
500
  ...(resumeFromRunId !== undefined ? { resumeFromRunId } : {}),
@@ -92,7 +92,7 @@ export interface WorkflowAgentSpec extends ScriptClampSeats {
92
92
  * restricting value, an add-only row through the row's script and entry laws. A row marked inherited-only (its
93
93
  * restricting value switches off an operator-side record) is outside this set and is stripped like any other key.
94
94
  */
95
- export declare const WHITELIST_KEYS: readonly ("thinking" | "systemPrompt" | "objective" | "images" | "limits" | ScriptSeat | "modelName")[];
95
+ export declare const WHITELIST_KEYS: readonly ("objective" | "images" | "thinking" | "systemPrompt" | "limits" | ScriptSeat | "modelName")[];
96
96
  /**
97
97
  * #505 ② — ACCEPTED ALIAS SPELLINGS on the spec: `alias → the {@link WHITELIST_KEYS} member it means`.
98
98
  *
@@ -359,6 +359,9 @@ export interface RunWorkflowOptions {
359
359
  * placed at the ladder/gate root. Absent ⇒ nothing extra travels (the rootSessionId chain is the
360
360
  * placement root through prepare's mint middle segment). */
361
361
  placementRoot?: string;
362
+ /** The host run tree's cross-boundary token (`RunInternals.crossBoundaryTree`, threaded by the run-workflow tool): every
363
+ * workflow-spawned agent joins the tree it names, so a bit a workflow agent sets is read by the host and its siblings. */
364
+ crossBoundaryTree?: import("../core/cross-boundary-taint.js").CrossBoundaryTreeToken;
362
365
  /** design/149 — the PROCESS-level child observer (same seam as
363
366
  * {@link import("../core/types.js").RunnerDeps.onBackgroundChildEvent}). When set, every workflow
364
367
  * agent emits spawn/tick/terminal {@link import("../core/types.js").BackgroundChildEvent} frames with a
@@ -120,7 +120,7 @@ export function applyWorkflowAgentType(spec, agentType, registry) {
120
120
  if (out.memoryPersistenceCapable === undefined && def.memoryPersistenceCapable !== undefined)
121
121
  out.memoryPersistenceCapable = def.memoryPersistenceCapable;
122
122
  if (def.allowTools !== undefined || def.denyTools !== undefined) {
123
- const perType = createAllowDenyPolicy({ ...(def.allowTools ? { allow: def.allowTools } : {}), ...(def.denyTools ? { deny: deniedToolNamesOf(`workflow agentType "${def.name}"`, def.denyTools) } : {}), scope: "narrowing" });
123
+ const perType = createAllowDenyPolicy({ ...(def.allowTools ? { allow: def.allowTools } : {}), ...(def.denyTools ? { deny: deniedToolNamesOf(`workflow agentType "${def.name}"`, def.denyTools) } : {}) });
124
124
  out.toolPolicy = out.toolPolicy ? combinePolicies(out.toolPolicy, perType) : perType;
125
125
  }
126
126
  return out;
@@ -725,6 +725,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
725
725
  ...(agentCeiling !== undefined ? { toolCeiling: agentCeiling } : {}),
726
726
  ...((opts.rootSessionId ?? opts.originatingSessionId) !== undefined ? { rootSessionId: opts.rootSessionId ?? opts.originatingSessionId } : {}),
727
727
  ...(opts.placementRoot !== undefined ? { placementRoot: opts.placementRoot } : {}),
728
+ ...(opts.crossBoundaryTree !== undefined ? { crossBoundaryTree: opts.crossBoundaryTree } : {}),
728
729
  ...(opts.originatingSessionId !== undefined ? { parentSessionId: opts.originatingSessionId } : {}),
729
730
  };
730
731
  const captureFloorSeatsNow = async () => {
@@ -33,6 +33,9 @@ export interface BandRow {
33
33
  * task (`TaskSpec.promptRole`); absent ⇒ `general`. */
34
34
  export declare const HOST_ROLES: readonly ["general", "code", "coordinator", "cowork"];
35
35
  export type HostRole = (typeof HOST_ROLES)[number];
36
+ /** Membership test for {@link HostRole} — the one reading of "is this a role base the engine selects": the task door
37
+ * (`TaskSpec.promptRole`) and the overlay grammar (`when.host.role`) both ask it. */
38
+ export declare function isHostRole(v: unknown): v is HostRole;
36
39
  /** The host form: `minimal` leaves the core-behavior band out (the locked bands stay). Written only by
37
40
  * `TaskSpec.promptForm`; absent ⇒ `standard`. */
38
41
  export declare const HOST_FORMS: readonly ["standard", "minimal"];
@@ -3,6 +3,9 @@ export function isWordOf(words, v) {
3
3
  }
4
4
  export const OVERLAY_OPS = Object.freeze(["replace", "append", "add", "drop"]);
5
5
  export const HOST_ROLES = Object.freeze(["general", "code", "coordinator", "cowork"]);
6
+ export function isHostRole(v) {
7
+ return isWordOf(HOST_ROLES, v);
8
+ }
6
9
  export const HOST_FORMS = Object.freeze(["standard", "minimal"]);
7
10
  export const PROMPT_TRAITS = Object.freeze(["lean_prompt", "fable_5_mitigations"]);
8
11
  export const PROMPT_FACT_NAMES = Object.freeze([
@@ -3,7 +3,7 @@ import { describeFault, isPlainRecord, receive } from "../core/mods/kernel/recei
3
3
  import { egressText } from "../core/untrusted-egress.js";
4
4
  import { bandOf, isBandId } from "./bands.js";
5
5
  import { SEMA_DEFAULT_CATALOG, catalogMessage } from "./catalog/index.js";
6
- import { HOST_FORMS, HOST_ROLES, OVERLAY_OPS, PROMPT_FACT_NAMES, PROMPT_TRAITS, isWordOf, } from "./catalog/schema.js";
6
+ import { HOST_FORMS, HOST_ROLES, OVERLAY_OPS, PROMPT_FACT_NAMES, PROMPT_TRAITS, isHostRole, isWordOf, } from "./catalog/schema.js";
7
7
  import { RENDERERS } from "./renderers.js";
8
8
  import { canonicalWhen } from "./select.js";
9
9
  export const OVERLAY_SOURCE_ORDER = Object.freeze(["bundled", "task", "center", "deploy"]);
@@ -125,7 +125,7 @@ function whenViolation(when) {
125
125
  if (typeof h !== "object" || h === null || Array.isArray(h))
126
126
  return `"when.host" is not an object`;
127
127
  for (const [k, v] of Object.entries(h)) {
128
- if (k === "role" ? !isWordOf(HOST_ROLES, v) : k === "form" ? !isWordOf(HOST_FORMS, v) : true) {
128
+ if (k === "role" ? !isHostRole(v) : k === "form" ? !isWordOf(HOST_FORMS, v) : true) {
129
129
  return `"when.host" carries ${str(k)} = ${str(v)} (role: ${HOST_ROLES.join(" | ")}; form: ${HOST_FORMS.join(" | ")})`;
130
130
  }
131
131
  }
@@ -1,4 +1,4 @@
1
- import { HOST_ROLES, isWordOf } from "./catalog/schema.js";
1
+ import { HOST_ROLES, isHostRole } from "./catalog/schema.js";
2
2
  export function promptTraitsOf(facts) {
3
3
  const traits = new Set();
4
4
  if (facts.promptProfile !== "classic")
@@ -32,7 +32,7 @@ export function resolveHost(task) {
32
32
  code: "config.prompt_selector_invalid",
33
33
  });
34
34
  };
35
- const role = task.promptRole === undefined ? undefined : isWordOf(HOST_ROLES, task.promptRole) ? task.promptRole : refuse("promptRole", task.promptRole, HOST_ROLES);
35
+ const role = task.promptRole === undefined ? undefined : isHostRole(task.promptRole) ? task.promptRole : refuse("promptRole", task.promptRole, HOST_ROLES);
36
36
  const form = task.promptForm === undefined ? undefined : task.promptForm === "minimal" ? "minimal" : refuse("promptForm", task.promptForm, ["minimal"]);
37
37
  return Object.freeze({ ...(role !== undefined ? { role } : {}), ...(form !== undefined ? { form } : {}) });
38
38
  }
@@ -46,7 +46,7 @@ export declare const MEMORY_SAFETY = "## Memory\nWhen you save a durable note to
46
46
  export declare const MEMORY_HYGIENE = "What's worth saving \u2014 organize by topic, not by when it happened:\n- who the user is \u2014 role, expertise, durable preferences;\n- guidance the user gave on HOW to work \u2014 corrections and confirmed approaches, with the reason why;\n- ongoing goals or constraints that aren't derivable from the code or its history;\n- pointers to external resources (URLs, dashboards, tickets).\n\nHygiene:\n- Convert relative dates (\"yesterday\", \"last week\") to absolute dates, so the note stays interpretable later.\n- Before saving, check what memory already holds: update an existing note rather than writing a near-duplicate, and remove a note that turns out to be wrong.\n- Don't save what the code, its history, or this conversation already records (structure, past fixes, transient task state). If asked to remember something obvious, save what was non-obvious about it instead.";
47
47
  /**
48
48
  * The full memory guidance = safety + hygiene, in the original order. Kept as a single exported
49
- * constant for back-compat and for a deployment composing the no-consolidation form in one piece;
49
+ * constant for back-compat (RL-1336-memory-guidance) and for a deployment composing the no-consolidation form in one piece;
50
50
  * core injects none of the three (RB-482 #16). Prefer {@link MEMORY_SAFETY} / {@link MEMORY_HYGIENE}
51
51
  * when you need the split.
52
52
  */
@@ -12,7 +12,7 @@ export declare const EXPANSION_KINDS: readonly ["parameter", "command", "process
12
12
  export type ExpansionKind = (typeof EXPANSION_KINDS)[number];
13
13
  /** Why a word's value cannot be read off the text. Each member is a spelling whose value the text does not
14
14
  * contain — never "the lexer did not look". */
15
- export declare const UNRESOLVED_REASONS: readonly ["ansi_c_escape", "locale_string", "parameter_text", "subscript_text", "compound_array_value"];
15
+ export declare const UNRESOLVED_REASONS: readonly ["ansi_c_escape", "locale_string", "parameter_text", "subscript_text", "compound_array_value", "trailing_backslash"];
16
16
  export type UnresolvedReason = (typeof UNRESOLVED_REASONS)[number];
17
17
  /** The sentence a consumer hands on for an unresolved word: every reason is one a face ASKS about (the locale
18
18
  * string included — its translation is not in the text), and a deny row is still judged on the written spelling
@@ -23,9 +23,10 @@ export declare const UNRESOLVED_SENTENCE: {
23
23
  readonly parameter_text: "a parameter expansion carries text this lexer does not read";
24
24
  readonly subscript_text: "a subscript carries text this lexer does not read";
25
25
  readonly compound_array_value: "a compound assignment value is a list this lexer does not read";
26
+ readonly trailing_backslash: "a backslash ends the command, which the shell versions read differently (3.2 drops it, 5 keeps it)";
26
27
  };
27
28
  /** Why a command yields no segments: bash's own syntax errors and the two constructs this lexer cannot delimit. */
28
- export declare const PARSE_FAILURE_REASONS: readonly ["unterminated_single_quote", "unterminated_double_quote", "unterminated_ansi_c", "unterminated_subscript", "unterminated_compound_value", "unterminated_command_substitution", "unterminated_process_substitution", "unterminated_parameter_expansion", "unterminated_heredoc", "undelimited_substitution", "redirect_without_target", "heredoc_without_delimiter", "heredoc_delimiter_expands"];
29
+ export declare const PARSE_FAILURE_REASONS: readonly ["unterminated_single_quote", "unterminated_double_quote", "unterminated_ansi_c", "unterminated_subscript", "unterminated_compound_value", "unterminated_command_substitution", "unterminated_process_substitution", "unterminated_parameter_expansion", "unterminated_arithmetic_expansion", "unterminated_heredoc", "undelimited_substitution", "redirect_without_target", "heredoc_without_delimiter", "heredoc_delimiter_expands"];
29
30
  export type ParseFailureReason = (typeof PARSE_FAILURE_REASONS)[number];
30
31
  export declare const PARSE_FAILURE_SENTENCE: {
31
32
  readonly unterminated_single_quote: "a single-quoted word never closes";
@@ -36,6 +37,7 @@ export declare const PARSE_FAILURE_SENTENCE: {
36
37
  readonly unterminated_command_substitution: "a command substitution never terminates";
37
38
  readonly unterminated_process_substitution: "a process substitution never terminates";
38
39
  readonly unterminated_parameter_expansion: "a parameter expansion never terminates";
40
+ readonly unterminated_arithmetic_expansion: "an arithmetic expansion never terminates";
39
41
  readonly unterminated_heredoc: "a here-document body never terminates";
40
42
  readonly undelimited_substitution: "a command substitution carries a construct this lexer cannot delimit";
41
43
  readonly redirect_without_target: "a redirection names no target";
@@ -76,7 +78,9 @@ export interface WordPiece {
76
78
  }
77
79
  export interface WordExpansion {
78
80
  readonly kind: ExpansionKind;
79
- /** `"many"` where the result is word-split (an unquoted parameter or command substitution, a brace list, a glob). */
81
+ /** `"many"` where the result may be zero or several words: word-split (an unquoted parameter, command or arithmetic
82
+ * substitution), a brace list, a glob, or a parameter expanded once per element even in double quotes (`"$@"`,
83
+ * `"${a[@]}"`, `"${!prefix@}"`). */
80
84
  readonly yields: "one" | "many";
81
85
  /** The text between the delimiters, where there is one (a substitution body, a brace list, a tilde-prefix). */
82
86
  readonly body?: string;
@@ -93,6 +97,9 @@ export interface WordExpansion {
93
97
  readonly written: string;
94
98
  readonly judged: string;
95
99
  };
100
+ /** The expansions a brace list or a bracket class encloses (`{${a[n]},x}`, `[$x]`): they run before the pattern is
101
+ * formed. Absent when it encloses none. */
102
+ readonly inner?: readonly WordExpansion[];
96
103
  }
97
104
  export interface QuotedSpan {
98
105
  /** Offsets into {@link LexedWord.raw}, `[start, end)`; the quote characters belong to their span. */
@@ -154,7 +161,8 @@ export interface LexedWord {
154
161
  * output form and `0` for an input form, is the reader's to apply). */
155
162
  export interface LexedRedirect {
156
163
  readonly kind: "redirect";
157
- readonly op: "<" | ">" | ">>" | "<<<" | "&>" | "&>>" | ">&" | "<&";
164
+ /** `<>` opens its target for reading AND writing (creating it), on descriptor 0 unless one is written. */
165
+ readonly op: "<" | ">" | ">>" | "<<<" | "<>" | "&>" | "&>>" | ">&" | "<&";
158
166
  readonly fd?: number;
159
167
  readonly target?: LexedWord;
160
168
  readonly dup?: string;
@@ -192,6 +200,9 @@ export interface LexedSegment {
192
200
  readonly tokens: readonly LexedToken[];
193
201
  /** The bodies of the substitutions found in THIS segment (its words and its redirection targets), in order. */
194
202
  readonly substitutions: readonly string[];
203
+ /** The bodies among {@link substitutions} of a function or value substitution (`${ list; }`, `${| list; }`): their list
204
+ * runs in the CURRENT shell, so what it assigns or where it moves outlives the word. Absent when there is none. */
205
+ readonly currentShell?: readonly string[];
195
206
  }
196
207
  export interface LexedCommand {
197
208
  readonly segments: readonly LexedSegment[];
@@ -203,6 +214,10 @@ export interface LexParseFailure {
203
214
  readonly reason: ParseFailureReason;
204
215
  readonly at: number;
205
216
  };
217
+ /** The segments the walk read before it stopped, the last one holding the tokens it had read when it stopped. Bash runs
218
+ * each complete line before it parses the next one (`rm -r x` on line 1 runs although line 2 never closes its quote),
219
+ * so a reader asking what the text MAY run reads these as runs; a reader asking what the text DOES reads the failure. */
220
+ readonly readBefore: readonly LexedSegment[];
206
221
  }
207
222
  /** A command: its segments, or the one reason it has none. */
208
223
  export type ShellLex = LexedCommand | LexParseFailure;
@@ -222,7 +237,7 @@ export declare const SHELL_CONTROL_PREFIXES: ReadonlySet<string>;
222
237
  * Does the `$` at `at` OPEN a quote form (`$'…'` / `$"…"`)? ONE question, asked by every walker that has to
223
238
  * answer it. Escaped means an ODD run of backslashes in front of it: `\$'y'` is a literal `$`, while `x\\$'y'`
224
239
  * is a literal backslash followed by a real ANSI-C quote. Counting the run is what a one-character peek behind
225
- * cannot do.
240
+ * cannot do. The quote may follow line continuations bash removes first (`$\<newline>"…"` is `$"…"`).
226
241
  */
227
242
  export declare function dollarOpensQuote(text: string, at: number): boolean;
228
243
  /** The `'` that CLOSES the ANSI-C quote opened at `open`, or `undefined` when none does. `\'` does not close
@@ -236,12 +251,75 @@ export declare function literalTildeSpelling(word: string): string;
236
251
  export declare function argvOf(segment: LexedSegment): readonly LexedWord[];
237
252
  /** The two-spelling view of {@link argvOf}, for the readers that compare spellings. */
238
253
  export declare function wordsOf(segment: LexedSegment): SegmentTokens;
239
- /** The substitution bodies of an UNQUOTED here-document body: `$(…)` and backticks are live there, quotes are
240
- * ordinary characters, a backslash protects the next character. `undefined` when a substitution never closes. */
254
+ /** One expansion of an unquoted here-document body; `unresolved` where the parameter text is one this lexer does not
255
+ * read (`${x:-$(…)}`, a nested `${…}`) — what it may assign or run is then not in the facts. */
256
+ export interface HeredocExpansion {
257
+ readonly expansion: WordExpansion;
258
+ readonly unresolved?: "parameter_text";
259
+ /** A function or value substitution ({@link opensCurrentShellSubstitution}): its list runs in the current shell. */
260
+ readonly currentShell?: true;
261
+ }
262
+ /** The expansions of an UNQUOTED here-document body, in order: `$(…)`, `$((…))`, `$[…]` and backticks, `${ …; }` /
263
+ * `${| …; }`, `${…}` and `$NAME`. The body expands as double-quoted text does: quotes are ordinary characters, a backslash
264
+ * protects the next character, nothing is word-split. `undefined` when an expansion never closes. */
265
+ export declare function expansionsInHeredocBody(body: string): readonly HeredocExpansion[] | undefined;
266
+ /** The substitution bodies of an UNQUOTED here-document body (the command and arithmetic members of
267
+ * {@link expansionsInHeredocBody}, a function / value substitution marked `currentShell`). `undefined` when an
268
+ * expansion never closes. */
241
269
  export declare function substitutionsInHeredocBody(body: string): {
242
270
  readonly body: string;
243
271
  readonly kind: "command" | "arithmetic";
272
+ readonly currentShell?: true;
244
273
  }[] | undefined;
274
+ /** Can expanding an unquoted here-document body run a program the text does not show ({@link expansionHidesRun} over
275
+ * its expansions)? A body whose expansions cannot be delimited, a parameter text this lexer does not read, and a
276
+ * process-substitution opener (text to bash in a body; read here as one — the side that judges more) all count. */
277
+ export declare function heredocBodyHidesRun(body: string): boolean;
278
+ /** A `${…}` body read once, past its head ({@link PARAMETER_HEAD}): the texts bash evaluates as arithmetic — the subscript
279
+ * (`a[i]`, `#a[i]`, `!a[i]`: up to the first `]`, or to the end where none closes) and a substring expansion's offset and
280
+ * length (`x:off:len`, `*:N+=1`, after a closed subscript or none; not the `:-` `:=` `:+` `:?` operators). A line
281
+ * continuation is removed first. The one reading of the body grammar: the deny lanes and auto-background ask whether
282
+ * either text names a variable ({@link expansionHidesRun}), the read station re-enters what either runs. */
283
+ export declare function parameterArithmeticTexts(body: string): {
284
+ readonly subscript?: string;
285
+ readonly offset?: string;
286
+ };
287
+ /**
288
+ * Can evaluating this expansion run a program the text does not show? A command or process substitution runs one. The
289
+ * arithmetic evaluator evaluates a NAME recursively (`a='x[$(…)]'; $((a))`), so arithmetic text that names a variable or
290
+ * carries a `$` may run one, and so may a parameter expansion's subscript or substring offset, its own or one nested in
291
+ * its text (`${x:-${a[i]}}`); a parameter, brace or glob text carrying a substitution opener runs what it opens.
292
+ */
293
+ export declare function expansionHidesRun(e: WordExpansion): boolean;
294
+ /** Can a compound assignment's value `(…)` run what its text does not show? The lexer keeps the value one opaque piece
295
+ * (its elements unread), so any `$` or backquote in it may (`(${P@P})`, `(${!N})`, `(${x:-$(…)})`), and so may a `[…]`
296
+ * subscript the evaluator reads — one naming a variable (`([x]=1)`), or carrying a `$` or backquote, an ANSI-C `$'…'`
297
+ * among them (`([$'\170']=1)`: bash decodes `\170` to the name `x` and evaluates it). A `$'…'` is not counted OUTSIDE a
298
+ * `[…]` — a standalone element opens an ANSI-C string and runs nothing (`($'\t' x)`) — but inside a subscript it is (the
299
+ * `[…]` arm's char class holds `$`). (An integer array's ANSI-C ELEMENT, `declare -ai A=($'\170')`, decodes and
300
+ * evaluates too, but needs the `-i` attribute the value text does not carry — not caught here, see KNOWN-LIMITS.) Its
301
+ * command substitutions are the segment's already. The one reading the deny lanes, the read station and
302
+ * auto-background share until the value is read element by element. */
303
+ export declare function compoundValueHidesRun(w: {
304
+ readonly assignment?: AssignmentFact;
305
+ }): boolean;
306
+ /** Does the parameter body (`P@P`, `a[i]@P`) carry a transformation that EVALUATES the value — `${P@P}` expands P's value
307
+ * as a prompt string, its parameter, command and arithmetic expansions run (measured on 5.3) — rather than respelling it? */
308
+ export declare function transformEvaluates(body: string): boolean;
309
+ /** Does evaluating a parameter expansion with this body RE-READ the parameter's own VALUE — an indirect `${!R}` (R's
310
+ * value names what is then expanded, so `R='a[$(…)]'` evaluates that subscript; a name list `${!prefix*}` / a key list
311
+ * `${!a[@]}` expand names only) or an evaluating transform `${P@P}` (the value is expanded as a prompt string, its own
312
+ * expansions run)? The one reading every consumer uses for "the value runs what the text does not show": the read
313
+ * station asks (it does not follow the value), the deny lanes and auto-background read it a hidden run through
314
+ * {@link expansionHidesRun}. A subscript or substring offset re-reads the BODY's own text, not the value — that is
315
+ * {@link parameterArithmeticTexts}. A line continuation in the body is removed first (`${P@\⏎P}` is `${P@P}`). */
316
+ export declare function parameterRereadsValue(body: string): boolean;
317
+ /** Is the arithmetic text (an arithmetic command's interior) provably free of a variable — the same reading
318
+ * {@link expansionHidesRun} gives an arithmetic expansion? */
319
+ export declare function arithmeticTextIsPlain(text: string): boolean;
320
+ /** Does `word`, standing right before the redirection `next`, name the descriptor that redirection opens (`{fd}>file`):
321
+ * a bare `{NAME}` that belongs to the operator rather than to the argv? */
322
+ export declare function namesRedirectDescriptor(word: LexedWord, next: LexedToken | undefined): boolean;
245
323
  /** The text a word's pieces hand the program when every piece is literal; `undefined` when one is an expansion or
246
324
  * unresolved. */
247
325
  export declare function literalTextOf(pieces: readonly WordPiece[]): string | undefined;
@@ -249,7 +327,8 @@ export declare function literalTextOf(pieces: readonly WordPiece[]): string | un
249
327
  * backquote or a backslash is removed (a line continuation was joined when the body was read), every other character
250
328
  * stands. `undefined` when an unescaped `$` or backquote stands in it: the program reads what the expansion makes. */
251
329
  export declare function literalHeredocBody(body: string): string | undefined;
252
- /** Lex a command: its segments, or the one reason bash (or this lexer) cannot delimit them. */
330
+ /** Lex a command: its segments, or the one reason bash (or this lexer) cannot delimit them — with the segments read
331
+ * before it ({@link LexParseFailure.readBefore}). */
253
332
  export declare function lexShell(source: string): ShellLex;
254
333
  /**
255
334
  * The refused-reading view of {@link lexShell}: a parse failure, and any word whose value cannot be read off the