@sema-agent/core 7.52.0 → 7.53.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 (191) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +3 -1
  3. package/dist/agents/cross-session-envelope.d.ts +8 -0
  4. package/dist/agents/cross-session-envelope.js +6 -0
  5. package/dist/agents/cross-session-judge.d.ts +21 -1
  6. package/dist/agents/cross-session-judge.js +8 -0
  7. package/dist/agents/cross-session-ref.d.ts +5 -0
  8. package/dist/agents/cross-session-ref.js +1 -0
  9. package/dist/agents/org-delivery-gate.d.ts +122 -0
  10. package/dist/agents/org-delivery-gate.js +69 -0
  11. package/dist/agents/peer-held-queue.d.ts +9 -4
  12. package/dist/agents/peer-held-queue.js +6 -5
  13. package/dist/agents/peer-session-drain.d.ts +35 -5
  14. package/dist/agents/peer-session-drain.js +271 -180
  15. package/dist/agents/read-inbox-tool.d.ts +7 -0
  16. package/dist/agents/read-inbox-tool.js +5 -1
  17. package/dist/agents/subagent.d.ts +3 -0
  18. package/dist/agents/subagent.js +99 -72
  19. package/dist/agents/tool-filter.d.ts +18 -0
  20. package/dist/agents/tool-filter.js +6 -0
  21. package/dist/brain/anthropic.d.ts +6 -1
  22. package/dist/brain/anthropic.js +4 -4
  23. package/dist/brain/model-presets.js +2 -1
  24. package/dist/brain/route-adjudicator.d.ts +6 -0
  25. package/dist/brain/routing.d.ts +24 -1
  26. package/dist/brain/routing.js +54 -19
  27. package/dist/brain/timeout.js +5 -4
  28. package/dist/core/a2a.d.ts +5 -2
  29. package/dist/core/a2a.js +30 -13
  30. package/dist/core/allow-entry.d.ts +2 -1
  31. package/dist/core/allow-entry.js +2 -2
  32. package/dist/core/auto-compaction.d.ts +57 -0
  33. package/dist/core/auto-compaction.js +28 -2
  34. package/dist/core/checkpoint-store.d.ts +29 -20
  35. package/dist/core/checkpoint-store.js +3 -3
  36. package/dist/core/compliance.js +3 -1
  37. package/dist/core/effective-path-target.d.ts +25 -6
  38. package/dist/core/effective-path-target.js +24 -2
  39. package/dist/core/engine-notice.d.ts +13 -2
  40. package/dist/core/gate-fold.js +3 -2
  41. package/dist/core/gate-lanes.js +11 -13
  42. package/dist/core/gate-outcome.d.ts +11 -6
  43. package/dist/core/governance-codes.d.ts +3 -1
  44. package/dist/core/governance-codes.js +3 -0
  45. package/dist/core/hands-band-screen.d.ts +7 -0
  46. package/dist/core/hands-band-screen.js +32 -0
  47. package/dist/core/hooks.d.ts +7 -5
  48. package/dist/core/hooks.js +5 -3
  49. package/dist/core/locked-config.d.ts +84 -32
  50. package/dist/core/locked-config.js +128 -42
  51. package/dist/core/mcp-probe.js +1 -1
  52. package/dist/core/mcp-server-spec.d.ts +88 -10
  53. package/dist/core/mcp-server-spec.js +183 -33
  54. package/dist/core/mcp.d.ts +21 -14
  55. package/dist/core/mcp.js +55 -51
  56. package/dist/core/memory-engine/engine.d.ts +14 -19
  57. package/dist/core/memory-engine/engine.js +7 -5
  58. package/dist/core/mode-policy.d.ts +3 -18
  59. package/dist/core/mode-policy.js +2 -16
  60. package/dist/core/mods/kernel/dispatch.js +13 -15
  61. package/dist/core/mods/kernel/events.d.ts +9 -6
  62. package/dist/core/mods/kernel/events.js +2 -2
  63. package/dist/core/mods/kernel/receive.d.ts +39 -10
  64. package/dist/core/mods/kernel/receive.js +119 -54
  65. package/dist/core/mods/kernel/site-rules.d.ts +80 -15
  66. package/dist/core/mods/kernel/site-rules.js +232 -40
  67. package/dist/core/mods/stations.d.ts +0 -6
  68. package/dist/core/mods/stations.js +0 -7
  69. package/dist/core/mounted-mcp.d.ts +57 -0
  70. package/dist/core/mounted-mcp.js +71 -2
  71. package/dist/core/own-property-receipt.d.ts +13 -0
  72. package/dist/core/own-property-receipt.js +3 -1
  73. package/dist/core/parent-spec-seat.d.ts +2 -2
  74. package/dist/core/permission-rule-model.d.ts +58 -9
  75. package/dist/core/permission-rule-model.js +81 -7
  76. package/dist/core/permission-rule-org.d.ts +163 -33
  77. package/dist/core/permission-rule-org.js +205 -71
  78. package/dist/core/permission-rules.d.ts +7 -1
  79. package/dist/core/permission-rules.js +37 -8
  80. package/dist/core/retention.js +7 -2
  81. package/dist/core/retired-keys.d.ts +8 -2
  82. package/dist/core/retired-keys.js +12 -1
  83. package/dist/core/roles.js +60 -10
  84. package/dist/core/rule-lane-reads.js +1 -1
  85. package/dist/core/runner/contracts.d.ts +1 -0
  86. package/dist/core/runner/gate-exit.js +4 -3
  87. package/dist/core/runner/mcp-redial.d.ts +52 -10
  88. package/dist/core/runner/mcp-redial.js +38 -9
  89. package/dist/core/runner/permission-rule-lanes.d.ts +5 -2
  90. package/dist/core/runner/permission-rule-lanes.js +22 -10
  91. package/dist/core/runner/person-path-rows.d.ts +3 -1
  92. package/dist/core/runner/person-path-rows.js +2 -2
  93. package/dist/core/runner/prepare-config-doors.d.ts +48 -2
  94. package/dist/core/runner/prepare-config-doors.js +161 -145
  95. package/dist/core/runner/prepare-defer-classify.js +4 -7
  96. package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
  97. package/dist/core/runner/prepare-delegation-surface.js +7 -5
  98. package/dist/core/runner/prepare-hands-readface.d.ts +1 -1
  99. package/dist/core/runner/prepare-hands-readface.js +2 -1
  100. package/dist/core/runner/prepare-inherited-gate.js +2 -2
  101. package/dist/core/runner/prepare-listings.d.ts +1 -1
  102. package/dist/core/runner/prepare-listings.js +4 -2
  103. package/dist/core/runner/prepare-memory-engine-session.d.ts +0 -6
  104. package/dist/core/runner/prepare-memory-engine-session.js +19 -6
  105. package/dist/core/runner/prepare-memory.d.ts +3 -2
  106. package/dist/core/runner/prepare-memory.js +6 -2
  107. package/dist/core/runner/prepare-offload-wrappers.d.ts +4 -4
  108. package/dist/core/runner/prepare-offload-wrappers.js +2 -1
  109. package/dist/core/runner/prepare-policy-chain.d.ts +1 -1
  110. package/dist/core/runner/prepare-policy-chain.js +12 -12
  111. package/dist/core/runner/prepare-project-context.d.ts +1 -1
  112. package/dist/core/runner/prepare-project-context.js +6 -4
  113. package/dist/core/runner/prepare-prompt-assembly.d.ts +1 -1
  114. package/dist/core/runner/prepare-prompt-assembly.js +20 -16
  115. package/dist/core/runner/prepare-prompt-inputs.d.ts +12 -6
  116. package/dist/core/runner/prepare-prompt-inputs.js +6 -3
  117. package/dist/core/runner/prepare-protocol-tools.d.ts +7 -5
  118. package/dist/core/runner/prepare-protocol-tools.js +19 -12
  119. package/dist/core/runner/prepare-question-face.d.ts +4 -2
  120. package/dist/core/runner/prepare-question-face.js +4 -3
  121. package/dist/core/runner/prepare-task.js +10 -10
  122. package/dist/core/runner/prepare-wiring-manifest.d.ts +8 -3
  123. package/dist/core/runner/prepare-wiring-manifest.js +8 -5
  124. package/dist/core/runner/resume-admission.d.ts +4 -1
  125. package/dist/core/runner/resume-admission.js +7 -5
  126. package/dist/core/runner/resume-internals-and-config.d.ts +3 -3
  127. package/dist/core/runner/resume-internals-and-config.js +3 -8
  128. package/dist/core/runner/retired-runner-deps.d.ts +5 -6
  129. package/dist/core/runner/retired-runner-deps.js +1 -4
  130. package/dist/core/runner/run-leg.js +42 -12
  131. package/dist/core/runner/run-recovery-lanes.js +5 -3
  132. package/dist/core/runner/run-terminal-adoption.js +6 -5
  133. package/dist/core/runner/run-turn-boundary.js +9 -3
  134. package/dist/core/runner/runtask.js +1 -1
  135. package/dist/core/runner/skills-seat.d.ts +15 -0
  136. package/dist/core/runner/skills-seat.js +57 -0
  137. package/dist/core/runner/sub-agent-deny-lists.d.ts +13 -11
  138. package/dist/core/runner/sub-agent-deny-lists.js +7 -5
  139. package/dist/core/runner/synthetic-tools.d.ts +5 -2
  140. package/dist/core/runner/synthetic-tools.js +2 -12
  141. package/dist/core/runner-deps.d.ts +29 -14
  142. package/dist/core/session-policy-store.d.ts +19 -0
  143. package/dist/core/session-policy-store.js +22 -2
  144. package/dist/core/store-contracts/mailbox-store-contract.js +13 -0
  145. package/dist/core/task-event.d.ts +38 -2
  146. package/dist/core/task-notification.js +2 -2
  147. package/dist/core/task-spec.d.ts +1 -1
  148. package/dist/core/tool-policy.d.ts +3 -0
  149. package/dist/core/tool-policy.js +10 -4
  150. package/dist/core/types.d.ts +2 -2
  151. package/dist/core/types.js +1 -1
  152. package/dist/core/untrusted-text.d.ts +16 -0
  153. package/dist/core/untrusted-text.js +7 -2
  154. package/dist/core/user-attachment.d.ts +13 -2
  155. package/dist/core/user-attachment.js +47 -41
  156. package/dist/core/wiring-manifest.d.ts +4 -2
  157. package/dist/engine/harness/agent-harness.js +2 -1
  158. package/dist/engine/harness/types.d.ts +3 -0
  159. package/dist/engine/loop/agent-loop.d.ts +12 -1
  160. package/dist/engine/loop/agent-loop.js +17 -5
  161. package/dist/engine/loop/types.d.ts +7 -1
  162. package/dist/index.d.ts +15 -9
  163. package/dist/index.js +12 -6
  164. package/dist/internal/harness.d.ts +1 -0
  165. package/dist/internal/harness.js +1 -0
  166. package/dist/orchestration/goal.d.ts +24 -1
  167. package/dist/orchestration/goal.js +3 -2
  168. package/dist/prompt-assembly/artifact-store.d.ts +44 -9
  169. package/dist/prompt-assembly/artifact-store.js +38 -12
  170. package/dist/prompt-assembly/artifact.d.ts +12 -11
  171. package/dist/prompt-assembly/artifact.js +52 -21
  172. package/dist/prompt-assembly/assemble.d.ts +2 -0
  173. package/dist/prompt-assembly/assemble.js +2 -1
  174. package/dist/prompt-assembly/catalog/index.js +2 -1
  175. package/dist/prompt-assembly/catalog/memory.d.ts +13 -0
  176. package/dist/prompt-assembly/catalog/memory.js +6 -0
  177. package/dist/prompt-assembly/catalog/schema.d.ts +16 -2
  178. package/dist/prompt-assembly/composer.d.ts +2 -0
  179. package/dist/prompt-assembly/composer.js +1 -1
  180. package/dist/prompt-assembly/overlay.d.ts +21 -11
  181. package/dist/prompt-assembly/overlay.js +55 -17
  182. package/dist/prompt-assembly/select.d.ts +21 -6
  183. package/dist/prompt-assembly/select.js +39 -2
  184. package/dist/prompts/default.d.ts +0 -11
  185. package/dist/prompts/default.js +0 -3
  186. package/dist/stores/file/permission-rule-store.d.ts +1 -1
  187. package/dist/stores/file/permission-rule-store.js +2 -1
  188. package/dist/tools/artifact/artifact-tool.js +4 -3
  189. package/package.json +1 -1
  190. package/test/export-surface.snapshot.json +65 -5
  191. package/wire-changes/core@7.53.0.json +1946 -0
@@ -20,7 +20,6 @@ import { type ToolRoster } from "./tool-roster.js";
20
20
  import { type PermissionMode } from "./ask-origin.js";
21
21
  import type { RuntimeCaps } from "./runtime-caps.js";
22
22
  import type { RunnerDeps } from "./runner-deps.js";
23
- import { recordedModeSeed } from "./checkpoint-store.js";
24
23
  import type { TaskSpec } from "./task-spec.js";
25
24
  /** WHY an effective mode is not the requested one: the deployment's deny bit (`RuntimeCaps.bypassPermissions` /
26
25
  * `RuntimeCaps.autoMode`), a requested `auto` with no classifier face wired, or a child's declaration folded under
@@ -162,25 +161,11 @@ export declare function modeRequestAfterResume(seat: PermissionMode | undefined,
162
161
  * (the re-supplied config, before the claim) — a resume prepare would refuse is refused before it consumes the row.
163
162
  */
164
163
  export declare function refusePermissionModeDoors(spec: Pick<TaskSpec, "permissionMode" | "shellGate">): PermissionMode | undefined;
165
- /**
166
- * The doors a RESUMED leg's prepare refuses at, read as one screen for the resume ladder to run BEFORE the claim: the mode
167
- * doors on the re-supplied config ({@link refusePermissionModeDoors}), its output-style seat (`resolveOutputStyleFrame`), its
168
- * approval-reach declaration (`screenApprovalReach`), the skills it invokes by name (`screenUserInvokedSkills` — the part no
169
- * roster changes; a name the leg's roster reads as nothing is prepare's), its attachments (`screenUserAttachments` — a resume
170
- * sends no new human message, so any entry is refused) and the mode words the checkpoint row recorded (`recordedModeSeed`) — prepare's own functions, so the refusal carries the very
171
- * sentence and code prepare would throw. It leaves by the resume's ONE refusal channel, `CheckpointError`
172
- * `checkpoint.config_refused` (`message` = prepare's sentence, `detail.code` = prepare's code, `detail.field` = the input that
173
- * carries it: `"taskConfig"` or `"inheritedGate"`), where prepare itself would end the run `failed` after the claim — a
174
- * refusal there consumes the row and reopens it as a system retry that replays the recorded decision, which a CONFIG
175
- * correction cannot reach. Before the claim the row stays `pending` and byte-identical, and the same token is redeemable by a
176
- * corrected resume. A throw that is not one of prepare's five codes (a caller's accessor failing) is not a refusal and leaves
177
- * as it was thrown.
178
- */
179
- export declare function refuseResumeDoors(config: Pick<TaskSpec, "permissionMode" | "shellGate" | "outputStyle" | "approvalReach" | "skills" | "userInvokedSkills" | "userAttachments">, recordedGate: Parameters<typeof recordedModeSeed>[0]): void;
180
164
  /** The resume's ONE refusal channel for a door prepare would refuse at: runs `screen` (prepare's own function, which throws prepare's sentence and code) and
181
165
  * leaves a refusal of one of {@link CHECKPOINT_CONFIG_REFUSAL_CODES} as `checkpoint.config_refused` — the sentence, prepare's code as `detail.code`, the input
182
- * that carries it as `detail.field`, and prepare's own arm as `detail.reason` when its error names one. Anything else leaves as it was thrown. */
183
- export declare function asResumeConfigRefusal(field: "taskConfig" | "inheritedGate", screen: () => void): void;
166
+ * that carries it as `detail.field`, and prepare's own arm as `detail.reason` when its error names one. Anything else leaves as it was thrown. What the
167
+ * door returns is returned (a {@link ConfigDoorEntry}: the resume's way of entering a door the config-door phase lists). */
168
+ export declare function asResumeConfigRefusal<T>(field: "taskConfig" | "deps" | "inheritedGate", screen: () => T): T;
184
169
  /** The three-member answer every mode question gets — the manifest member, the steer verb's return value and the
185
170
  * fold notice's detail share it. `requested` is absent when the caller declared nothing (a root inherits `default`,
186
171
  * a child inherits its parent). */
@@ -7,11 +7,7 @@ import { argvOf, lexShellText } from "../shell/lex.js";
7
7
  import { COMMAND_RULE_TOOL } from "./permission-rule-model.js";
8
8
  import { createSafeNotifier } from "./safe-notify.js";
9
9
  import { assertNoRetiredKeys, RETIRED_TASK_SPEC_KEYS } from "./retired-keys.js";
10
- import { resolveOutputStyleFrame } from "./output-style.js";
11
- import { screenUserAttachments } from "./user-attachment.js";
12
- import { CheckpointError, isCheckpointConfigRefusalCode, recordedModeSeed } from "./checkpoint-store.js";
13
- import { screenApprovalReach } from "./wiring-manifest.js";
14
- import { screenUserInvokedSkills } from "./skill-grant.js";
10
+ import { CheckpointError, isCheckpointConfigRefusalCode } from "./checkpoint-store.js";
15
11
  export const PERMISSION_MODE_FOLD_REASONS = Object.freeze(["operator_deny", "face_absent", "inherited_ceiling"]);
16
12
  export const ACCEPT_EDITS_FILE_COMMANDS = Object.freeze(["mkdir", "touch", "rm", "rmdir", "mv", "cp", "sed"]);
17
13
  const MODE_POLICY_TABLE = {
@@ -111,19 +107,9 @@ export function refusePermissionModeDoors(spec) {
111
107
  }
112
108
  return mode;
113
109
  }
114
- export function refuseResumeDoors(config, recordedGate) {
115
- asResumeConfigRefusal("taskConfig", () => {
116
- refusePermissionModeDoors(config);
117
- resolveOutputStyleFrame(config.outputStyle);
118
- screenApprovalReach(config.approvalReach);
119
- screenUserInvokedSkills(config);
120
- screenUserAttachments(config.userAttachments, { resumeLeg: true });
121
- });
122
- asResumeConfigRefusal("inheritedGate", () => void recordedModeSeed(recordedGate));
123
- }
124
110
  export function asResumeConfigRefusal(field, screen) {
125
111
  try {
126
- screen();
112
+ return screen();
127
113
  }
128
114
  catch (e) {
129
115
  if (e instanceof Error && "code" in e && isCheckpointConfigRefusalCode(e.code)) {
@@ -1,7 +1,7 @@
1
1
  import { CATCH_BUDGET_MS, createSelfBudget, POST_ABORT_GRACE_MS, RUNAWAY_AFTER_MS, unrefTimer, watchRunaway } from "./budget.js";
2
2
  import { expandModPattern, isModEventName, MOD_SEATS, nextToRefusal, stationOn } from "./events.js";
3
3
  import { ENGINE_ORIGIN, modsInvalid, screenModSeats, } from "./registration.js";
4
- import { answerUpward, answerViolation, detachedCopy, matcherMatches, matcherViolation, methodAnswerViolation, NEXT_IN_FLIGHT, pinnedKeyChanged, receiveAnswer, receivePayload, } from "./site-rules.js";
4
+ import { answerUpward, answerViolation, detachedCopy, engineObjectsOnce, handedOn, matcherMatches, matcherViolation, methodAnswerViolation, NEXT_IN_FLIGHT, receiveAnswer, receiveEvent, receivePayload, } from "./site-rules.js";
5
5
  const TIER_RANK = { prepend: 0, user: 1, append: 2, builtin: 3, core: 4 };
6
6
  const bypassed = (floors, tier) => floors.some((f) => TIER_RANK[f.from] < TIER_RANK[tier] && TIER_RANK[tier] < TIER_RANK[f.to]);
7
7
  export async function admitMods(input) {
@@ -202,7 +202,7 @@ function createBus(side, mounted, bottoms, report, records, describe, isProxy) {
202
202
  hasHandlers: (event, participant) => mounted.some((h) => h.events.has(event) && admits(participant, h)),
203
203
  dispatch: (event, e, options) => {
204
204
  const st = stationOn(event, side);
205
- const received = st === undefined ? { refusal: `${event} is not raised on the ${side} bus` } : receivePayload(e);
205
+ const received = st === undefined ? { refusal: `${event} is not raised on the ${side} bus` } : receiveEvent(e);
206
206
  if (st === undefined || "refusal" in received) {
207
207
  const reason = `${event}: ${"refusal" in received ? received.refusal : "not raised"}`;
208
208
  report(new Error(`engine: ${reason} — the dispatch was not run`));
@@ -237,7 +237,7 @@ function createBus(side, mounted, bottoms, report, records, describe, isProxy) {
237
237
  throw new Error(`${caller.origin.plugin}: $.${event}: not raised on the ${side} bus`);
238
238
  let e0 = args;
239
239
  if (seat.enter !== undefined) {
240
- const shaped = receivePayload(seat.enter(args), undefined, isProxy);
240
+ const shaped = receivePayload(seat.enter(args), isProxy, engineObjectsOnce(args)());
241
241
  if ("refusal" in shaped)
242
242
  throw new TypeError(`${caller.origin.plugin}: $.${event}: ${shaped.refusal}`);
243
243
  e0 = shaped.payload;
@@ -263,7 +263,7 @@ function createBus(side, mounted, bottoms, report, records, describe, isProxy) {
263
263
  throw new Error(`${caller.origin.plugin}: $.${event}: ${deny}`);
264
264
  return typeof answer === "object" && answer !== null ? Reflect.get(answer, "value") : undefined;
265
265
  }
266
- function dollarFor(reg, frames, budget, signal) {
266
+ function dollarFor(reg, frames, budget, signal, engines) {
267
267
  const origin = Object.freeze({ plugin: reg.name, tier: reg.tier });
268
268
  const callerFrames = Object.freeze([...frames, Object.freeze({ kind: "dollar", plugin: reg.name })]);
269
269
  const namespaces = new Map();
@@ -273,7 +273,7 @@ function createBus(side, mounted, bottoms, report, records, describe, isProxy) {
273
273
  ns[m.method] = async (args) => {
274
274
  if (!reg.callsBound.has(m.event))
275
275
  throw new Error(`${reg.name}: $.${m.event}: not declared in uses.calls`);
276
- const received = receivePayload(args === undefined ? {} : args, undefined, isProxy);
276
+ const received = receivePayload(args === undefined ? {} : args, isProxy, engines());
277
277
  if ("refusal" in received)
278
278
  throw new TypeError(`${reg.name}: $.${m.event}: takes one object — ${received.refusal}`);
279
279
  if (m.pauses)
@@ -347,19 +347,17 @@ function createBus(side, mounted, bottoms, report, records, describe, isProxy) {
347
347
  const drop = () => void pendingRuns.delete(run);
348
348
  run.then(drop, drop);
349
349
  };
350
+ const engines = engineObjectsOnce(e0);
350
351
  const handOn = (passed) => {
351
352
  if (passed === e0)
352
353
  return { snap: e0 };
353
- const received = receivePayload(passed, e0, isProxy);
354
- if ("refusal" in received)
355
- return { violation: `next() takes the event object: ${received.refusal}` };
356
- const k = pinnedKeyChanged(st.pinned, e0, received.payload);
357
- return k === undefined ? { snap: received.payload } : { violation: `next() changed the pinned key "${k}"` };
354
+ const handed = handedOn(st.pinned, e0, passed, engines(), isProxy);
355
+ return "violation" in handed ? handed : { snap: handed.payload };
358
356
  };
359
357
  const takeAnswer = (answer, s, e, h) => {
360
358
  let taken = answer;
361
359
  if (!s.answers.some((a) => Object.is(a, answer))) {
362
- const received = receiveAnswer(answer, isProxy);
360
+ const received = receiveAnswer(answer, engines(), isProxy);
363
361
  if ("refusal" in received)
364
362
  return { violation: received.refusal };
365
363
  taken = received.answer;
@@ -472,7 +470,7 @@ function createBus(side, mounted, bottoms, report, records, describe, isProxy) {
472
470
  }
473
471
  else {
474
472
  const next = makeNext({ budget, signal: ac.signal, trace: () => Object.freeze([...state.below]) }, (x) => descend(x, floors), to);
475
- const $ = dollarFor(h.reg, myFrames, budget, ac.signal);
473
+ const $ = dollarFor(h.reg, myFrames, budget, ac.signal, engines);
476
474
  handlerP = invokeNow(() => h.fn($, e, next));
477
475
  try {
478
476
  const raced = await Promise.race([handlerP, budget.expired]);
@@ -578,7 +576,7 @@ function createBus(side, mounted, bottoms, report, records, describe, isProxy) {
578
576
  return why !== undefined ? Promise.reject(new Error(`${h.reg.name}: ${why}`)) : replay(x, [...floors, { from: h.reg.tier, to: tier }]);
579
577
  }, { error, called: state.called });
580
578
  try {
581
- const raced = await Promise.race([invokeNow(() => onCatch(dollarFor(h.reg, myFrames, budget, ac.signal), e, next)), budget.expired]);
579
+ const raced = await Promise.race([invokeNow(() => onCatch(dollarFor(h.reg, myFrames, budget, ac.signal, engines), e, next)), budget.expired]);
582
580
  if (raced === "expired" && budget.isExpired())
583
581
  return { problem: `its .catch ran past its ${CATCH_BUDGET_MS}ms budget` };
584
582
  if (violated !== undefined)
@@ -610,7 +608,7 @@ function createBus(side, mounted, bottoms, report, records, describe, isProxy) {
610
608
  report(new Error(`${name}: ${event}: an observe handler returned a verdict — not adopted; an observe handler answers undefined`), "observational-hook-verdict-ignored");
611
609
  };
612
610
  try {
613
- const raced = await Promise.race([invokeNow(() => h.fn(dollarFor(h.reg, myFrames, budget, ac.signal), detachedCopy(e0), next)), budget.expired]);
611
+ const raced = await Promise.race([invokeNow(() => h.fn(dollarFor(h.reg, myFrames, budget, ac.signal, engines), detachedCopy(e0), next)), budget.expired]);
614
612
  if (raced === "expired" && budget.isExpired())
615
613
  throw new Error(`ran past its ${h.reg.budgetMs}ms budget`);
616
614
  verdict(raced);
@@ -631,7 +629,7 @@ function createBus(side, mounted, bottoms, report, records, describe, isProxy) {
631
629
  called: false,
632
630
  });
633
631
  try {
634
- const caught = await Promise.race([invokeNow(() => onCatch(dollarFor(h.reg, myFrames, catchBudget, cac.signal), detachedCopy(e0), cnext)), catchBudget.expired]);
632
+ const caught = await Promise.race([invokeNow(() => onCatch(dollarFor(h.reg, myFrames, catchBudget, cac.signal, engines), detachedCopy(e0), cnext)), catchBudget.expired]);
635
633
  if (!(caught === "expired" && catchBudget.isExpired()))
636
634
  verdict(caught);
637
635
  }
@@ -8,12 +8,14 @@
8
8
  */
9
9
  /**
10
10
  * The kernel CONTRACT number — a monotonic integer, independent of the package version (the package is
11
- * cut with core every release; this number is not). Every party that loads a kernel copy compares it for
12
- * EQUALITY. It is raised when, and only when, one of four things changes: the chain semantics (`next`,
13
- * `next.to`, origin, trace, re-entry frames), the registration form (`ModRegistration`, `on`, `uses`),
14
- * the event closed set (names and their `side`), or the failure columns (failure, abort, pinned keys).
11
+ * cut with core every release; this number is not). It is exported for a host that loads a kernel copy
12
+ * beside another one to compare for EQUALITY; nothing in this folder or in the engine reads it, so two
13
+ * copies that differ are not detected here. It is raised when, and only when, one of four things changes:
14
+ * the chain semantics (`next`, `next.to`, origin, trace, re-entry frames), the registration form (`ModRegistration`, `on`, `uses`),
15
+ * the event closed set (names and their `side`), or the failure columns (failure, abort, pinned keys — which keys,
16
+ * and how a pinned key is judged: by value since 2).
15
17
  */
16
- export declare const KERNEL_CONTRACT_VERSION = 1;
18
+ export declare const KERNEL_CONTRACT_VERSION = 2;
17
19
  /** The five tiers in run order (CC names). `builtin` and `core` are the engine's own. */
18
20
  export declare const MOD_TIERS: readonly ["prepend", "user", "append", "builtin", "core"];
19
21
  export type ModTier = (typeof MOD_TIERS)[number];
@@ -58,7 +60,8 @@ export declare const SEAT_TIER_TABLE: Readonly<{
58
60
  export interface ModStation {
59
61
  readonly failure: ModFailureAnswer;
60
62
  readonly abort: ModAbortPosture;
61
- /** Keys a handler may not change when it passes `e` on through `next`. */
63
+ /** Keys a handler may not change when it passes `e` on through `next` — compared by value: the engine's fact handed on,
64
+ * or a copy of it (a spread, a structured clone, a JSON round trip), is not a change; a different value is. */
62
65
  readonly pinned: readonly string[];
63
66
  readonly method?: {
64
67
  readonly pausesCallerBudget: boolean;
@@ -1,4 +1,4 @@
1
- export const KERNEL_CONTRACT_VERSION = 1;
1
+ export const KERNEL_CONTRACT_VERSION = 2;
2
2
  export const MOD_TIERS = Object.freeze(["prepend", "user", "append", "builtin", "core"]);
3
3
  export const MOD_SEATS = Object.freeze(["deployment", "task"]);
4
4
  export const MOD_EVENT_SIDES = Object.freeze(["engine", "surface", "both"]);
@@ -87,7 +87,7 @@ export const MOD_EVENT_TABLE = Object.freeze({
87
87
  "prompt.context": engine(),
88
88
  "prompt.attachment": engine(),
89
89
  "prompt.mention": surface,
90
- "prompt.compose": engine(station("skip", "grace", ["model"])),
90
+ "prompt.compose": engine(station("skip", "grace", ["model", "catalog", "selectors"])),
91
91
  "tool.describe": engine(),
92
92
  "command.run": surface,
93
93
  "command.describe": surface,
@@ -8,8 +8,11 @@
8
8
  * ordinary array — within `depth` levels is copied member by member off its own property descriptors, captured in
9
9
  * one call before any of the caller's code can run; each member lands as an enumerable data property of the copy (a
10
10
  * defined property, never an assignment, so an own `__proto__` key stays a plain key). Anything else — a function, an
11
- * object of another prototype, a container past `depth` — is handed as it is or refused, by `others`, which also
12
- * says whether cycles and shared references keep their shape or are refused / walked again.
11
+ * object of another prototype, a container past `depth` — is handed as it is, refused, or (an object of another
12
+ * prototype under `copy`) copied off its own members like a record, by `others`, which also says whether cycles and
13
+ * shared references keep their shape or are refused / walked again. A refusal refuses the whole receipt, or — under
14
+ * `contain` — only the member it was met in. The walk keeps its own stack (one frame per container being copied), so
15
+ * neither how deep a value nests nor how deep its caller already stands decides a verdict: only `depth` does.
13
16
  *
14
17
  * Zero-dependency on purpose (this folder compiles on its own): a Proxy cannot be recognised from here, so the
15
18
  * host passes its probe (`isProxy`); with a probe, a Proxy is refused before anything is asked of it.
@@ -18,7 +21,8 @@
18
21
  export interface ReceiptMode {
19
22
  /**
20
23
  * `call-once`: every own string-keyed member is read — a data property's value, or an accessor's getter called exactly
21
- * once on the object (a getter that throws refuses the receipt; one that moves the object's prototype refuses it).
24
+ * once on the object (a getter that throws refuses the receipt; one that moves the object's prototype refuses it, but
25
+ * under `others: "copy"`).
22
26
  * `refuse`: only own enumerable string-keyed DATA properties are members; an accessor, a non-enumerable property or
23
27
  * a symbol-keyed one refuses the receipt (no getter ever runs).
24
28
  */
@@ -28,14 +32,34 @@ export interface ReceiptMode {
28
32
  /** What a value that is not copied is (a function, another prototype, a container past `depth`, a symbol or a
29
33
  * bigint): handed as it is, or a refusal of the receipt. Shared references keep their shape in the copy (one copy
30
34
  * each); under `as-is` a cycle does too, under `refuse` a cycle is refused and a shared reference must fit `depth`
31
- * where it sits each time it is met. */
32
- readonly others: "as-is" | "refuse";
35
+ * where it sits each time it is met. `copy` is `refuse`, except that an object of another prototype within `depth`
36
+ * — a class instance, another realm's object or array, an array of a subclass — is copied as an ordinary record (an
37
+ * array as an ordinary array) off its OWN members: its prototype is not kept and no code of the prototype runs, so a
38
+ * prototype's getters and a built-in's internal state (a `Date`'s time, a `Map`'s entries) are not members and are
39
+ * not copied, while every own data member is (and an own accessor is read as `accessors` says; one that moves the
40
+ * object's prototype changes nothing the copy holds). A Proxy and a cycle are still refused. */
41
+ readonly others: "as-is" | "refuse" | "copy";
42
+ /** `refuse`: a container (an ordinary record or array) past `depth` refuses the receipt (`too-deep`) even where
43
+ * `others` hands other values as they are — a value of another kind there is still what `others` says — and a shared
44
+ * reference must fit `depth` where it sits each time it is met, by the height its first walk found (a walk that ran
45
+ * into a container still being walked — a cycle — counts that container as one level, and so does an object the
46
+ * reader holds, met directly or in what holds it): `depth` is a bound, never a copy depth. Absent ⇒ a container past
47
+ * `depth` is what `others` says. */
48
+ readonly past?: "refuse";
49
+ /** A fault met in a member — its getter threw, it is a value the receipt refuses, it holds a cycle, it runs too deep,
50
+ * or it could not be copied at all (`uncopyable`) — leaves THAT member out of its container's copy and is reported in the
51
+ * receipt's `faults` (where it was met); the walk goes on with the next member. A container refused where it was met
52
+ * is refused again wherever else it is met (never read twice). Only a fault of the received value itself refuses the
53
+ * receipt. Absent ⇒ the first fault refuses the receipt. */
54
+ readonly contain?: true;
33
55
  /** Every container the copy makes is frozen (a received value cannot be changed after it was received). */
34
56
  readonly freeze?: boolean;
35
57
  /** The host's Proxy probe. */
36
58
  readonly isProxy?: (value: object) => boolean;
37
59
  }
38
- /** Why a receipt was refused. */
60
+ /** Why a receipt was refused. `uncopyable` has two causes, and depth is neither (the walk keeps its own stack): reading
61
+ * the value threw where none of its getters runs — a Proxy's trap, on a receipt given no Proxy probe; what it threw is
62
+ * never read — or the value holds more objects than one receipt can index. */
39
63
  export declare const RECEIPT_FAULTS: readonly ["proxy", "accessor", "hidden", "getter-threw", "prototype-moved", "not-data", "cycle", "too-deep", "uncopyable"];
40
64
  export type ReceiptFaultKind = (typeof RECEIPT_FAULTS)[number];
41
65
  /** A refusal and where in the value it was met (member names from the top). */
@@ -43,9 +67,11 @@ export interface ReceiptFault {
43
67
  readonly kind: ReceiptFaultKind;
44
68
  readonly path: readonly string[];
45
69
  }
70
+ /** A receipt: the copy (with, under `contain`, every fault a member was left out for), or the refusal. */
46
71
  export type Receipt = {
47
72
  readonly ok: true;
48
73
  readonly value: unknown;
74
+ readonly faults?: readonly ReceiptFault[];
49
75
  } | {
50
76
  readonly ok: false;
51
77
  readonly fault: ReceiptFault;
@@ -53,13 +79,16 @@ export type Receipt = {
53
79
  /** Whether `v` is an ordinary record: an object whose prototype is `Object.prototype` or none (an array is not one). */
54
80
  export declare function isPlainRecord(v: unknown): v is Record<string, unknown>;
55
81
  /**
56
- * Receive `value` by `mode`. `held` names objects the reader already owns: met in the walk, each is kept as itself
57
- * (not received again) — so a member handed back unchanged keeps its identity.
82
+ * Receive `value` by `mode`. `held` names objects the reader already owns: met in the walk, each is kept as itself — not
83
+ * received again, never walked — so a member handed back unchanged keeps its identity. The set is consulted as it is,
84
+ * never copied, so one set serves every receipt of the same owner.
58
85
  */
59
- export declare function receive(value: unknown, mode: ReceiptMode, held?: Iterable<unknown>): Receipt;
86
+ export declare function receive(value: unknown, mode: ReceiptMode, held?: ReadonlySet<object>): Receipt;
60
87
  /** The mods bus's mode: a payload and an answer received whole — no accessor runs, no depth limit, cycles and shared
61
88
  * references kept, a function or a class instance handed as it is, every copied container frozen (so a member the
62
- * bus holds cannot be changed under it by a handler that holds it too). */
89
+ * bus holds cannot be changed under it by a handler that holds it too). The engine's own event is received in it as it
90
+ * is; what a handler gives the bus — a payload, an answer, a `$` call's arguments — in it held to the bus's one depth
91
+ * (the site rules set `depth` and `past: "refuse"`). */
63
92
  export declare const MODS_PAYLOAD_MODE: ReceiptMode;
64
93
  /** One clause naming a fault for a sentence ("an accessor at input.deep.x"). */
65
94
  export declare function describeFault(fault: ReceiptFault): string;
@@ -10,91 +10,156 @@ function isOrdinaryArray(v) {
10
10
  }
11
11
  export function receive(value, mode, held) {
12
12
  const seen = new Map();
13
- if (held !== undefined)
14
- for (const h of held)
15
- if (typeof h === "object" && h !== null)
16
- seen.set(h, h);
17
13
  const path = [];
18
14
  const open = new Set();
19
15
  const heights = new Map();
16
+ const faults = [];
17
+ const refusedAt = new Map();
18
+ const stack = [];
19
+ const refusals = new Set();
20
20
  const refuse = (kind) => {
21
- throw { refused: Object.freeze({ kind, path: Object.freeze([...path]) }) };
21
+ const fault = Object.freeze({ kind, path: Object.freeze([...path]) });
22
+ refusals.add(fault);
23
+ throw fault;
22
24
  };
25
+ const faultOf = (err) => typeof err === "object" && err !== null && refusals.has(err) ? err : Object.freeze({ kind: "uncopyable", path: Object.freeze([...path]) });
23
26
  const other = (v, kind) => (mode.others === "as-is" ? v : refuse(kind));
24
- const walk = (v, level) => {
27
+ const refuseContainer = (v, kind) => {
28
+ if (mode.contain === true) {
29
+ refusedAt.set(v, kind);
30
+ open.delete(v);
31
+ }
32
+ return refuse(kind);
33
+ };
34
+ const memberOf = (v, d) => {
35
+ if (Object.hasOwn(d, "value")) {
36
+ if (mode.accessors === "refuse" && d.enumerable !== true)
37
+ refuse("hidden");
38
+ return d.value;
39
+ }
40
+ if (mode.accessors === "refuse")
41
+ refuse("accessor");
42
+ const get = Object.hasOwn(d, "get") ? d.get : undefined;
43
+ try {
44
+ return get === undefined ? undefined : Reflect.apply(get, v, []);
45
+ }
46
+ catch {
47
+ return refuse("getter-threw");
48
+ }
49
+ };
50
+ const measured = mode.past === "refuse" || mode.others !== "as-is";
51
+ const heightOf = (v) => heights.get(v) ?? (open.has(v) || (held?.has(v) === true && mode.isProxy?.(v) !== true && (isOrdinaryArray(v) || isPlainRecord(v))) ? 1 : 0);
52
+ const pastDepth = (v, level) => measured && level + heightOf(v) > mode.depth;
53
+ const meet = (v, level) => {
25
54
  if (v === null || v === undefined || typeof v === "string" || typeof v === "number" || typeof v === "boolean")
26
55
  return v;
27
56
  if (typeof v !== "object")
28
57
  return other(v, "not-data");
58
+ if (held?.has(v) === true)
59
+ return pastDepth(v, level) ? refuse("too-deep") : v;
29
60
  if (seen.has(v)) {
30
- if (open.has(v))
31
- return mode.others === "refuse" ? refuse("cycle") : seen.get(v);
32
- if (mode.others === "refuse" && level + (heights.get(v) ?? 1) > mode.depth)
61
+ if (refusedAt.has(v))
62
+ return refuse(refusedAt.get(v));
63
+ if (open.has(v) && mode.others !== "as-is")
64
+ return refuse("cycle");
65
+ return pastDepth(v, level) ? refuse("too-deep") : seen.get(v);
66
+ }
67
+ if (level >= mode.depth && mode.past === "refuse") {
68
+ if (mode.isProxy?.(v) === true)
69
+ return refuse("proxy");
70
+ if (isOrdinaryArray(v) || isPlainRecord(v))
33
71
  return refuse("too-deep");
34
- return seen.get(v);
35
72
  }
36
73
  if (level >= mode.depth)
37
74
  return other(v, "too-deep");
38
75
  if (mode.isProxy?.(v) === true)
39
76
  return refuse("proxy");
40
- const list = isOrdinaryArray(v);
41
- if (!list && !isPlainRecord(v))
77
+ const list = isOrdinaryArray(v) || (mode.others === "copy" && Array.isArray(v));
78
+ if (!list && !isPlainRecord(v) && mode.others !== "copy")
42
79
  return other(v, "not-data");
43
80
  const proto = Object.getPrototypeOf(v);
44
81
  const captured = Object.getOwnPropertyDescriptors(v);
45
82
  const out = list ? [] : {};
46
83
  seen.set(v, out);
47
84
  open.add(v);
48
- let height = 1;
49
85
  if (mode.accessors === "refuse" && Object.getOwnPropertySymbols(captured).length > 0)
50
- refuse("hidden");
51
- for (const key of Object.getOwnPropertyNames(captured)) {
52
- if (list && key === "length")
53
- continue;
54
- const d = captured[key];
55
- path.push(key);
56
- let member;
57
- if (Object.hasOwn(d, "value")) {
58
- if (mode.accessors === "refuse" && d.enumerable !== true)
59
- refuse("hidden");
60
- member = d.value;
61
- }
62
- else {
63
- if (mode.accessors === "refuse")
64
- refuse("accessor");
65
- const get = Object.hasOwn(d, "get") ? d.get : undefined;
66
- try {
67
- member = get === undefined ? undefined : get.call(v);
68
- }
69
- catch {
70
- return refuse("getter-threw");
71
- }
72
- }
73
- Object.defineProperty(out, key, { value: walk(member, level + 1), enumerable: true, writable: true, configurable: true });
74
- if (typeof member === "object" && member !== null)
75
- height = Math.max(height, (heights.get(member) ?? 0) + 1);
76
- path.pop();
77
- }
78
- heights.set(v, height);
79
- if (mode.accessors === "call-once" && Object.getPrototypeOf(v) !== proto)
80
- refuse("prototype-moved");
81
- if (Array.isArray(out)) {
82
- const length = captured["length"]?.value;
86
+ refuseContainer(v, "hidden");
87
+ stack.push({ v, out, list, proto, captured, keys: Object.getOwnPropertyNames(captured), level, next: 0, height: 1, key: "", member: undefined });
88
+ return out;
89
+ };
90
+ const land = (f, key, copy, member) => {
91
+ if (measured && typeof member === "object" && member !== null)
92
+ f.height = Math.max(f.height, heightOf(member) + 1);
93
+ Object.defineProperty(f.out, key, { value: copy, enumerable: true, writable: true, configurable: true });
94
+ path.length = f.level;
95
+ };
96
+ const finish = (f) => {
97
+ heights.set(f.v, f.height);
98
+ if (mode.accessors === "call-once" && mode.others !== "copy" && Object.getPrototypeOf(f.v) !== f.proto)
99
+ refuseContainer(f.v, "prototype-moved");
100
+ if (Array.isArray(f.out)) {
101
+ const length = f.captured["length"]?.value;
83
102
  if (typeof length === "number")
84
- out.length = length;
103
+ f.out.length = length;
85
104
  }
86
105
  if (mode.freeze === true)
87
- Object.freeze(out);
88
- open.delete(v);
89
- return out;
106
+ Object.freeze(f.out);
107
+ open.delete(f.v);
108
+ return f.out;
90
109
  };
110
+ const received = (copy) => (mode.contain === true ? { ok: true, value: copy, faults: Object.freeze(faults) } : { ok: true, value: copy });
91
111
  try {
92
- return { ok: true, value: walk(value, 0) };
112
+ const copy = meet(value, 0);
113
+ if (stack.length === 0)
114
+ return received(copy);
93
115
  }
94
116
  catch (err) {
95
- if (typeof err === "object" && err !== null && "refused" in err)
96
- return { ok: false, fault: err.refused };
97
- return { ok: false, fault: Object.freeze({ kind: "uncopyable", path: Object.freeze([...path]) }) };
117
+ return { ok: false, fault: faultOf(err) };
118
+ }
119
+ for (;;) {
120
+ const f = stack[stack.length - 1];
121
+ if (f.next < f.keys.length) {
122
+ const key = f.keys[f.next++];
123
+ if (f.list && key === "length")
124
+ continue;
125
+ path.push(key);
126
+ try {
127
+ const member = memberOf(f.v, f.captured[key]);
128
+ const frames = stack.length;
129
+ const copy = meet(member, f.level + 1);
130
+ if (stack.length > frames) {
131
+ f.key = key;
132
+ f.member = member;
133
+ }
134
+ else
135
+ land(f, key, copy, member);
136
+ }
137
+ catch (err) {
138
+ if (mode.contain !== true)
139
+ return { ok: false, fault: faultOf(err) };
140
+ faults.push(faultOf(err));
141
+ path.length = f.level;
142
+ }
143
+ continue;
144
+ }
145
+ let copy;
146
+ try {
147
+ copy = finish(f);
148
+ }
149
+ catch (err) {
150
+ stack.pop();
151
+ const holder = stack[stack.length - 1];
152
+ if (mode.contain !== true || holder === undefined)
153
+ return { ok: false, fault: faultOf(err) };
154
+ faults.push(faultOf(err));
155
+ path.length = holder.level;
156
+ continue;
157
+ }
158
+ stack.pop();
159
+ const holder = stack[stack.length - 1];
160
+ if (holder === undefined)
161
+ return received(copy);
162
+ land(holder, holder.key, copy, holder.member);
98
163
  }
99
164
  }
100
165
  export const MODS_PAYLOAD_MODE = Object.freeze({ accessors: "refuse", depth: Infinity, others: "as-is", freeze: true });