@sema-agent/core 7.35.2 → 7.37.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 (85) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/dist/brain/anthropic.js +6 -6
  3. package/dist/brain/errors.d.ts +1 -14
  4. package/dist/brain/errors.js +2 -6
  5. package/dist/brain/open-responses.js +7 -7
  6. package/dist/brain/openai.js +7 -7
  7. package/dist/brain/provider-echo-shape.d.ts +26 -0
  8. package/dist/brain/provider-echo-shape.js +12 -0
  9. package/dist/brain/tool-call-id.d.ts +27 -15
  10. package/dist/brain/tool-call-id.js +43 -0
  11. package/dist/core/ask-origin.d.ts +35 -5
  12. package/dist/core/ask-origin.js +23 -4
  13. package/dist/core/ask-question.d.ts +0 -30
  14. package/dist/core/ask-question.js +3 -2
  15. package/dist/core/checkpoint-store.d.ts +18 -6
  16. package/dist/core/checkpoint-store.js +2 -1
  17. package/dist/core/context-edit.d.ts +4 -0
  18. package/dist/core/context-edit.js +5 -2
  19. package/dist/core/decision-receipt.d.ts +21 -0
  20. package/dist/core/decision-receipt.js +63 -0
  21. package/dist/core/engine-notice.d.ts +15 -5
  22. package/dist/core/gate-fold.js +2 -1
  23. package/dist/core/gate-lanes.js +20 -23
  24. package/dist/core/gate-outcome.d.ts +7 -5
  25. package/dist/core/gate-outcome.js +5 -1
  26. package/dist/core/governance-codes.d.ts +1 -1
  27. package/dist/core/governance-codes.js +2 -0
  28. package/dist/core/hooks.d.ts +14 -2
  29. package/dist/core/hooks.js +7 -3
  30. package/dist/core/mode-policy.js +2 -1
  31. package/dist/core/park-selfcheck.js +2 -0
  32. package/dist/core/permission-rule-model.d.ts +2 -2
  33. package/dist/core/permission-rule-model.js +2 -2
  34. package/dist/core/permission-rules.js +2 -5
  35. package/dist/core/runner/active-skill-scope.js +5 -5
  36. package/dist/core/runner/decide-continuation.js +2 -1
  37. package/dist/core/runner/denial-limit-arms.d.ts +28 -18
  38. package/dist/core/runner/denial-limit-arms.js +11 -7
  39. package/dist/core/runner/inherited-ask-grants.d.ts +32 -31
  40. package/dist/core/runner/inherited-ask-grants.js +5 -0
  41. package/dist/core/runner/permission-rule-lanes.d.ts +3 -0
  42. package/dist/core/runner/permission-rule-lanes.js +7 -6
  43. package/dist/core/runner/prepare-context-lane.d.ts +4 -0
  44. package/dist/core/runner/prepare-context-lane.js +7 -5
  45. package/dist/core/runner/prepare-gate-stations.js +17 -12
  46. package/dist/core/runner/prepare-inherited-gate.js +1 -1
  47. package/dist/core/runner/prepare-park-ask.d.ts +4 -2
  48. package/dist/core/runner/prepare-park-ask.js +8 -11
  49. package/dist/core/runner/prepare-policy-chain.d.ts +5 -8
  50. package/dist/core/runner/prepare-policy-chain.js +50 -31
  51. package/dist/core/runner/prepare-safety-scan.js +9 -2
  52. package/dist/core/runner/prepare-task.js +3 -3
  53. package/dist/core/runner/resume-apply.js +2 -1
  54. package/dist/core/runner/run-attachment-seats.js +3 -1
  55. package/dist/core/runner/run-harness-handlers.js +6 -1
  56. package/dist/core/runner/run-leg.js +11 -9
  57. package/dist/core/runner/run-recovery-lanes.js +2 -0
  58. package/dist/core/runner/session-rule-policy.js +5 -4
  59. package/dist/core/runner/tool-call-id-fold-seat.d.ts +13 -0
  60. package/dist/core/runner/tool-call-id-fold-seat.js +31 -0
  61. package/dist/core/sensitive-path-policy.js +2 -2
  62. package/dist/core/side-query.d.ts +5 -1
  63. package/dist/core/side-query.js +5 -1
  64. package/dist/core/store-contracts/checkpoint-store-contract.js +18 -0
  65. package/dist/core/tool-name-readings.d.ts +370 -0
  66. package/dist/core/tool-name-readings.js +137 -0
  67. package/dist/core/tool-policy.d.ts +79 -89
  68. package/dist/core/tool-policy.js +86 -49
  69. package/dist/core/tool-registry.d.ts +1 -31
  70. package/dist/core/tool-registry.js +2 -22
  71. package/dist/core/tool-result-budget.d.ts +3 -0
  72. package/dist/core/tool-result-budget.js +5 -3
  73. package/dist/core/tool-roster.d.ts +30 -9
  74. package/dist/core/tool-roster.js +45 -5
  75. package/dist/engine/llm/types.d.ts +4 -0
  76. package/dist/index.d.ts +1 -1
  77. package/dist/index.js +1 -1
  78. package/dist/tools/fs/file-read-check.d.ts +1 -1
  79. package/dist/tools/fs/file-read-check.js +4 -3
  80. package/dist/tools/fs/shell-read-check.d.ts +15 -1
  81. package/dist/tools/fs/shell-read-check.js +5 -2
  82. package/package.json +2 -1
  83. package/test/export-surface.snapshot.json +7 -1
  84. package/wire-changes/core@7.36.0.json +288 -0
  85. package/wire-changes/core@7.37.0.json +405 -0
@@ -381,6 +381,24 @@ export async function checkpointStoreContract(make, runAssertion) {
381
381
  assert.equal("mandate" in byId.get("mw-absent"), false, "absent must project the key OMITTED");
382
382
  assert.equal("mandate" in byId.get("mw-bogus"), false, "a word outside the closed set must read as absent, never project verbatim");
383
383
  });
384
+ run("#1214 ask class pair: a tool_approval row's `askClass` / `classes` survive the round-trip verbatim (the array whole, in its order); an absent pair stays absent", async () => {
385
+ const store = make();
386
+ const single = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "ac-single" });
387
+ Object.assign(single.pendingAction, { askClass: "denial_limit_fallback", classes: ["denial_limit_fallback"] });
388
+ const several = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "ac-several" });
389
+ Object.assign(several.pendingAction, { askClass: "transcript_write", classes: ["transcript_write", "org_rule"] });
390
+ const absent = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "ac-absent" });
391
+ for (const cp of [single, several, absent])
392
+ await store.put(cp.token, cp);
393
+ for (const [cp, askClass, classes] of [[single, "denial_limit_fallback", ["denial_limit_fallback"]], [several, "transcript_write", ["transcript_write", "org_rule"]]]) {
394
+ const back = (await store.get(cp.token)).pendingAction;
395
+ assert.equal(back.askClass, askClass, "the row's askClass must survive the round-trip verbatim");
396
+ assert.deepEqual(back.classes, classes, "the row's classes must survive the round-trip as the same array, in the same order");
397
+ }
398
+ const bare = (await store.get(absent.token)).pendingAction;
399
+ assert.equal("askClass" in bare, false, "an absent askClass must stay OMITTED on the row");
400
+ assert.equal("classes" in bare, false, "an absent classes must stay OMITTED on the row");
401
+ });
384
402
  await settle();
385
403
  }
386
404
  const sortByToken = (a, b) => a.token.localeCompare(b.token);
@@ -0,0 +1,370 @@
1
+ /**
2
+ * The ONE table of how every per-call site keyed on an engine tool name reads a call's names, and the read ports a
3
+ * site must go through. A call answers to several names once a caller tool declares aliases (the model's spelling,
4
+ * the wire name the gate resolves it to, the other aliases that resolve to the same row); which of them a site reads
5
+ * is a property of the SITE, declared here once:
6
+ *
7
+ * · `any` — a TIGHTEN keyed on an engine name set, or a rule name a user wrote: the call is held under EVERY name it
8
+ * answers to ({@link heldBy}); a set-valued table is read as the union of its entries ({@link heldValues}).
9
+ * · `seat` — a multi-key seat keyed on engine names: ONE name is chosen ({@link seatOf}) and every key of the seat is
10
+ * read under it. `tier` = the name whose irreversibility tier is strictest (`never < maybe < always`, an
11
+ * absent tier lowest, ties to the earlier name of the call); `member` = the first name the table holds.
12
+ * · `wire` — a GRANT or a ROUTING keyed on an engine name: the call's wire name only ({@link wireOf}).
13
+ * · `spelling` — a record of what the model said (stream frames, transcripts, counters): the harness event's own name;
14
+ * no port — listed so the table is complete.
15
+ *
16
+ * A site passes its {@link SiteId}; the port's parameter type admits only the ids of its kind, so moving a row to another
17
+ * kind fails to compile at the site. `gate:name-readings` holds the rest: a name-keyed table read outside a port, a call's
18
+ * name compared with an engine name outside {@link wireOf}, and a site id spelled at more than one place.
19
+ *
20
+ * Layer-0 vocabulary: depends on nothing but the tool declaration's tier type and, type-only, the mark-source word
21
+ * (`ToolMarkSource`); nothing here runs at import time.
22
+ */
23
+ import type { ToolSpec } from "./tool-spec.js";
24
+ export type NameReading = {
25
+ readonly kind: "any";
26
+ readonly file: string;
27
+ } | {
28
+ readonly kind: "wire";
29
+ readonly file: string;
30
+ } | {
31
+ readonly kind: "spelling";
32
+ readonly file: string;
33
+ } | {
34
+ readonly kind: "seat";
35
+ readonly rank: "tier" | "member";
36
+ readonly file: string;
37
+ };
38
+ export declare const NAME_READINGS: {
39
+ readonly "gate.compliance": {
40
+ readonly kind: "any";
41
+ readonly file: string;
42
+ };
43
+ readonly "gate.egress": {
44
+ readonly kind: "any";
45
+ readonly file: string;
46
+ };
47
+ readonly "gate.peerMessage": {
48
+ readonly kind: "any";
49
+ readonly file: string;
50
+ };
51
+ readonly "gate.markSources": {
52
+ readonly kind: "any";
53
+ readonly file: string;
54
+ };
55
+ readonly "gate.shellSeat": {
56
+ readonly kind: "seat";
57
+ readonly rank: "tier";
58
+ readonly file: string;
59
+ };
60
+ readonly "gate.sandboxBoundary.egress": {
61
+ readonly kind: "any";
62
+ readonly file: string;
63
+ };
64
+ readonly "gate.sandboxBoundary.hand": {
65
+ readonly kind: "wire";
66
+ readonly file: string;
67
+ };
68
+ readonly "gate.preToolUse": {
69
+ readonly kind: "wire";
70
+ readonly file: string;
71
+ };
72
+ readonly "fold.readStation": {
73
+ readonly kind: "seat";
74
+ readonly rank: "member";
75
+ readonly file: string;
76
+ };
77
+ readonly "lanes.contentQuestionClass": {
78
+ readonly kind: "any";
79
+ readonly file: string;
80
+ };
81
+ readonly "lanes.readOnlyShellAllow": {
82
+ readonly kind: "wire";
83
+ readonly file: string;
84
+ };
85
+ readonly "lanes.modeFileCommand": {
86
+ readonly kind: "wire";
87
+ readonly file: string;
88
+ };
89
+ readonly "lanes.contentAskRoute": {
90
+ readonly kind: "wire";
91
+ readonly file: string;
92
+ };
93
+ readonly "lanes.orgContentAskExemption": {
94
+ readonly kind: "wire";
95
+ readonly file: string;
96
+ };
97
+ readonly "lanes.persistedEditSkip": {
98
+ readonly kind: "wire";
99
+ readonly file: string;
100
+ };
101
+ readonly "lanes.persistedLaneSkip": {
102
+ readonly kind: "wire";
103
+ readonly file: string;
104
+ };
105
+ readonly "lanes.allowLayerQuestionSkip": {
106
+ readonly kind: "wire";
107
+ readonly file: string;
108
+ };
109
+ readonly "lanes.sandboxQuestionSkip": {
110
+ readonly kind: "wire";
111
+ readonly file: string;
112
+ };
113
+ readonly "policy.nameLists": {
114
+ readonly kind: "any";
115
+ readonly file: string;
116
+ };
117
+ readonly "policy.shellToolsList": {
118
+ readonly kind: "any";
119
+ readonly file: string;
120
+ };
121
+ readonly "policy.projectionReplay": {
122
+ readonly kind: "any";
123
+ readonly file: string;
124
+ };
125
+ readonly "policy.sessionRules": {
126
+ readonly kind: "any";
127
+ readonly file: string;
128
+ };
129
+ readonly "policy.sessionRules.effect": {
130
+ readonly kind: "wire";
131
+ readonly file: string;
132
+ };
133
+ readonly "policy.skillScope": {
134
+ readonly kind: "any";
135
+ readonly file: string;
136
+ };
137
+ readonly "policy.skillScope.effect": {
138
+ readonly kind: "wire";
139
+ readonly file: string;
140
+ };
141
+ readonly "policy.permissionRules": {
142
+ readonly kind: "any";
143
+ readonly file: string;
144
+ };
145
+ readonly "policy.persistedGrammar": {
146
+ readonly kind: "any";
147
+ readonly file: string;
148
+ };
149
+ readonly "policy.durableQuestion": {
150
+ readonly kind: "any";
151
+ readonly file: string;
152
+ };
153
+ readonly "policy.sensitivePathGuardedByName": {
154
+ readonly kind: "any";
155
+ readonly file: string;
156
+ };
157
+ readonly "floor.protectivePathTarget": {
158
+ readonly kind: "seat";
159
+ readonly rank: "member";
160
+ readonly file: string;
161
+ };
162
+ readonly "mode.planRosterRow": {
163
+ readonly kind: "wire";
164
+ readonly file: string;
165
+ };
166
+ readonly "offer.laneTool": {
167
+ readonly kind: "wire";
168
+ readonly file: string;
169
+ };
170
+ readonly "offer.orgQuestionExemption": {
171
+ readonly kind: "wire";
172
+ readonly file: string;
173
+ };
174
+ readonly "offer.marks": {
175
+ readonly kind: "seat";
176
+ readonly rank: "tier";
177
+ readonly file: string;
178
+ };
179
+ readonly "offer.marks.egress": {
180
+ readonly kind: "any";
181
+ readonly file: string;
182
+ };
183
+ readonly "park.riskDescriptor": {
184
+ readonly kind: "seat";
185
+ readonly rank: "tier";
186
+ readonly file: string;
187
+ };
188
+ readonly "park.syncFirstRoute": {
189
+ readonly kind: "wire";
190
+ readonly file: string;
191
+ };
192
+ readonly "report.riskAxes": {
193
+ readonly kind: "seat";
194
+ readonly rank: "tier";
195
+ readonly file: string;
196
+ };
197
+ readonly "report.riskAxes.egress": {
198
+ readonly kind: "any";
199
+ readonly file: string;
200
+ };
201
+ readonly "report.riskAxes.negatives": {
202
+ readonly kind: "seat";
203
+ readonly rank: "member";
204
+ readonly file: string;
205
+ };
206
+ readonly "report.markSources": {
207
+ readonly kind: "any";
208
+ readonly file: string;
209
+ };
210
+ readonly "verify.wroteThisRun": {
211
+ readonly kind: "wire";
212
+ readonly file: string;
213
+ };
214
+ readonly "verify.wroteThisRunSeed": {
215
+ readonly kind: "wire";
216
+ readonly file: string;
217
+ };
218
+ readonly "event.toolStartEnd": {
219
+ readonly kind: "spelling";
220
+ readonly file: string;
221
+ };
222
+ readonly "hook.postToolUse": {
223
+ readonly kind: "wire";
224
+ readonly file: string;
225
+ };
226
+ readonly "hook.postToolUseFailure": {
227
+ readonly kind: "wire";
228
+ readonly file: string;
229
+ };
230
+ readonly "hook.postToolBatch": {
231
+ readonly kind: "spelling";
232
+ readonly file: string;
233
+ };
234
+ readonly "stats.subagentToolCounter": {
235
+ readonly kind: "spelling";
236
+ readonly file: string;
237
+ };
238
+ readonly "activity.sink": {
239
+ readonly kind: "spelling";
240
+ readonly file: string;
241
+ };
242
+ readonly "reconcile.orphanEffect": {
243
+ readonly kind: "spelling";
244
+ readonly file: string;
245
+ };
246
+ readonly "inherited.questionFloat": {
247
+ readonly kind: "wire";
248
+ readonly file: string;
249
+ };
250
+ readonly "park.contentKindProjection": {
251
+ readonly kind: "wire";
252
+ readonly file: string;
253
+ };
254
+ readonly "resume.contentAskRow": {
255
+ readonly kind: "wire";
256
+ readonly file: string;
257
+ };
258
+ readonly "resume.editRecheckQuestionSkip": {
259
+ readonly kind: "wire";
260
+ readonly file: string;
261
+ };
262
+ readonly "resume.questionArgsShown": {
263
+ readonly kind: "wire";
264
+ readonly file: string;
265
+ };
266
+ readonly "budget.exemptResult": {
267
+ readonly kind: "wire";
268
+ readonly file: string;
269
+ };
270
+ readonly "compaction.compactableResult": {
271
+ readonly kind: "wire";
272
+ readonly file: string;
273
+ };
274
+ };
275
+ type Readings = typeof NAME_READINGS;
276
+ export type SiteId = keyof Readings;
277
+ export type AnySite = {
278
+ [K in SiteId]: Readings[K]["kind"] extends "any" ? K : never;
279
+ }[SiteId];
280
+ export type WireSite = {
281
+ [K in SiteId]: Readings[K]["kind"] extends "wire" ? K : never;
282
+ }[SiteId];
283
+ export type SpellingSite = {
284
+ [K in SiteId]: Readings[K]["kind"] extends "spelling" ? K : never;
285
+ }[SiteId];
286
+ export type TierSeatSite = {
287
+ [K in SiteId]: Readings[K] extends {
288
+ kind: "seat";
289
+ rank: "tier";
290
+ } ? K : never;
291
+ }[SiteId];
292
+ export type MemberSeatSite = {
293
+ [K in SiteId]: Readings[K] extends {
294
+ kind: "seat";
295
+ rank: "member";
296
+ } ? K : never;
297
+ }[SiteId];
298
+ export type SeatSite = TierSeatSite | MemberSeatSite;
299
+ /** A call as a site reads it: the wire name and, on a gated request, the face whose `names` lists every name it answers to. */
300
+ export interface NamedCall {
301
+ readonly toolName: string;
302
+ readonly face?: {
303
+ readonly names?: readonly string[];
304
+ } | undefined;
305
+ }
306
+ /**
307
+ * Every name the call answers to — the face's `names` when they include the call's name, else the call's name alone (a
308
+ * bare request, or a face minted for another name). A tool-name rule a host matches reaches the call iff its name is on
309
+ * this list; inside the engine only the ports below read it.
310
+ */
311
+ export declare function ruleNamesOf(call: NamedCall): readonly string[];
312
+ /** An `any` site's membership read: does `set` hold any of the call's names? */
313
+ export declare function heldBy(site: AnySite, set: {
314
+ has(name: string): boolean;
315
+ }, call: NamedCall): boolean;
316
+ /** An `any` site's value read: every entry `map` holds under any of the call's names, in the call's name order. */
317
+ export declare function heldValues<V>(site: AnySite, map: {
318
+ get(name: string): V | undefined;
319
+ }, call: NamedCall): V[];
320
+ /** A `wire` site's read: the call's wire name. */
321
+ export declare function wireOf(site: WireSite, call: {
322
+ readonly toolName: string;
323
+ }): string;
324
+ /** A tool's irreversibility tier, the one vocabulary `ToolSpec.irreversibility` declares. */
325
+ type IrreversibilityTierWord = NonNullable<ToolSpec["irreversibility"]>;
326
+ /** One seat, read under ONE name: the name the port chose, the value it holds there, and the seat's other keys under that name. */
327
+ export interface Seat<V> {
328
+ readonly name: string;
329
+ readonly value: V;
330
+ /** Another key of this seat, read under the seat's name. */
331
+ read<W>(table: {
332
+ get(name: string): W | undefined;
333
+ }): W | undefined;
334
+ /** Another key of this seat, a membership, read under the seat's name. */
335
+ holds(set: {
336
+ has(name: string): boolean;
337
+ }): boolean;
338
+ }
339
+ /** A tier seat: the risk seat of the engine's irreversibility tables. */
340
+ export interface TierSeat extends Seat<IrreversibilityTierWord> {
341
+ /**
342
+ * The seat's shell-seat provenance: the seat's name is in `shellGated` (the engine's shell seat is the sole positive
343
+ * source of its tier) AND no name the call answers to carries a deployment mark in `markSources` — a mark on any of the
344
+ * call's names outranks the shell seat's attribution.
345
+ */
346
+ shellGated(shellGated: {
347
+ has(name: string): boolean;
348
+ }, markSources: {
349
+ get(name: string): ReadonlySet<string> | undefined;
350
+ }): boolean;
351
+ }
352
+ /**
353
+ * A `seat` site's read. A `tier` seat takes the irreversibility tier table and picks the name whose tier is strictest;
354
+ * a `member` seat takes a table (a map, or a set whose value reads `true`) and picks the first name it holds.
355
+ */
356
+ export declare function seatOf(site: TierSeatSite, tiers: {
357
+ get(name: string): IrreversibilityTierWord | undefined;
358
+ }, call: NamedCall): TierSeat | undefined;
359
+ export declare function seatOf<V>(site: MemberSeatSite, table: {
360
+ get(name: string): V | undefined;
361
+ }, call: NamedCall): Seat<V> | undefined;
362
+ export declare function seatOf(site: MemberSeatSite, table: {
363
+ has(name: string): boolean;
364
+ }, call: NamedCall): Seat<true> | undefined;
365
+ export declare function seatOf<V>(site: MemberSeatSite, table: {
366
+ get(name: string): V | undefined;
367
+ } | {
368
+ has(name: string): boolean;
369
+ }, call: NamedCall): Seat<V | true> | undefined;
370
+ export {};
@@ -0,0 +1,137 @@
1
+ const GATE = "src/core/runner/prepare-gate-stations.ts";
2
+ const CHAIN = "src/core/runner/prepare-policy-chain.ts";
3
+ const LANES = "src/core/gate-lanes.ts";
4
+ const POLICY = "src/core/tool-policy.ts";
5
+ const OFFER = "src/core/runner/permission-rule-lanes.ts";
6
+ const PARK = "src/core/runner/prepare-park-ask.ts";
7
+ const HANDLERS = "src/core/runner/run-harness-handlers.ts";
8
+ const any = (file) => ({ kind: "any", file });
9
+ const wire = (file) => ({ kind: "wire", file });
10
+ const spelling = (file) => ({ kind: "spelling", file });
11
+ const seat = (rank, file) => ({ kind: "seat", rank, file });
12
+ export const NAME_READINGS = {
13
+ "gate.compliance": any(GATE),
14
+ "gate.egress": any(GATE),
15
+ "gate.peerMessage": any(GATE),
16
+ "gate.markSources": any(GATE),
17
+ "gate.shellSeat": seat("tier", GATE),
18
+ "gate.sandboxBoundary.egress": any(CHAIN),
19
+ "gate.sandboxBoundary.hand": wire(CHAIN),
20
+ "gate.preToolUse": wire("src/core/hooks.ts"),
21
+ "fold.readStation": seat("member", "src/tools/fs/shell-read-check.ts"),
22
+ "lanes.contentQuestionClass": any(LANES),
23
+ "lanes.readOnlyShellAllow": wire(LANES),
24
+ "lanes.modeFileCommand": wire(LANES),
25
+ "lanes.contentAskRoute": wire(LANES),
26
+ "lanes.orgContentAskExemption": wire(LANES),
27
+ "lanes.persistedEditSkip": wire(LANES),
28
+ "lanes.persistedLaneSkip": wire(LANES),
29
+ "lanes.allowLayerQuestionSkip": wire(LANES),
30
+ "lanes.sandboxQuestionSkip": wire(LANES),
31
+ "policy.nameLists": any(POLICY),
32
+ "policy.shellToolsList": any(POLICY),
33
+ "policy.projectionReplay": any(POLICY),
34
+ "policy.sessionRules": any("src/core/runner/session-rule-policy.ts"),
35
+ "policy.sessionRules.effect": wire("src/core/runner/session-rule-policy.ts"),
36
+ "policy.skillScope": any("src/core/runner/active-skill-scope.ts"),
37
+ "policy.skillScope.effect": wire("src/core/runner/active-skill-scope.ts"),
38
+ "policy.permissionRules": any("src/core/permission-rules.ts"),
39
+ "policy.persistedGrammar": any("src/core/permission-rule-model.ts"),
40
+ "policy.durableQuestion": any("src/core/ask-question.ts"),
41
+ "policy.sensitivePathGuardedByName": any("src/core/sensitive-path-policy.ts"),
42
+ "floor.protectivePathTarget": seat("member", "src/core/tool-registry.ts"),
43
+ "mode.planRosterRow": wire("src/core/mode-policy.ts"),
44
+ "offer.laneTool": wire(OFFER),
45
+ "offer.orgQuestionExemption": wire(OFFER),
46
+ "offer.marks": seat("tier", OFFER),
47
+ "offer.marks.egress": any(OFFER),
48
+ "park.riskDescriptor": seat("tier", PARK),
49
+ "park.syncFirstRoute": wire(PARK),
50
+ "report.riskAxes": seat("tier", CHAIN),
51
+ "report.riskAxes.egress": any(CHAIN),
52
+ "report.riskAxes.negatives": seat("member", CHAIN),
53
+ "report.markSources": any(CHAIN),
54
+ "verify.wroteThisRun": wire(HANDLERS),
55
+ "verify.wroteThisRunSeed": wire("src/core/runner/run-attachment-seats.ts"),
56
+ "event.toolStartEnd": spelling(HANDLERS),
57
+ "hook.postToolUse": wire(GATE),
58
+ "hook.postToolUseFailure": wire(GATE),
59
+ "hook.postToolBatch": spelling(HANDLERS),
60
+ "stats.subagentToolCounter": spelling("src/agents/subagent.ts"),
61
+ "activity.sink": spelling(HANDLERS),
62
+ "reconcile.orphanEffect": spelling("src/core/session-reconcile.ts"),
63
+ "inherited.questionFloat": wire(CHAIN),
64
+ "park.contentKindProjection": wire("src/core/checkpoint-store.ts"),
65
+ "resume.contentAskRow": wire("src/core/runner/decide-continuation.ts"),
66
+ "resume.editRecheckQuestionSkip": wire("src/core/runner/resume-apply.ts"),
67
+ "resume.questionArgsShown": wire("src/core/runner/run-leg.ts"),
68
+ "budget.exemptResult": wire("src/core/tool-result-budget.ts"),
69
+ "compaction.compactableResult": wire("src/core/context-edit.ts"),
70
+ };
71
+ export function ruleNamesOf(call) {
72
+ const names = call.face?.names;
73
+ return names !== undefined && names.includes(call.toolName) ? names : [call.toolName];
74
+ }
75
+ function readingOf(site, kind) {
76
+ const r = NAME_READINGS[site];
77
+ if (r.kind !== kind)
78
+ throw new Error(`tool-name reading: site ${JSON.stringify(site)} is a ${r.kind} site, read through the ${kind} port`);
79
+ return r;
80
+ }
81
+ export function heldBy(site, set, call) {
82
+ readingOf(site, "any");
83
+ return ruleNamesOf(call).some((n) => set.has(n));
84
+ }
85
+ export function heldValues(site, map, call) {
86
+ readingOf(site, "any");
87
+ const out = [];
88
+ for (const n of ruleNamesOf(call)) {
89
+ const v = map.get(n);
90
+ if (v !== undefined)
91
+ out.push(v);
92
+ }
93
+ return out;
94
+ }
95
+ export function wireOf(site, call) {
96
+ readingOf(site, "wire");
97
+ return call.toolName;
98
+ }
99
+ const DEPLOYMENT_MARK = "deployment";
100
+ const TIER_RANK = { never: 1, maybe: 2, always: 3 };
101
+ function seatAt(name, value) {
102
+ return {
103
+ name,
104
+ value,
105
+ read: (table) => table.get(name),
106
+ holds: (set) => set.has(name),
107
+ };
108
+ }
109
+ export function seatOf(site, table, call) {
110
+ const r = readingOf(site, "seat");
111
+ const names = ruleNamesOf(call);
112
+ const valueAt = (n) => (typeof table.get === "function" ? table.get(n) : table.has(n) ? true : undefined);
113
+ if (r.kind === "seat" && r.rank === "tier") {
114
+ let best;
115
+ for (const n of names) {
116
+ const v = valueAt(n);
117
+ if (v === undefined)
118
+ continue;
119
+ if (best === undefined || TIER_RANK[v] > TIER_RANK[best.value])
120
+ best = { name: n, value: v };
121
+ }
122
+ if (best === undefined)
123
+ return undefined;
124
+ const chosen = seatAt(best.name, best.value);
125
+ const tierSeat = {
126
+ ...chosen,
127
+ shellGated: (shellGated, markSources) => shellGated.has(chosen.name) && !names.some((n) => markSources.get(n)?.has(DEPLOYMENT_MARK) === true),
128
+ };
129
+ return tierSeat;
130
+ }
131
+ for (const n of names) {
132
+ const v = valueAt(n);
133
+ if (v !== undefined)
134
+ return seatAt(n, v);
135
+ }
136
+ return undefined;
137
+ }