@sema-agent/server 7.106.0 → 7.108.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 (70) hide show
  1. package/MIGRATION.md +52 -0
  2. package/README.md +2 -2
  3. package/USAGE.md +40 -4
  4. package/dist/approval-ask-audit-store.d.ts +62 -4
  5. package/dist/approval-ask-audit-store.js +67 -11
  6. package/dist/approval.js +16 -13
  7. package/dist/boot/coordinators.d.ts +4 -0
  8. package/dist/boot/coordinators.js +3 -2
  9. package/dist/boot/shutdown.d.ts +8 -0
  10. package/dist/boot/shutdown.js +31 -8
  11. package/dist/boot/stage-06-runners.d.ts +1 -0
  12. package/dist/boot/stage-06-runners.js +6 -3
  13. package/dist/boot/stage-07-capability-layer.d.ts +1 -0
  14. package/dist/boot/stage-08-reapers.d.ts +1 -0
  15. package/dist/boot/stage-09-leader.d.ts +1 -0
  16. package/dist/boot/stage-10-http-server.d.ts +1 -0
  17. package/dist/boot/stage-10-http-server.js +3 -1
  18. package/dist/brain.js +2 -1
  19. package/dist/capabilities/composite-members.d.ts +1 -1
  20. package/dist/capabilities/composite-members.js +1 -1
  21. package/dist/config-catalog.d.ts +6 -1
  22. package/dist/config-catalog.js +34 -17
  23. package/dist/config-center/apply-effective.js +2 -0
  24. package/dist/config-center/types.d.ts +7 -0
  25. package/dist/deployment-governance.js +2 -1
  26. package/dist/fleet-client.d.ts +10 -2
  27. package/dist/fleet-client.js +14 -8
  28. package/dist/http/memory-compliance-failure-reply.d.ts +41 -0
  29. package/dist/http/memory-compliance-failure-reply.js +73 -0
  30. package/dist/http/memory-store-refusal-reply.d.ts +2 -2
  31. package/dist/http/resume-legs.js +3 -2
  32. package/dist/http/routes/capabilities.js +0 -2
  33. package/dist/http/routes/diagnostics.js +2 -0
  34. package/dist/http/routes/memory-compliance.js +2 -34
  35. package/dist/http/routes/memory-origin.d.ts +1 -1
  36. package/dist/http/routes/sessions.js +3 -11
  37. package/dist/http/routes/side-query.js +7 -2
  38. package/dist/http/routes/tasks.js +2 -1
  39. package/dist/memory-store-refusal.d.ts +2 -1
  40. package/dist/model-entry-headers.d.ts +41 -0
  41. package/dist/model-entry-headers.js +61 -0
  42. package/dist/model-entry-refusal.d.ts +6 -0
  43. package/dist/model-entry-refusal.js +4 -0
  44. package/dist/model-reach-scope.d.ts +55 -0
  45. package/dist/model-reach-scope.js +16 -0
  46. package/dist/model-unreachable-notice.d.ts +38 -0
  47. package/dist/model-unreachable-notice.js +72 -0
  48. package/dist/observability/fail-open.d.ts +4 -0
  49. package/dist/observability/fail-open.js +4 -0
  50. package/dist/observability/run-terminal.d.ts +8 -5
  51. package/dist/outbound-dispatcher.d.ts +69 -0
  52. package/dist/outbound-dispatcher.js +123 -3
  53. package/dist/plugins/park-commit-watch.d.ts +48 -0
  54. package/dist/plugins/park-commit-watch.js +87 -0
  55. package/dist/router/route-orchestration.js +7 -2
  56. package/dist/runs.js +3 -2
  57. package/dist/runtime-governance.js +3 -2
  58. package/dist/task-settings.d.ts +30 -17
  59. package/dist/task-settings.js +15 -4
  60. package/dist/tool-approval.d.ts +62 -0
  61. package/dist/tool-approval.js +67 -3
  62. package/dist/tool-rule-names.d.ts +25 -0
  63. package/dist/tool-rule-names.js +5 -0
  64. package/dist/trace/core-keyset-guard.d.ts +13 -1
  65. package/dist/trace/engine-notice-wire.d.ts +3 -1
  66. package/dist/trace/engine-notice-wire.js +2 -0
  67. package/dist/trace/project.d.ts +6 -3
  68. package/dist/trace/self-minted-wire.d.ts +71 -0
  69. package/dist/trace/self-minted-wire.js +28 -0
  70. package/package.json +2 -2
@@ -5,6 +5,7 @@ import { type CardRulePersisted, type RuleConsentLane } from "./rules-consent.js
5
5
  import { type DenialLimitFallback, type RuleOffersAbsence, type ApprovalRequestFrame, type ApprovalRevokeFrame, type RuleEvidenceProjection } from "./approval-card.js";
6
6
  import { type ApprovalAskStore, type AskRow } from "./plugins/approval-ask-store-sql.js";
7
7
  import type { ApprovalAskAuditSink } from "./approval-ask-audit-store.js";
8
+ import type { ParkCommitWatch } from "./plugins/park-commit-watch.js";
8
9
  import { type GovernanceAskMarks } from "./governance-ask-marks.js";
9
10
  import { type GovernancePosture } from "./runtime-governance.js";
10
11
  /** [ref]:审批 gate kind 闭集(core gateMatch 的 `human`/`irreversible_ask` ↔ outcome.gate "policy_ask")。
@@ -1126,6 +1127,15 @@ export declare class ToolApprovalCoordinator {
1126
1127
  /** S-877 候裁 16 → A:耐久会话授权店的**读面**(`/decide` 的 `remember:"session"` 写、本席读)。在场 ⇔ 部署有 store backend
1127
1128
  * (装配点 `boot/coordinators.ts`,与 `/decide` 写的是**同一只**店)。读口唯一 = {@link standingGrantSettles}。 */
1128
1129
  private readonly sessionGrants;
1130
+ /** S-929:有序关停已开始(`drainForShutdown` 锁存,不复位 —— 进程正在退出)。之后登记的每一张卡在登记那一刻按关停收场。 */
1131
+ private shutdownDraining;
1132
+ /** S-929:此刻在 `askBroadcast` 里的 ask 数(进出成对,见那里的 try/finally)—— 关停等待「还没登记进 `pending` 的卡」的计数。 */
1133
+ private asksInFlight;
1134
+ /** S-929 F1:park 提交的观察面(checkpoint 店的装饰层;在场 ⇔ 本部署有 park 设施,装配点 `boot/stage-06-runners.ts`)。 */
1135
+ private readonly parkCommits;
1136
+ /** S-929 F1:有序关停下路由到 park、**处置尚未被观察到**的卡 —— 审计 settle 行在这里候着({@link settleParksAwaitingCommit})。
1137
+ * 只在关停期有成员(进程随后退出,不需要回收)。 */
1138
+ private readonly parksAwaitingCommit;
1129
1139
  constructor(opts?: {
1130
1140
  ttlMs?: number;
1131
1141
  /** [ref](案A):零活流铸造的 ask 的窗(见 {@link unreachedTtlMs})。装配点在协议**上场**时才传
@@ -1155,6 +1165,8 @@ export declare class ToolApprovalCoordinator {
1155
1165
  askAudit?: ApprovalAskAuditSink;
1156
1166
  /** S-877 候裁 16:耐久会话授权店的读面(见 {@link ToolApprovalCoordinator.sessionGrants})。 */
1157
1167
  sessionGrants?: SessionGrantReader;
1168
+ /** S-929 F1:park 提交的观察面(见 {@link ToolApprovalCoordinator.parkCommits})。 */
1169
+ parkCommits?: ParkCommitWatch;
1158
1170
  });
1159
1171
  /**
1160
1172
  * [ref] R-13 C —— 「**无人可答**」这一类终局的**唯一**成形口(park 路由 vs deny 政策)。
@@ -1309,6 +1321,54 @@ export declare class ToolApprovalCoordinator {
1309
1321
  * 流没了,值相等即目标集;极端复用形下多转的那条也只是从流内卡变 durable 卡,方向保守。
1310
1322
  * 返回触发条数(0 = 无流内未决 ask,调用方照旧 abort)。幂等:已结算条目跳过,重复调用无害。 */
1311
1323
  parkOpenAsksForTask(wireTaskId: string): number;
1324
+ /**
1325
+ * 🔴 **S-929 —— 有序关停的审批面收尾(一只;`boot/shutdown.ts` 的 `hardShutdown` 起点调,SIGTERM / SIGINT / SIGHUP 三条停止路同一处)。**
1326
+ *
1327
+ * 病(cli [ref] S7):修前关停序不碰协调器 ⇒ 本进程握着的活卡在下一次 boot 被审计收敛器标成 `crashed_before_park`,
1328
+ * 用户屏上看到「crashed」而实为有序重启。
1329
+ *
1330
+ * 三步:
1331
+ * ① 本进程握着的**每一张**未决卡(可达 / 悬挂 / 委派子代,不挑)按**断连同路**收场 —— {@link PendingApproval.forceParkNow}(park 路由;
1332
+ * `deny` 政策下是部署政策拒;有持久 ask 店时走同一把终局 claim)。有序关停与断连是同一件事:投递面要没了,**不是**有人拒了
1333
+ * (不交裸 `false`,S-689 同律)。审计 settle 行携 `cause:"shutdown_before_park"`(成因在 `settle` 咽喉读锁存位,F10);
1334
+ * ② 在 `deadlineMs` 之前等:未决卡结清、在飞腿闭合(闭了的腿照常落 `leg_closed`)、**本次路由到 park 的卡全部结清**
1335
+ * (F1:观察到 core 已处置这次调用 —— 停车行提交或承载它的腿退场 ⇒ 那张卡的审计 settle 行此刻才写,见 {@link settleParksAwaitingCommit})、
1336
+ * 店里在飞的 `put` 静默;
1337
+ * ③ 到期:仍未闭的腿 ⇒ 审计落 `interrupted_by_shutdown` 记号(下一次 boot 收敛照样列出它,decided 臂的 `resumeSafe:false` 提醒不丢);
1338
+ * 仍未提交的 park ⇒ settle 行**不写**(账本上仍是 pending 孤儿 ⇒ 下一次 boot 与崩溃同路收敛,证据不丢),交回调用方
1339
+ * (`uncommittedParks`,运维 warn `approval_park_uncommitted_at_shutdown` 由关停序落 —— 它握 logger)。之后才观察到的照样写 settle 行
1340
+ * (观察到就是结清,迟到也是)。
1341
+ * **一条规则覆盖先后两批卡**(codex r1 F2):收尾一开始就锁存 {@link shutdownDraining};此后**登记**的卡(收尾开始时还在 `ensureAsk`
1342
+ * 的 await 里 —— 零活流的后台 / 委派子代也在内)在登记那一刻按同一条路收场(`askBroadcast` 的铸造审计点之后读这一位)。等待条件
1343
+ * 因此数「在飞的 ask」({@link asksInFlight},`askBroadcast` 进出计数)而不只是 `pending` —— 尚未登记的卡不在 `pending` 里。
1344
+ * `deadlineMs` 是绝对时刻(`Date.now()` 域):关停序对审批面与店 quiesce 读**同一个**到期点(一个宽限窗,不是两个)。
1345
+ * 从不抛错(关停序不许被审计面卡住);再调一次 = 再等一次(记号有去重集,零重写)。
1346
+ */
1347
+ drainForShutdown(deadlineMs: number): Promise<{
1348
+ uncommittedParks: Array<{
1349
+ approvalId: string;
1350
+ taskId: string;
1351
+ }>;
1352
+ }>;
1353
+ /**
1354
+ * S-929 F1:checkpoint 店的观察层报「一只审批停车行提交了」({@link CommittedParkCalls}:行自己陈述的会话与握着的调用)⇒ 候着的
1355
+ * **同一次调用**结清:执行会话**相等**(候者记的是 ask 自己陈述的发起会话,行陈述的是它停住的会话 —— core 同一个变量)∧ 调用 id ∈ 行握着的调用。
1356
+ * 不做「会话相交」:调用 id 只在 run 内唯一(父子各自的第一只调用常同名),宿主会话若作子代的等价别名,宿主停车行一提交就会冒充子代的提交(车 SO)。
1357
+ * 只在关停期有候者;非关停期的提交一个不碰。
1358
+ * 🔴 观察层的订阅者契约是**不得抛**(它跑在 `put` 已提交之后、返回 core 之前 —— 抛了会把一次已提交的 park 报成失败),见下面的写行口。
1359
+ */
1360
+ private noteParkCommitted;
1361
+ /**
1362
+ * S-929 F1 的**结清口**(一处):有序关停下 park 路由的卡,在**观察到 core 已处置这次调用**时写它的审计 settle 行 —— 两种观察,同一件事实:
1363
+ * · `park-committed` —— 停车行提交了(观察层看到 `put` 落定,或 core 回读确认的那一次 `get` 读到了行);
1364
+ * · `leg-closed` —— **承载它的那条腿**退场了(`runWithContext` finally 按 ctx 身份配 {@link ParkAwaitingCommit.carrierLeg}:core 已走完这条腿 ——
1365
+ * 停靠了,或拒了停靠 fail-closed 继续)。
1366
+ * 两者都没观察到就退出 ⇒ 这行不写,与崩溃同路(下一次 boot 列 pending 孤儿)。委派 / 后台子代与零活流的卡没有被追踪的承载腿,只有第一种观察
1367
+ * (子代的出处是宿主:宿主腿退场不是子代这次调用的处置 —— 车 SO)。
1368
+ * 写行的 sink 自吞失败;sink 实现**自身**抛的那一形在这里兜住并按 [ref] 留痕,同 tag 扩射程(`http/server.ts` `noteLateSettleIfAccepted`
1369
+ * 同一先例):丢的只是这一条审计行 —— 下一次 boot 它被当 pending 孤儿列出(证据方向),park 行本身不受影响。
1370
+ */
1371
+ private settleParksAwaitingCommit;
1312
1372
  /** 测试/可观测性钩子(X-2):某 (taskId) 维当前占用的准入名额数。 */
1313
1373
  admittedCount(taskId: string): number;
1314
1374
  /** [ref] 车2(D5 store 故障姿势):任何 store 调用 throw ⇒ fail-open 到进程内机械照旧(store 是记账/
@@ -1570,6 +1630,8 @@ export declare class ToolApprovalCoordinator {
1570
1630
  * 送达某个客户端之后发生(respond 需要客户端手上有这个 id),这是 outcome==="allowed"/"denied" 与
1571
1631
  * "expired"(TTL/abort/run-exit-cleanup/emit 广播全灭四类共用)的天然分野。 */
1572
1632
  private askBroadcast;
1633
+ /** {@link askBroadcast} 的本体(计数在外层,一处进出)。 */
1634
+ private askBroadcastCounted;
1573
1635
  /** `POST /v1/tool-approvals/:id/respond` — settle a parked approval with the shell's decision. Owner-gated with a
1574
1636
  * 404 (no existence oracle), body validated first (400 is existence-independent) — question/steer parity. The HTTP
1575
1637
  * layer owns auth (gatedPrincipal + REQUIRE_PRINCIPAL) before calling.
@@ -26,6 +26,8 @@ const SUSPENDED_POLL_MAX_PER_TICK = 64;
26
26
  const SUSPENDED_POLL_BUDGET_MS = 5_000;
27
27
  const EDITED_DECISION_MARK_CAP_MS = 10_000;
28
28
  const DEFAULT_WINDOW_MARGIN_MS = 10_000;
29
+ const SHUTDOWN_DRAIN_POLL_MS = 50;
30
+ const SHUTDOWN_BEFORE_PARK = "shutdown_before_park";
29
31
  const MAX_ALLOW_SESSIONS = 4096;
30
32
  const DEFAULT_ADMIT_MAX_PER_TASK = 32;
31
33
  const DEFAULT_ADMIT_MAX_PER_OWNER = 256;
@@ -364,6 +366,10 @@ export class ToolApprovalCoordinator {
364
366
  parkedRedeem;
365
367
  askAudit;
366
368
  sessionGrants;
369
+ shutdownDraining = false;
370
+ asksInFlight = 0;
371
+ parkCommits;
372
+ parksAwaitingCommit = new Set();
367
373
  constructor(opts) {
368
374
  this.ruleConsent = opts?.ruleConsent;
369
375
  this.ruleScopeRootFor = opts?.ruleScopeRootFor;
@@ -381,6 +387,8 @@ export class ToolApprovalCoordinator {
381
387
  this.parkTombstonePerPrincipalMax = opts?.parkTombstonePerPrincipalMax ?? DEFAULT_PARK_TOMBSTONE_PER_PRINCIPAL_MAX;
382
388
  this.askAudit = opts?.askAudit;
383
389
  this.sessionGrants = opts?.sessionGrants;
390
+ this.parkCommits = opts?.parkCommits;
391
+ this.parkCommits?.onParkCommitted((calls) => this.noteParkCommitted(calls));
384
392
  }
385
393
  unattendedOutcome(site) {
386
394
  let outcome;
@@ -644,6 +652,34 @@ export class ToolApprovalCoordinator {
644
652
  }
645
653
  return n;
646
654
  }
655
+ async drainForShutdown(deadlineMs) {
656
+ this.shutdownDraining = true;
657
+ for (const e of [...this.pending.values()])
658
+ if (e.settledOutcome === undefined)
659
+ e.forceParkNow();
660
+ while ((this.asksInFlight > 0 || this.pending.size > 0 || this.streams.size > 0 || this.parksAwaitingCommit.size > 0) && Date.now() < deadlineMs) {
661
+ await new Promise((r) => setTimeout(r, SHUTDOWN_DRAIN_POLL_MS));
662
+ }
663
+ await this.parkCommits?.quiesce(deadlineMs);
664
+ this.askAudit?.noteLegsInterruptedByShutdown();
665
+ return { uncommittedParks: [...this.parksAwaitingCommit].map((p) => ({ approvalId: p.approvalId, taskId: p.taskId })) };
666
+ }
667
+ noteParkCommitted(calls) {
668
+ this.settleParksAwaitingCommit((p) => p.executingSessionId === calls.sessionId && calls.toolCallIds.includes(p.toolCallId), "park-committed");
669
+ }
670
+ settleParksAwaitingCommit(match, site) {
671
+ for (const p of [...this.parksAwaitingCommit]) {
672
+ if (!match(p))
673
+ continue;
674
+ this.parksAwaitingCommit.delete(p);
675
+ try {
676
+ this.askAudit?.recordSettle(p.settleRecord());
677
+ }
678
+ catch (err) {
679
+ recordFailOpen("server.approval-ask-audit.append-failed", `kind=settle site=${site} err=${err instanceof Error ? err.message : String(err)}`);
680
+ }
681
+ }
682
+ }
647
683
  admittedCount(taskId) {
648
684
  return this.admitByTask.get(taskId) ?? 0;
649
685
  }
@@ -950,6 +986,7 @@ export class ToolApprovalCoordinator {
950
986
  if (p.targetCtxs?.has(ctx))
951
987
  p.onTargetGone?.(ctx);
952
988
  }
989
+ this.settleParksAwaitingCommit((p) => p.carrierLeg === ctx, "leg-closed");
953
990
  this.askAudit?.noteLegClosed(ctx.taskId);
954
991
  }
955
992
  }));
@@ -983,6 +1020,15 @@ export class ToolApprovalCoordinator {
983
1020
  };
984
1021
  };
985
1022
  async askBroadcast(origin, ctxs, req, signal) {
1023
+ this.asksInFlight++;
1024
+ try {
1025
+ return await this.askBroadcastCounted(origin, ctxs, req, signal);
1026
+ }
1027
+ finally {
1028
+ this.asksInFlight--;
1029
+ }
1030
+ }
1031
+ async askBroadcastCounted(origin, ctxs, req, signal) {
986
1032
  if (signal?.aborted)
987
1033
  return false;
988
1034
  const governanceForced = this.governanceForcedOf(origin, req);
@@ -1008,6 +1054,7 @@ export class ToolApprovalCoordinator {
1008
1054
  }
1009
1055
  const id = uuidv7();
1010
1056
  const child = isChildAsk(req);
1057
+ const carrierLeg = child ? undefined : aliveCtxs.find((c) => c.taskId === origin.taskId);
1011
1058
  const bounded = boundArgs(req.args);
1012
1059
  let effectiveGovernanceForced = governanceForced;
1013
1060
  let ruleLaneMaterial = this.buildRuleLaneMaterial(req, origin.owner, governanceForced, origin.sessionId);
@@ -1282,13 +1329,28 @@ export class ToolApprovalCoordinator {
1282
1329
  releaseAdmission();
1283
1330
  if (selfEntry)
1284
1331
  selfEntry.settledOutcome = outcome;
1285
- this.askAudit?.recordSettle({
1332
+ const shutdownEnded = this.shutdownDraining && outcome === "expired";
1333
+ const settleRecord = () => ({
1286
1334
  approvalId: wireApprovalId,
1287
1335
  outcome,
1288
1336
  ...(windowRouteUnavailable ? { parkRouted: true } : {}),
1289
1337
  ...(hostSettledBy !== undefined ? { settledBy: hostSettledBy } : {}),
1338
+ ...(shutdownEnded ? { cause: SHUTDOWN_BEFORE_PARK } : {}),
1290
1339
  settledAtMs: Date.now(),
1291
1340
  });
1341
+ if (shutdownEnded && windowRouteUnavailable && this.unattendedRoutesToPark && this.parkCommits !== undefined) {
1342
+ this.parksAwaitingCommit.add({
1343
+ approvalId: wireApprovalId,
1344
+ taskId: origin.taskId,
1345
+ carrierLeg,
1346
+ executingSessionId: req.sourceTaskId,
1347
+ toolCallId: req.toolCallId,
1348
+ settleRecord,
1349
+ });
1350
+ }
1351
+ else {
1352
+ this.askAudit?.recordSettle(settleRecord());
1353
+ }
1292
1354
  resolveAllowed({ allowed, ...(allowed && updatedInput !== undefined ? { updatedInput } : {}), ...(allowed && readRootGrant !== undefined ? { readRootGrant } : {}) });
1293
1355
  };
1294
1356
  const broadcastClaimTruth = (read) => {
@@ -1471,7 +1533,7 @@ export class ToolApprovalCoordinator {
1471
1533
  if (done)
1472
1534
  return;
1473
1535
  if (!this.askStore || askId === undefined || batchId === undefined) {
1474
- settleParkRouteIfUnsettled();
1536
+ settleParkRouteIfUnsettled(undefined);
1475
1537
  return;
1476
1538
  }
1477
1539
  windowExpired(undefined);
@@ -1586,10 +1648,12 @@ export class ToolApprovalCoordinator {
1586
1648
  owner: origin.owner,
1587
1649
  toolName: req.toolName,
1588
1650
  toolCallId: req.toolCallId,
1589
- legTracked: !child && aliveCtxs.some((c) => c.taskId === origin.taskId),
1651
+ legTracked: carrierLeg !== undefined,
1590
1652
  expiresAtMs: persistedExpiresAtMs,
1591
1653
  mintedAtMs: Date.now(),
1592
1654
  });
1655
+ if (this.shutdownDraining)
1656
+ forceParkNow();
1593
1657
  const cardFrame = askId !== undefined && cardForFrame !== undefined
1594
1658
  ? buildApprovalRequestFrame({ askId, taskId: origin.taskId, approvalId: wireApprovalId, card: cardForFrame, expiresAtMs: persistedExpiresAtMs, nowMs: Date.now() })
1595
1659
  : undefined;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * S-919(安全轴,八病审计 SG H2-a)—— 本仓**收紧方向**按引擎工具名匹配的策略臂的**唯一**名字读法。
3
+ *
4
+ * ## 规则(一条,与 core 7.36.0 [ref] `NAME_READINGS` 同判)
5
+ * 一个调用可以答好几个名字:嵌入方工具声明别名(`MyShell{aliases:["Bash"]}`)后,模型按哪个名调它、它的 wire 名、
6
+ * 解到同一行的其余别名,都是这次调用的名字。core 把**收紧**方向的名单(`policy.nameLists` / `policy.shellToolsList` /
7
+ * `policy.durableQuestion` / `policy.sensitivePathGuardedByName`)读成 `any` —— 调用答得上的**任何**一个名字在名单里就算
8
+ * 命中;**授权 / 路由**方向的座(会话放行、审批卡分型、停车行)读 `wire`(`req.toolName`)。core 给宿主的读法端口是包根
9
+ * `ruleNamesOf(req)`(`dist/core/tool-name-readings.js`,亲读实现:`face.names` 含调用名时取它,否则只有调用名;拼写在先)。
10
+ *
11
+ * 本仓照此分两边:
12
+ * · **收紧臂**(`APPROVAL_DENY` / `APPROVAL_REQUIRE` / `APPROVAL_NEVER_AUTO`、commandPolicy 的 ask 表、治理 shell 标、
13
+ * durable 问题门、supervisor 危险门、workflow 问 / 拒)一律经本函数读全集;
14
+ * · **授权 / 路由臂**(会话豁免探针、会话放行键、审批卡分型、停车行的 AskUserQuestion 判别)读 `req.toolName`,不经这里 ——
15
+ * 用户对 `Bash` 说过的「本会话不再询问」不许替一只叫 `MyShell` 的工具放行(放宽臂不因别名放宽)。
16
+ *
17
+ * 修前(≤7.107.0)本仓自写的这些臂只比 `req.toolName`:模型按别名调嵌入方工具时,运维名单一个都不拦(core 同族策略判 deny、
18
+ * 本仓判 allow)。本仓自身不声明带别名的工具,所以只有经 `resolveSpec` 席挂别名工具的嵌入方走得到。
19
+ *
20
+ * 返回**命中的那个名字**(调用答得上的名字里第一个被规则够到的,拼写在先)—— 规则归因(`matchedAskRule`)写运维名单里的那个名,
21
+ * 不写一个名单里查不到的 wire 名;够不到 ⇒ `undefined`。判定式(精确集 / covering 规则)由调用方给,本函数只管「读哪些名字」。
22
+ */
23
+ import { type ToolCallRequest } from "@sema-agent/core";
24
+ export declare function heldRuleName(req: Pick<ToolCallRequest, "toolName" | "face">, holds: (name: string) => boolean): string | undefined;
25
+ //# sourceMappingURL=tool-rule-names.d.ts.map
@@ -0,0 +1,5 @@
1
+ import { ruleNamesOf } from "@sema-agent/core";
2
+ export function heldRuleName(req, holds) {
3
+ return ruleNamesOf(req).find(holds);
4
+ }
5
+ //# sourceMappingURL=tool-rule-names.js.map
@@ -17,7 +17,7 @@
17
17
  * ⑨⑩([ref],2026-08-31)把两个最大的 wire 面也纳入:TaskSpec 按「server 有无写点」记 Wired/Unwired,
18
18
  * TaskResult 按「转发/摘除」记 Forwarded/Stripped —— 谁改 resolve-spec.ts / runs.ts 的发射点谁同步本文件。
19
19
  */
20
- import type { TaskNotificationPayload, SemaProvenance, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, PendingAction, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, WiringManifest, GateOutcome, GateDisposition, Settlement, PausedCause, WorkflowRun, ReadRootGrantCandidate, McpReconnectResult, McpServerStatus, NodeLspManagerOptions, RunnerDeps } from "@sema-agent/core";
20
+ import type { TaskNotificationPayload, SemaProvenance, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, PendingAction, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult, SideQueryResult, WiringManifest, GateOutcome, GateDisposition, Settlement, PausedCause, WorkflowRun, ReadRootGrantCandidate, McpReconnectResult, McpServerStatus, NodeLspManagerOptions, RunnerDeps } from "@sema-agent/core";
21
21
  import type { ToolApprovalFrame } from "../tool-approval.js";
22
22
  /** 编译期断言:T 必须收敛到 never(有残余键 = tsc 红)。
23
23
  * 🔴 **导出**([ref] U1/U2):本仓「词表闭集,未处置成员=编译红」的钉法只许有**一个**形 ——
@@ -157,6 +157,14 @@ type PausedCauseForwarded = "kind" | "gate" | "checkpointId" | "restoreMode";
157
157
  type PausedCauseStripped = "token";
158
158
  type _GuardPausedCause = AssertAllKeysHandled<Exclude<keyof PausedCause, PausedCauseForwarded | PausedCauseStripped>>;
159
159
  type _GuardPausedCauseReverse = AssertAllKeysHandled<Exclude<PausedCauseForwarded | PausedCauseStripped, keyof PausedCause>>;
160
+ type SideQueryResultForwarded = "text" | "toolCalls" | "model" | "servedModel" | "degraded" | "responseModel" | "usage" | "usageMissing" | "stopReason" | "errorMessage";
161
+ type SideQueryResultStripped = never;
162
+ type _GuardSideQueryResult = AssertAllKeysHandled<Exclude<keyof SideQueryResult, SideQueryResultForwarded | SideQueryResultStripped>>;
163
+ type _GuardSideQueryResultReverse = AssertAllKeysHandled<Exclude<SideQueryResultForwarded | SideQueryResultStripped, keyof SideQueryResult>>;
164
+ type SideQueryToolCallForwarded = "id" | "wireId" | "name" | "args";
165
+ type SideQueryToolCallStripped = never;
166
+ type _GuardSideQueryToolCall = AssertAllKeysHandled<Exclude<keyof SideQueryResult["toolCalls"][number], SideQueryToolCallForwarded | SideQueryToolCallStripped>>;
167
+ type _GuardSideQueryToolCallReverse = AssertAllKeysHandled<Exclude<SideQueryToolCallForwarded | SideQueryToolCallStripped, keyof SideQueryResult["toolCalls"][number]>>;
160
168
  type SettlementKeys = Settlement extends infer S ? (S extends object ? keyof S : never) : never;
161
169
  type SettlementWhoKeys = Settlement["who"] extends infer W ? (W extends object ? keyof W : never) : never;
162
170
  type GateOutcomeProjected = "disposition" | "settlement" | "origin";
@@ -289,6 +297,10 @@ export type CoreKeysetGuards = [
289
297
  _GuardTaskResultReverse,
290
298
  _GuardPausedCause,
291
299
  _GuardPausedCauseReverse,
300
+ _GuardSideQueryResult,
301
+ _GuardSideQueryResultReverse,
302
+ _GuardSideQueryToolCall,
303
+ _GuardSideQueryToolCallReverse,
292
304
  _GuardGateOutcome,
293
305
  _GuardGateOutcomeReverse,
294
306
  _GuardGateDisposition,
@@ -16,7 +16,7 @@
16
16
  * 同文件 G1 e2e 的全码扇出夹具、以及契约文档附录 D.3 那张表(含**小节标题里的码数** —— 那道门本批
17
17
  * 刚立,立完就在下一次加码时自己咬住了)。
18
18
  */
19
- export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval", "config.durable_gate_unavailable", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "mcp.injection_dropped", "delegation.ask_unresolvable", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "instructions.source_changed", "session.records_skipped", "classifier.unavailable", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable"];
19
+ export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval", "config.durable_gate_unavailable", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "mcp.injection_dropped", "delegation.ask_unresolvable", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "instructions.source_changed", "session.records_skipped", "server.model_unreachable", "classifier.unavailable", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable"];
20
20
  export type EngineNoticeWireCode = (typeof ENGINE_NOTICE_WIRE_CODES)[number];
21
21
  /**
22
22
  * S-690 —— **本仓自铸**的通告码与它们的受众(码 / 铸点 / 受众三者的属主都是本仓)。
@@ -42,6 +42,8 @@ export declare const SERVER_NOTICE_AUDIENCE: {
42
42
  readonly "memory.layer_locked_legacy": "operator";
43
43
  /** S-612 —— 会话存档部分记录被跳过(唯一铸点 `src/session-records-skipped-notice.ts`);受众 user,理由见白名单键注。 */
44
44
  readonly "session.records_skipped": "user";
45
+ /** S-925 —— 模型调用因建连失败终态(唯一铸点 `src/model-unreachable-notice.ts`);受众 user,理由见白名单键注。 */
46
+ readonly "server.model_unreachable": "user";
45
47
  };
46
48
  export type ServerNoticeCode = keyof typeof SERVER_NOTICE_AUDIENCE;
47
49
  /** 该码是否本仓自铸(属主判据单点:门与投递面都读这一个)。 */
@@ -27,6 +27,7 @@ export const ENGINE_NOTICE_WIRE_CODES = [
27
27
  "approval.read_root_grant_rejected",
28
28
  "instructions.source_changed",
29
29
  "session.records_skipped",
30
+ "server.model_unreachable",
30
31
  "classifier.unavailable",
31
32
  "config.permission_mode_unavailable",
32
33
  "permission.ask_released_by_mode",
@@ -37,6 +38,7 @@ export const SERVER_NOTICE_AUDIENCE = {
37
38
  "instructions.source_changed": "user",
38
39
  "memory.layer_locked_legacy": "operator",
39
40
  "session.records_skipped": "user",
41
+ "server.model_unreachable": "user",
40
42
  };
41
43
  export function isServerNoticeCode(code) {
42
44
  return Object.hasOwn(SERVER_NOTICE_AUDIENCE, code);
@@ -296,9 +296,12 @@ export declare function toolEndEventData(ev: {
296
296
  * **不等于** `tool_end` 的 duration)。
297
297
  *
298
298
  * ## 信任划线
299
- * · `toolCallId` / `toolName` —— 调用 id(🔴 S-283 订正:**服务商**在 tool_use / tool_calls 里给的,引擎只在缺席时自铸兜底 ——
300
- * 此前写「引擎铸」不实)与名册里的工具名(与 `tool_start` 逐字同源)⇒ verbatim。调用 id 这一族在**逐帧面**上的形状闸
301
- * 不在 S-283 射程(它是全 wire 的 join 键,要闸得所有面一起闸,见 run-terminal `admitProviderEcho` 顶注与车QO 收车档)。
299
+ * · `toolCallId` / `toolName` —— 调用 id(🔴 S-283 订正:出身是**服务商**在 tool_use / tool_calls 里给的,引擎缺席时自铸兜底、越界时折叠 ——
300
+ * 此前写「引擎铸」不实)与名册里的工具名(与 `tool_start` 逐字同源)⇒ verbatim。调用 id 这一族的形状在**全部面的共同入口**一次施加 ——
301
+ * core 7.36.0(#1207)brain 建调用那一行的准入(在形原样 / 越界单射折叠,原字节只在转录 `ToolCall.wireId`),一方 brain 新铸的调用在逐帧面上
302
+ * 恒是在形的引擎 id(残余两条 S-921:≤7.106.0 写下的存量行 resume 出来的帧是服务商原串逐字;宿主 Brain 不准入 KL-1207-a —— 详见
303
+ * run-terminal `admitProviderEcho` 顶注);
304
+ * 本仓不设第二道闸(见 run-terminal `admitProviderEcho` 顶注;S-890 关)。
302
305
  * · `elapsedTimeSeconds` —— 引擎铸的整秒,单调、一 call 一个原点 ⇒ 有限数才铸键。
303
306
  * · `output` —— 🔴 **UNTRUSTED RAW**,core 逐字「exactly as `tool_end.output`: process output that may
304
307
  * carry secrets, paths or PII; core bounds only its SIZE, and a consumer MUST redact before persisting
@@ -0,0 +1,71 @@
1
+ /** 一个自铸 wire 值成员的登记。 */
2
+ export interface SelfMintedWireMember {
3
+ readonly attr: string | number;
4
+ readonly since: string;
5
+ }
6
+ export declare const SELF_MINTED_WIRE: {
7
+ /** [ref](7.55.0)起的终态成因注;S-929(7.108.0)+`shutdown_before_park`。 */
8
+ readonly crashConvergedCause: {
9
+ readonly at: "GET /v1/approvals crashConverged[].cause";
10
+ readonly members: {
11
+ crashed_before_park: {
12
+ attr: string;
13
+ since: string;
14
+ };
15
+ shutdown_before_park: {
16
+ attr: string;
17
+ since: string;
18
+ };
19
+ };
20
+ };
21
+ /**
22
+ * 会话所有者自助抹除面的**已分类拒** → HTTP 状态(S-930 起与 operator 面 `POST /v1/memory/erase` 同一只映射;`since` 记的是**本面**
23
+ * 自哪一版起这样答 —— 7.107.0 及以前本面只认前两只,其余落 500 `internal.error`)。
24
+ */
25
+ readonly sessionMemoryEraseStatus: {
26
+ readonly at: "POST /v1/sessions/:id/memory/erase(已分类拒 errorCode → HTTP 状态)";
27
+ readonly members: {
28
+ readonly "config.memory_erasure_request": {
29
+ readonly attr: 400;
30
+ readonly since: "7.52.0";
31
+ };
32
+ readonly "memory.target_outside_store": {
33
+ readonly attr: 409;
34
+ readonly since: "7.101.0";
35
+ };
36
+ readonly "memory.erasure_evidence_unavailable": {
37
+ readonly attr: 409;
38
+ readonly since: "7.108.0";
39
+ };
40
+ readonly "memory.erasure_selector_mismatch": {
41
+ readonly attr: 409;
42
+ readonly since: "7.108.0";
43
+ };
44
+ readonly "memory.erasure_census_incomplete": {
45
+ readonly attr: 409;
46
+ readonly since: "7.108.0";
47
+ };
48
+ readonly "internal.memory_control_plane_corrupt": {
49
+ readonly attr: 500;
50
+ readonly since: "7.108.0";
51
+ };
52
+ };
53
+ };
54
+ /** 本仓在出口对 core 值做的变换(值变、键不变)。 */
55
+ readonly wireValueRule: {
56
+ readonly at: "出口值变换(<端点>#<键> → 变换)";
57
+ readonly members: {
58
+ /** S-105(7.57.0):run 读面的终态错误文案脱敏(钉 test/run-failure-visibility.test.ts)。 */
59
+ readonly "GET /v1/runs/:id#result.errorMessage": {
60
+ readonly attr: "redactErrorMessage";
61
+ readonly since: "7.57.0";
62
+ };
63
+ /** S-922(7.108.0):side-query 200 体 `errorMessage` 与任务面同一只脱敏口(钉 test/side-query-provider-echo.test.ts)。 */
64
+ readonly "POST /v1/side-query#errorMessage": {
65
+ readonly attr: "redactErrorMessage";
66
+ readonly since: "7.108.0";
67
+ };
68
+ };
69
+ };
70
+ };
71
+ //# sourceMappingURL=self-minted-wire.d.ts.map
@@ -0,0 +1,28 @@
1
+ export const SELF_MINTED_WIRE = {
2
+ crashConvergedCause: {
3
+ at: "GET /v1/approvals crashConverged[].cause",
4
+ members: {
5
+ crashed_before_park: { attr: "-", since: "7.55.0" },
6
+ shutdown_before_park: { attr: "-", since: "7.108.0" },
7
+ },
8
+ },
9
+ sessionMemoryEraseStatus: {
10
+ at: "POST /v1/sessions/:id/memory/erase(已分类拒 errorCode → HTTP 状态)",
11
+ members: {
12
+ "config.memory_erasure_request": { attr: 400, since: "7.52.0" },
13
+ "memory.target_outside_store": { attr: 409, since: "7.101.0" },
14
+ "memory.erasure_evidence_unavailable": { attr: 409, since: "7.108.0" },
15
+ "memory.erasure_selector_mismatch": { attr: 409, since: "7.108.0" },
16
+ "memory.erasure_census_incomplete": { attr: 409, since: "7.108.0" },
17
+ "internal.memory_control_plane_corrupt": { attr: 500, since: "7.108.0" },
18
+ },
19
+ },
20
+ wireValueRule: {
21
+ at: "出口值变换(<端点>#<键> → 变换)",
22
+ members: {
23
+ "GET /v1/runs/:id#result.errorMessage": { attr: "redactErrorMessage", since: "7.57.0" },
24
+ "POST /v1/side-query#errorMessage": { attr: "redactErrorMessage", since: "7.108.0" },
25
+ },
26
+ },
27
+ };
28
+ //# sourceMappingURL=self-minted-wire.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.106.0",
3
+ "version": "7.108.0",
4
4
  "description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -61,7 +61,7 @@
61
61
  "build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
62
62
  },
63
63
  "dependencies": {
64
- "@sema-agent/core": "7.35.2",
64
+ "@sema-agent/core": "7.36.0",
65
65
  "@sema-agent/settings-schema": "6.3.0",
66
66
  "e2b": "^2.28.0",
67
67
  "libsodium-wrappers": "^0.8.4",