@sema-agent/server 7.103.1 → 7.104.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 (177) hide show
  1. package/MIGRATION.md +362 -3
  2. package/README.md +6 -1
  3. package/README.zh-CN.md +5 -1
  4. package/USAGE.md +279 -18
  5. package/deploy/sema-up/README.md +57 -7
  6. package/deploy/sema-up/cluster-up.sh +18 -4
  7. package/deploy/sema-up/kube-up.sh +16 -0
  8. package/deploy/sema-up/lib/dataroot.sh +142 -0
  9. package/deploy/sema-up/lib/flagshape.sh +69 -0
  10. package/deploy/sema-up/lib/lanes.py +14 -4
  11. package/deploy/sema-up/lib/shell-access.sh +41 -0
  12. package/deploy/sema-up/preflight.sh +14 -1
  13. package/deploy/sema-up/sema-up.sh +134 -37
  14. package/deploy/sema-up/smoke.sh +5 -0
  15. package/dist/auto-mode-face.js +3 -4
  16. package/dist/boot/budget-tracing.js +1 -1
  17. package/dist/boot/config-center.d.ts +6 -2
  18. package/dist/boot/config-center.js +5 -59
  19. package/dist/boot/execution-env.d.ts +11 -0
  20. package/dist/boot/execution-env.js +14 -0
  21. package/dist/boot/execution-lane.d.ts +25 -0
  22. package/dist/boot/execution-lane.js +95 -0
  23. package/dist/boot/memory-consolidation.d.ts +185 -9
  24. package/dist/boot/memory-consolidation.js +192 -32
  25. package/dist/boot/resolve-spec.d.ts +2 -0
  26. package/dist/boot/resolve-spec.js +28 -24
  27. package/dist/boot/runner-deps.d.ts +6 -2
  28. package/dist/boot/runner-deps.js +4 -0
  29. package/dist/boot/session-faces.d.ts +5 -0
  30. package/dist/boot/session-faces.js +18 -2
  31. package/dist/boot/session-incarnation-seat.d.ts +41 -0
  32. package/dist/boot/session-incarnation-seat.js +69 -0
  33. package/dist/boot/stage-01-config.js +2 -2
  34. package/dist/boot/stage-02-registry-pull.js +3 -1
  35. package/dist/boot/stage-04-stores.d.ts +2 -0
  36. package/dist/boot/stage-04-stores.js +3 -1
  37. package/dist/boot/stage-05-execution-env.d.ts +4 -0
  38. package/dist/boot/stage-05-execution-env.js +2 -2
  39. package/dist/boot/stage-06-runners.d.ts +4 -1
  40. package/dist/boot/stage-06-runners.js +7 -14
  41. package/dist/boot/stage-07-capability-layer.d.ts +4 -1
  42. package/dist/boot/stage-08-reapers.d.ts +4 -1
  43. package/dist/boot/stage-09-leader.d.ts +6 -1
  44. package/dist/boot/stage-09-leader.js +2 -1
  45. package/dist/boot/stage-10-http-server.d.ts +6 -1
  46. package/dist/boot/stage-10-http-server.js +4 -1
  47. package/dist/brain.d.ts +1 -1
  48. package/dist/config-catalog.d.ts +16 -1
  49. package/dist/config-catalog.js +9 -9
  50. package/dist/config-center/restart-signal.d.ts +4 -4
  51. package/dist/config-invariants.d.ts +7 -1
  52. package/dist/config-invariants.js +37 -6
  53. package/dist/config-provider.d.ts +42 -10
  54. package/dist/config-provider.js +18 -16
  55. package/dist/config-types.d.ts +37 -16
  56. package/dist/config-types.js +1 -0
  57. package/dist/config.d.ts +13 -28
  58. package/dist/config.js +53 -70
  59. package/dist/env-facts.d.ts +13 -3
  60. package/dist/env-facts.js +15 -3
  61. package/dist/execution-lane-caps.d.ts +15 -0
  62. package/dist/execution-lane-caps.js +8 -0
  63. package/dist/file-history-capture.d.ts +42 -35
  64. package/dist/file-history-capture.js +15 -10
  65. package/dist/host-lsp-manager.d.ts +1 -1
  66. package/dist/http/admission.d.ts +11 -4
  67. package/dist/http/admission.js +5 -1
  68. package/dist/http/dispatch.js +3 -3
  69. package/dist/http/principal-gate.d.ts +22 -0
  70. package/dist/http/principal-gate.js +20 -0
  71. package/dist/http/resume-legs.js +9 -5
  72. package/dist/http/routes/agents-roster.d.ts +7 -0
  73. package/dist/http/routes/agents-roster.js +6 -2
  74. package/dist/http/routes/approvals-assistant.d.ts +48 -19
  75. package/dist/http/routes/approvals-assistant.js +117 -50
  76. package/dist/http/routes/capabilities.js +3 -2
  77. package/dist/http/routes/config-catalog.js +1 -0
  78. package/dist/http/routes/memory-consolidation.d.ts +3 -2
  79. package/dist/http/routes/memory-policy.js +3 -3
  80. package/dist/http/routes/rules.js +2 -2
  81. package/dist/http/routes/runs.js +27 -2
  82. package/dist/http/routes/session-sync.js +8 -14
  83. package/dist/http/routes/sessions-list.js +3 -2
  84. package/dist/http/routes/sessions.js +44 -23
  85. package/dist/http/routes/shared-memory.js +2 -2
  86. package/dist/http/routes/tasks.js +22 -19
  87. package/dist/http/routes/trace-usage.js +4 -4
  88. package/dist/http/send.d.ts +12 -0
  89. package/dist/http/send.js +12 -1
  90. package/dist/http/server.d.ts +21 -4
  91. package/dist/http/server.js +7 -4
  92. package/dist/http/sync-submit-body.d.ts +43 -0
  93. package/dist/http/sync-submit-body.js +4 -0
  94. package/dist/http/wire-types.d.ts +23 -5
  95. package/dist/index.d.ts +1 -0
  96. package/dist/index.js +1 -0
  97. package/dist/leader/merge.d.ts +8 -3
  98. package/dist/memory-incident-sink.js +2 -1
  99. package/dist/model-select.d.ts +9 -0
  100. package/dist/model-select.js +5 -1
  101. package/dist/observability/corrupt-read-seat.d.ts +151 -16
  102. package/dist/observability/corrupt-read-seat.js +158 -15
  103. package/dist/observability/fail-open.d.ts +12 -0
  104. package/dist/observability/fail-open.js +12 -0
  105. package/dist/observability/run-terminal.d.ts +15 -1
  106. package/dist/observability/run-terminal.js +4 -0
  107. package/dist/parked-decide.d.ts +8 -3
  108. package/dist/parked-decide.js +56 -20
  109. package/dist/parked-revive-surface.d.ts +14 -7
  110. package/dist/parked-revive-surface.js +5 -1
  111. package/dist/plugins/approval-ask-store-file.d.ts +4 -1
  112. package/dist/plugins/approval-ask-store-file.js +9 -12
  113. package/dist/plugins/approval-ask-store-sql.d.ts +4 -0
  114. package/dist/plugins/approval-ask-store-sql.js +4 -1
  115. package/dist/plugins/approval-exemption-store.d.ts +21 -1
  116. package/dist/plugins/approval-exemption-store.js +36 -7
  117. package/dist/plugins/approval-nonce-store.d.ts +16 -2
  118. package/dist/plugins/approval-nonce-store.js +17 -7
  119. package/dist/plugins/caching-session-store.d.ts +5 -0
  120. package/dist/plugins/caching-session-store.js +3 -0
  121. package/dist/plugins/file-resume-anchor-store.d.ts +7 -2
  122. package/dist/plugins/file-resume-anchor-store.js +19 -5
  123. package/dist/plugins/file-run-store.d.ts +21 -8
  124. package/dist/plugins/file-run-store.js +60 -38
  125. package/dist/plugins/local-session-store.d.ts +22 -0
  126. package/dist/plugins/local-session-store.js +74 -11
  127. package/dist/plugins/memory-run-store.d.ts +7 -5
  128. package/dist/plugins/memory-run-store.js +11 -4
  129. package/dist/plugins/one-time-migrations.d.ts +38 -1
  130. package/dist/plugins/one-time-migrations.js +143 -15
  131. package/dist/plugins/permission-rule-store-file.d.ts +9 -2
  132. package/dist/plugins/permission-rule-store-file.js +37 -51
  133. package/dist/plugins/pg-session-storage.d.ts +3 -0
  134. package/dist/plugins/pg-session-storage.js +19 -10
  135. package/dist/plugins/run-store-sql.d.ts +12 -4
  136. package/dist/plugins/run-store-sql.js +15 -13
  137. package/dist/plugins/send-file-ledger.d.ts +5 -1
  138. package/dist/plugins/send-file-ledger.js +11 -6
  139. package/dist/plugins/session-event-payload.d.ts +14 -4
  140. package/dist/plugins/session-event-payload.js +5 -2
  141. package/dist/plugins/store-backend.d.ts +7 -3
  142. package/dist/plugins/store-backend.js +10 -6
  143. package/dist/plugins/store-contracts.d.ts +7 -2
  144. package/dist/plugins/tidb-pool.js +8 -2
  145. package/dist/plugins/tidb-session-store.d.ts +6 -1
  146. package/dist/plugins/tidb-session-store.js +17 -10
  147. package/dist/project-identity.d.ts +4 -6
  148. package/dist/request-key-closure.js +1 -1
  149. package/dist/request-value-closure.d.ts +72 -3
  150. package/dist/request-value-closure.js +12 -4
  151. package/dist/roster-bound-http-status.d.ts +6 -0
  152. package/dist/roster-bound-http-status.js +6 -0
  153. package/dist/run-local.js +22 -27
  154. package/dist/runs.d.ts +22 -2
  155. package/dist/runs.js +29 -7
  156. package/dist/sealed-key.d.ts +31 -4
  157. package/dist/sealed-key.js +9 -0
  158. package/dist/security.d.ts +5 -1
  159. package/dist/server-secret-env.d.ts +2 -2
  160. package/dist/session-children-retire.d.ts +92 -0
  161. package/dist/session-children-retire.js +85 -0
  162. package/dist/session-error-wire.d.ts +58 -0
  163. package/dist/session-error-wire.js +38 -0
  164. package/dist/session-incarnation.d.ts +61 -0
  165. package/dist/session-incarnation.js +43 -0
  166. package/dist/session-records-skipped-notice.d.ts +20 -0
  167. package/dist/session-records-skipped-notice.js +27 -0
  168. package/dist/tool-approval.d.ts +65 -0
  169. package/dist/tool-approval.js +36 -9
  170. package/dist/trace/core-keyset-guard.d.ts +8 -7
  171. package/dist/trace/engine-notice-wire.d.ts +3 -1
  172. package/dist/trace/engine-notice-wire.js +2 -0
  173. package/dist/trace/project.d.ts +1 -1
  174. package/dist/trace/project.js +9 -0
  175. package/dist/trace/wire-projection-faces.d.ts +10 -3
  176. package/dist/trace/wire-projection-faces.js +8 -2
  177. package/package.json +3 -3
@@ -0,0 +1,61 @@
1
+ /** 会话元数据的一次读:属主 + 化身(同一行、同一次读)。 */
2
+ export interface SessionFace {
3
+ /** `null` = 登记为无主的会话。 */
4
+ readonly owner: string | null;
5
+ /** 这一位住客的化身令牌(非空;空值在读口就响亮拒了)。 */
6
+ readonly incarnation: string;
7
+ }
8
+ /** 读口的形(三只会话店 + 缓存包装层同一个签名)。 */
9
+ export type SessionFaceReader = (sessionId: string) => Promise<SessionFace | undefined>;
10
+ /** 铸一只新化身(铸点见头注,只在新建行)。 */
11
+ export declare function mintSessionIncarnation(): string;
12
+ /** 日志里点名一只化身时唯一允许的形:前 8 位。完整令牌不进任何日志正文。 */
13
+ export declare function incarnationLogPrefix(incarnation: string): string;
14
+ /**
15
+ * 把一段**不归本仓撰写**的文本(典型:core 记忆引擎的事故披露句 —— `memory.session_incarnation_mismatch` 逐字带着两只令牌)落日志之前,
16
+ * 把其中每一只化身形的串截成 {@link incarnationLogPrefix}(前 8 位 + `…`)。规则只有一条:完整令牌不进日志正文;前 8 位留着对账。
17
+ */
18
+ export declare function redactIncarnations(text: string): string;
19
+ /**
20
+ * 两次读是不是**同一位住客**:都在、化身逐字相等。任一次读不到(会话不在)⇒ 不是 —— 没有「读不到就当相同」的臂。
21
+ * 属主是否等于某个身份是调用方自己的判据(赎回面比的是卡的权威 principal),不在这里。
22
+ */
23
+ export declare function sameSessionOccupant(before: SessionFace | undefined, after: SessionFace | undefined): boolean;
24
+ /**
25
+ * 会话行在、化身却是空的 —— 响亮拒(头注「读到空化身」一条)。具名类型:调用方按类型认它,不按文案。
26
+ * 文案不带任何令牌(本来就是空的),只点名会话 id 与出路。
27
+ */
28
+ export declare class SessionIncarnationMissingError extends Error {
29
+ readonly sessionId: string;
30
+ readonly code = "session.incarnation_missing";
31
+ constructor(sessionId: string);
32
+ }
33
+ /**
34
+ * 店把一行原始列读成 {@link SessionFace} 的唯一口(三只店共用 —— 空值判据只写这一处)。
35
+ * `incarnation` 不是非空字符串 ⇒ {@link SessionIncarnationMissingError}。
36
+ */
37
+ export declare function sessionFaceFromRow(sessionId: string, owner: string | null, incarnation: unknown): SessionFace;
38
+ /**
39
+ * session-sync **整段换装**(replace)时化身的去留 —— 三只店共用的一句判据:换装删掉再插回的是**同一位住客**的那一行
40
+ * (删除带属主门),所以行在、属主相同、化身非空 ⇒ 沿用;行不在(这是一次新建)或还是空的存量形 ⇒ 铸一只新的。
41
+ */
42
+ export declare function carriedIncarnation(prior: {
43
+ owner?: unknown;
44
+ incarnation?: unknown;
45
+ } | undefined, owner: string | null): string;
46
+ /**
47
+ * `session_meta.incarnation` 的列定义 —— 两方言各一句,**唯一属主**:中央 DDL(`tidb-pool.ts` / `pg-session-storage.ts` 的
48
+ * `CREATE TABLE session_meta`)与一次性迁移 M5 的 `ADD COLUMN`(`plugins/one-time-migrations.ts`)都内插这同一串,
49
+ * 于是「新库建出来的列」与「存量库补上的列」逐字同形(S-547 起服列册探针比的是列名,形由这里保证)。
50
+ *
51
+ * `NOT NULL DEFAULT ''`:空串就是「没铸过」的唯一表示 —— 读口对它响亮拒({@link SessionIncarnationMissingError})。
52
+ * 缺省值只为两件事存在:①存量表 `ADD COLUMN` 在两方言上一句成立(PG 对非空表加无缺省的 NOT NULL 列直接拒);
53
+ * ②一条漏铸的写腿落下的是**读得出来的错**(读口拒),而不是一条插入期的 500 —— 两形都 fail-closed,前者还能被迁移补上。
54
+ * 宽度 64:`randomUUID()` 是 36 字符,留出余量但不给任意长串留位。
55
+ */
56
+ export declare const SESSION_META_INCARNATION_COLUMN: {
57
+ readonly name: "incarnation";
58
+ readonly tidb: "incarnation VARCHAR(64) NOT NULL DEFAULT ''";
59
+ readonly pg: "incarnation VARCHAR(64) COLLATE \"C\" NOT NULL DEFAULT ''";
60
+ };
61
+ //# sourceMappingURL=session-incarnation.d.ts.map
@@ -0,0 +1,43 @@
1
+ import { randomUUID } from "node:crypto";
2
+ export function mintSessionIncarnation() {
3
+ return randomUUID();
4
+ }
5
+ export function incarnationLogPrefix(incarnation) {
6
+ return incarnation.slice(0, 8);
7
+ }
8
+ const INCARNATION_TOKEN_SHAPE = /[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi;
9
+ export function redactIncarnations(text) {
10
+ return text.replace(INCARNATION_TOKEN_SHAPE, (token) => `${incarnationLogPrefix(token)}…`);
11
+ }
12
+ export function sameSessionOccupant(before, after) {
13
+ return before !== undefined && after !== undefined && before.incarnation === after.incarnation;
14
+ }
15
+ export class SessionIncarnationMissingError extends Error {
16
+ sessionId;
17
+ code = "session.incarnation_missing";
18
+ constructor(sessionId) {
19
+ super(`session ${sessionId} has no incarnation token: every session row is minted one when it is created, and rows written before ` +
20
+ `7.104.0 get one from the one-time migration that runs at boot (plugins/one-time-migrations.ts, entry M5). An empty value ` +
21
+ `after that means a write path that did not mint one, or a hand-edited row — it is refused rather than read as "matches anyone". ` +
22
+ `Restart a 7.104.0+ replica so the migration backfills it, or delete the session.`);
23
+ this.sessionId = sessionId;
24
+ this.name = "SessionIncarnationMissingError";
25
+ }
26
+ }
27
+ export function sessionFaceFromRow(sessionId, owner, incarnation) {
28
+ if (typeof incarnation !== "string" || incarnation === "")
29
+ throw new SessionIncarnationMissingError(sessionId);
30
+ return { owner, incarnation };
31
+ }
32
+ export function carriedIncarnation(prior, owner) {
33
+ if (prior !== undefined && (prior.owner ?? null) === owner && typeof prior.incarnation === "string" && prior.incarnation !== "") {
34
+ return prior.incarnation;
35
+ }
36
+ return mintSessionIncarnation();
37
+ }
38
+ export const SESSION_META_INCARNATION_COLUMN = {
39
+ name: "incarnation",
40
+ tidb: "incarnation VARCHAR(64) NOT NULL DEFAULT ''",
41
+ pg: `incarnation VARCHAR(64) COLLATE "C" NOT NULL DEFAULT ''`,
42
+ };
43
+ //# sourceMappingURL=session-incarnation.js.map
@@ -0,0 +1,20 @@
1
+ import type { FileStorageCorruptReadInfo, SessionRepo } from "@sema-agent/core";
2
+ import { type EngineNoticeRouter, type ServerNoticeCode } from "./trace/engine-notice-wire.js";
3
+ export declare const SESSION_RECORDS_SKIPPED: ServerNoticeCode;
4
+ /**
5
+ * 披露座上的「作用域在场则记」腿(装配点 = `plugins/store-backend.ts` 里喂给 `LocalSessionStore` 的那只
6
+ * `FileSessionRepo` 的 `onCorruptRead`)。只认 `kind:"replay"` 这一级(`record` 由它汇总,`file` 没有会话归属)。
7
+ * 作用域外 ⇒ 什么都不做(那一次披露照旧只进运维日志)。纯内存赋值,不抛。
8
+ */
9
+ export declare function noteReplayInSessionOpenScope(info: FileStorageCorruptReadInfo): void;
10
+ /** 一次打开的产物(core `SessionRepo.open` 的返回型,派生不手抄)。 */
11
+ type OpenedSession = Awaited<ReturnType<SessionRepo["open"]>>;
12
+ /**
13
+ * **唯一铸点**:为 `sessionId` 打开它自己的存档(`open` = 那一次 `repo.open(...)`),打开成功且这次回放跳过了记录 ⇒
14
+ * 数出幸存条目、铸一帧投出。打开失败原样上抛,不铸。
15
+ *
16
+ * 计数与投递都不抛(`getEntries()` 是存储内存条目的拷贝;`route()` 按口隔离、口的失败走 F 类留痕),所以这里没有降级臂。
17
+ */
18
+ export declare function openSessionForItself(sessionId: string, open: () => Promise<OpenedSession>, router?: EngineNoticeRouter): Promise<OpenedSession>;
19
+ export {};
20
+ //# sourceMappingURL=session-records-skipped-notice.d.ts.map
@@ -0,0 +1,27 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { defaultEngineNoticeRouter } from "./trace/engine-notice-wire.js";
3
+ export const SESSION_RECORDS_SKIPPED = "session.records_skipped";
4
+ const openScope = new AsyncLocalStorage();
5
+ export function noteReplayInSessionOpenScope(info) {
6
+ if (info.kind !== "replay")
7
+ return;
8
+ const scope = openScope.getStore();
9
+ if (scope !== undefined)
10
+ scope.dropped = info.dropped;
11
+ }
12
+ export async function openSessionForItself(sessionId, open, router = defaultEngineNoticeRouter) {
13
+ const scope = { sessionId };
14
+ const session = await openScope.run(scope, open);
15
+ const dropped = scope.dropped;
16
+ if (dropped !== undefined && dropped > 0) {
17
+ const entriesKept = (await session.getEntries()).length;
18
+ router.route({
19
+ code: SESSION_RECORDS_SKIPPED,
20
+ message: `Part of this session's saved history could not be read: ${dropped} record(s) were skipped when it was reopened, and this turn continues from the ${entriesKept} entries that remain.`,
21
+ detail: { dropped, entriesKept, sessionId },
22
+ sessionId,
23
+ });
24
+ }
25
+ return session;
26
+ }
27
+ //# sourceMappingURL=session-records-skipped-notice.js.map
@@ -717,7 +717,67 @@ export declare const PERSIST_RULE_BATCH_EXCLUSIVE_ERROR = "persistRule.batchOffe
717
717
  * `persistRule.rule` / `persistRule.edited` 三道既有形门**同族**(那三道在,只有本格没有 = 本格才是特例)。
718
718
  */
719
719
  export declare const SETTLED_BY_MUST_BE_STRING = "settledBy must be a string \u2014 the settlement source is one word from the engine's closed set, and this layer judges only the SHAPE (which word it is, the engine decides)";
720
+ /**
721
+ * #1016 / S-558 —— **live 座的那一次边界 cast,全仓只此一处**(耐久座的那一次在 `hostDecisionFacts` 里,
722
+ * 一座一次)。它说的是「我把一个**未判词**的串放到只收词的座上」:判词的是 core —— `resolveAsk` 对词表外
723
+ * 是 `approver_contract` 的响亮 fail-closed deny,不是静默当人拒(实测三形同判,特征钉在
724
+ * `test/tool-approval.test.ts` 的 #1016 组)。本层判过的只有**形**(是个串、只与 deny 同行,见
725
+ * {@link SETTLED_BY_MUST_BE_STRING} / {@link SETTLED_BY_DENY_ONLY}),而形不是词。
726
+ *
727
+ * 两个座位读它:这次回决的**即时结算**(`entry.settle` 的自报参)与**持久行**(`decideAsk` 的
728
+ * `settledBy` 列)。S-558 之前只有前者 —— 于是同一个事实在「当次」与「重放」上是两个答案。
729
+ * S-843:耐久 ask 回决口(`POST /v1/tasks/:taskId/asks/:askId/decision`)写的是**同一个座**(同一列,经
730
+ * `outcomeOfDecidedRow` 交给同一个 `AskOutcome.settledBy`),所以它也走这一只 —— cast 仍是一座一次。
731
+ */
732
+ export declare function respondSettledBy(parsed: Pick<ParsedApprovalResponse, "settledBy">): HostSettledBy | undefined;
720
733
  export declare const SETTLED_BY_DENY_ONLY = "settledBy is only meaningful with decision \"deny\" \u2014 only a person approves an action that EXECUTES, so a self-reported settlement source on an allow is a contradiction the engine refuses; the refusal would arrive too late to undo this door's own side effects (a session allow-all grant, a persisted rule, a DECIDED row that replays as a plain approval), so it is refused here, before the decision";
734
+ /**
735
+ * S-843 / 车QK F7 —— `settledBy` 的**宽**拒句(三条回决腿共用一份文本,与 {@link SETTLED_BY_DENY_ONLY} 同姿势)。句子逐字是 S-843 在
736
+ * 耐久 ask 回决口路由里写下的那一句(车QK F7 起搬到判官旁,路由不再自己写),尺 = 店的同一个常量({@link MAX_SETTLED_BY_CHARS})。
737
+ */
738
+ export declare const SETTLED_BY_TOO_WIDE = "settledBy must be 1-16 characters \u2014 the settlement source is one word from the engine's closed set and lands in a fixed-width column on the decision row";
739
+ /**
740
+ * S-843 —— 决议体上 `settledBy` 的判据,**一处成形**。三条 HTTP 回决腿共读:
741
+ * `POST /v1/approvals/:sessionId/decide`(耐久 park 腿)、`POST /v1/tasks/:taskId/asks/:askId/decision`(耐久 ask 腿)与
742
+ * live `POST /v1/tool-approvals/:id/respond`(经 {@link parseToolApprovalResponse};车QK F7 起 —— 此前那只 parser 手写了 ②③ 两臂、
743
+ * 没有 ④,是这条判据的第二个写者)。
744
+ *
745
+ * 次序即契约(①–③ = decide 腿修前那三道 if 的原序,逐字搬来,不是新判据):
746
+ * ① 直连门在场 ⇒ `not_in_proof` —— 键不在决策证明的签名载荷里(§6.3 同族第四条,理由在 approvals-assistant 的
747
+ * `DECIDE_NOT_IN_PROOF_KEYS` 注);
748
+ * ② 不是 deny ⇒ `deny_only` —— {@link SETTLED_BY_DENY_ONLY};
749
+ * ③ 不是串 ⇒ `not_string` —— {@link SETTLED_BY_MUST_BE_STRING};
750
+ * ④ 长度不在 1..{@link MAX_SETTLED_BY_CHARS} ⇒ `too_wide` —— {@link SETTLED_BY_TOO_WIDE}(车QK F7 / A3,7.104.0 合并复审)。
751
+ * 尺与店门口的形门同一只谓词({@link settledByFitsColumn}):`/respond` 与耐久 ask 口写的是**同一列**
752
+ * (`approval_ask.settled_by`,`VARCHAR(16)`),列宽是列的事实,不是某一条腿的事实。修前这一道只长在耐久 ask 口的路由里:
753
+ * `/respond` 上一只超宽 / 空串的来源位要到 CAS 里才被店门抛,落进 D5「店抖动不挡真实回决」臂 —— 行没决,决议照样交给了引擎,200。
754
+ * `/decide` 的来源位不落这一列(交给 core 的 checkpoint,core 在 CAS 之前按词表拒),这一道对它同样成立:core 的闭集词
755
+ * (`human` / `timeout` / `policy`)都远短于这把尺,尺外的串不可能是一个词 ⇒ 三条口对同一个体同判同句,不为一条腿开特例。
756
+ * 判的是**键在不在 / 配不配这个决议 / 形**,**从不判词**(词表属主是 core;三条腿的词都由引擎响亮拒)。
757
+ *
758
+ * 返回判决而不是直接回 400:各条腿在调用点按自己的码约定发出(错误文案冻结门按字面站点采集,码是 wire 契约);
759
+ * 调用点对判决做穷举 `switch` + `never` 兜底(加一臂 = 编译红,[ref])。`accepted` 臂带着已证形的串 —— 调用点拿它往下传,
760
+ * 不再对 `body.settledBy` 做第二次窄化。第三参给字面 `false` 的调用点(live `/respond`:那条口没有直连门证明)拿到的判决型里
761
+ * **没有** `not_in_proof` 臂(重载),不必为一条结构上不可达的臂写兜底。
762
+ */
763
+ export type SettledByBodyVerdict = {
764
+ kind: "absent";
765
+ } | {
766
+ kind: "accepted";
767
+ settledBy: string;
768
+ } | {
769
+ kind: "not_in_proof";
770
+ } | {
771
+ kind: "deny_only";
772
+ } | {
773
+ kind: "not_string";
774
+ } | {
775
+ kind: "too_wide";
776
+ };
777
+ export declare function judgeSettledByBody(settledBy: unknown, decision: string, directDoorActive: false): Exclude<SettledByBodyVerdict, {
778
+ kind: "not_in_proof";
779
+ }>;
780
+ export declare function judgeSettledByBody(settledBy: unknown, decision: string, directDoorActive: boolean): SettledByBodyVerdict;
721
781
  /** S-720(core 7.32.0 #1093)—— `readRootGrant` 只随 `decision:"allow"`(CC 卡的第三选项「Yes, and add directory」= 放行这一次
722
782
  * + 加目录;`allow_session` 不是那一个选项,`deny` 上它没有意义)。裁决之前响亮拒,不静默丢。 */
723
783
  export declare const READ_ROOT_GRANT_ALLOW_ONLY = "readRootGrant is only meaningful with decision \"allow\" \u2014 it is the card's \"Yes, and add directory\" answer (allow this call and add the card's readRootCandidate.dir to this run's read directories)";
@@ -737,6 +797,11 @@ export declare function parseToolApprovalResponse(body: unknown): {
737
797
  ok: false;
738
798
  error: string;
739
799
  };
800
+ /** [ref] 车2:`parseToolApprovalResponse` 的成功分支形——`respond()` 拆分出的私有收尾方法(`finishRespond`/
801
+ * `respondWithCas`)复用,避免裸内联匿名类型重复三处。 */
802
+ type ParsedApprovalResponse = Extract<ReturnType<typeof parseToolApprovalResponse>, {
803
+ ok: true;
804
+ }>;
740
805
  /**
741
806
  * #1016 —— wire 上**开给调用方**的那一个词,从 core 的两只闭集里取**交集**。
742
807
  *
@@ -9,7 +9,7 @@ import { createLogger } from "./observability/logger.js";
9
9
  import { recordFailOpen } from "./observability/fail-open.js";
10
10
  import { deriveAskId, deriveAskScopedBatchId, deriveBatchId, MAX_DECISION_NOTE_CHARS } from "./approval-ask-machine.js";
11
11
  import { ApprovalCardEnvelopeSchema, MAX_RULE_OFFERS, buildApprovalCard, buildApprovalCardEnvelope, buildApprovalRequestFrame, buildRevokeFrame, readDenialLimitFallback, readProbeCause, readReadRootCandidate, readRuleEvidence, readRuleOffersAbsence, readRuleOffersAbsenceSeat, readParkRowTrueSeat, readClosedWordSeat, RuleOfferRawSchema, backfillLegacyBatchMemberKind, screenUncoveredDetailSeat, } from "./approval-card.js";
12
- import { POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth } from "./plugins/approval-ask-store-sql.js";
12
+ import { MAX_SETTLED_BY_CHARS, POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth, settledByFitsColumn } from "./plugins/approval-ask-store-sql.js";
13
13
  import { settleByPolicy } from "./approval-policy-settlement.js";
14
14
  import { governanceAskMarksFor, runWithGovernanceAskScope } from "./governance-ask-marks.js";
15
15
  import { governanceOriginOf } from "./runtime-governance.js";
@@ -107,10 +107,24 @@ export const PERSIST_RULE_EDITED_FLAG_ERROR = "persistRule.edited must be a bool
107
107
  export const PERSIST_RULE_BATCH_INDEX_ERROR = "persistRule.batchOfferIndex must be a non-negative integer when present";
108
108
  export const PERSIST_RULE_BATCH_EXCLUSIVE_ERROR = "persistRule.batchOfferIndex is mutually exclusive with persistRule.rule / persistRule.edited — send exactly one arm";
109
109
  export const SETTLED_BY_MUST_BE_STRING = "settledBy must be a string — the settlement source is one word from the engine's closed set, and this layer judges only the SHAPE (which word it is, the engine decides)";
110
- function respondSettledBy(parsed) {
110
+ export function respondSettledBy(parsed) {
111
111
  return parsed.settledBy;
112
112
  }
113
113
  export const SETTLED_BY_DENY_ONLY = 'settledBy is only meaningful with decision "deny" — only a person approves an action that EXECUTES, so a self-reported settlement source on an allow is a contradiction the engine refuses; the refusal would arrive too late to undo this door\'s own side effects (a session allow-all grant, a persisted rule, a DECIDED row that replays as a plain approval), so it is refused here, before the decision';
114
+ export const SETTLED_BY_TOO_WIDE = `settledBy must be 1-${MAX_SETTLED_BY_CHARS} characters — the settlement source is one word from the engine's closed set and lands in a fixed-width column on the decision row`;
115
+ export function judgeSettledByBody(settledBy, decision, directDoorActive) {
116
+ if (settledBy === undefined)
117
+ return { kind: "absent" };
118
+ if (directDoorActive)
119
+ return { kind: "not_in_proof" };
120
+ if (decision !== "deny")
121
+ return { kind: "deny_only" };
122
+ if (typeof settledBy !== "string")
123
+ return { kind: "not_string" };
124
+ if (!settledByFitsColumn(settledBy))
125
+ return { kind: "too_wide" };
126
+ return { kind: "accepted", settledBy };
127
+ }
114
128
  export const READ_ROOT_GRANT_ALLOW_ONLY = 'readRootGrant is only meaningful with decision "allow" — it is the card\'s "Yes, and add directory" answer (allow this call and add the card\'s readRootCandidate.dir to this run\'s read directories)';
115
129
  export const READ_ROOT_GRANT_SHAPE = `readRootGrant must be the card's readRootCandidate.dir sent back byte for byte — a non-empty string of at most ${READ_ROOT_CANDIDATE_DIR_MAX} characters`;
116
130
  export const PERSIST_RULE_SCOPE_REFUSED = "this endpoint does not accept a rule scope — the server mints it from where the approval happened (send neither `scope` nor `persistRule.scope`)";
@@ -157,12 +171,25 @@ export function parseToolApprovalResponse(body) {
157
171
  }
158
172
  note = rawNote;
159
173
  }
160
- const rawSettledBy = body.settledBy;
161
- if (rawSettledBy !== undefined && d !== "deny")
162
- return { ok: false, error: SETTLED_BY_DENY_ONLY };
163
- if (rawSettledBy !== undefined && typeof rawSettledBy !== "string")
164
- return { ok: false, error: SETTLED_BY_MUST_BE_STRING };
165
- const settledBy = rawSettledBy;
174
+ const settledByVerdict = judgeSettledByBody(body.settledBy, d, false);
175
+ let settledBy;
176
+ switch (settledByVerdict.kind) {
177
+ case "deny_only":
178
+ return { ok: false, error: SETTLED_BY_DENY_ONLY };
179
+ case "not_string":
180
+ return { ok: false, error: SETTLED_BY_MUST_BE_STRING };
181
+ case "too_wide":
182
+ return { ok: false, error: SETTLED_BY_TOO_WIDE };
183
+ case "absent":
184
+ break;
185
+ case "accepted":
186
+ settledBy = settledByVerdict.settledBy;
187
+ break;
188
+ default: {
189
+ const unreachable = settledByVerdict;
190
+ throw new Error(`unhandled settledBy verdict: ${JSON.stringify(unreachable)}`);
191
+ }
192
+ }
166
193
  const rawGrant = body.readRootGrant;
167
194
  if (rawGrant !== undefined && d !== "allow")
168
195
  return { ok: false, error: READ_ROOT_GRANT_ALLOW_ONLY };
@@ -175,7 +202,7 @@ export function parseToolApprovalResponse(body) {
175
202
  ...(d !== "deny" && u !== undefined ? { updatedInput: u } : {}),
176
203
  ...(persistRule !== undefined ? { persistRule } : {}),
177
204
  ...(note !== undefined ? { note } : {}),
178
- ...(rawSettledBy !== undefined ? { settledBy } : {}),
205
+ ...(settledBy !== undefined ? { settledBy } : {}),
179
206
  ...(readRootGrant !== undefined ? { readRootGrant } : {}),
180
207
  };
181
208
  }
@@ -59,8 +59,8 @@ type _GuardMailboxReverse = AssertAllKeysHandled<Exclude<MailboxProjected, keyof
59
59
  type MailboxAppendProjected = "from" | "content" | "sentAt" | "hopChain" | "peerMeta";
60
60
  type _GuardMailboxAppend = AssertAllKeysHandled<Exclude<keyof MailboxAppendMessage, MailboxAppendProjected>>;
61
61
  type _GuardMailboxAppendReverse = AssertAllKeysHandled<Exclude<MailboxAppendProjected, keyof MailboxAppendMessage>>;
62
- type WiringSectionProjected = "schemaVersion" | "leg" | "ask" | "question" | "interaction" | "elicit" | "parkLane" | "session" | "fleet" | "permissionRules" | "modelGate" | "autoMode" | "mcp" | "tools" | "hooks" | "lsp" | "hands" | "writeProtection" | "teamRole";
63
- type WiringSectionExcluded = "governance" | "configFingerprint" | "autoConsolidation" | "readDeny" | "peerPresence" | "fileHistory";
62
+ type WiringSectionProjected = "schemaVersion" | "leg" | "ask" | "question" | "interaction" | "elicit" | "parkLane" | "session" | "fleet" | "permissionRules" | "modelGate" | "autoMode" | "mcp" | "tools" | "hooks" | "lsp" | "hands" | "writeProtection" | "teamRole" | "fileHistory";
63
+ type WiringSectionExcluded = (typeof import("./wire-projection-faces.js").WIRE_PROJECTION_FACES)["wiring_manifest_tenant"]["excluded"][number];
64
64
  type _GuardWiringSections = AssertAllKeysHandled<Exclude<keyof WiringManifest, WiringSectionProjected | WiringSectionExcluded>>;
65
65
  type _GuardWiringSectionsReverse = AssertAllKeysHandled<Exclude<WiringSectionProjected | WiringSectionExcluded, keyof WiringManifest>>;
66
66
  type WiringSection<K extends keyof WiringManifest> = NonNullable<WiringManifest[K]>;
@@ -145,8 +145,8 @@ type ParkArmQueueExcluded = "kind" | "args" | "batchToolCallIds" | "completedCal
145
145
  type _GuardParkArmQueue = AssertAllKeysHandled<Exclude<ParkArmKeys, ParkArmQueueProjected | ParkArmQueueExcluded>>;
146
146
  type _GuardParkArmQueueReverse = AssertAllKeysHandled<Exclude<ParkArmQueueProjected | ParkArmQueueExcluded, ParkArmKeys>>;
147
147
  type _NoDoubleBookParkArmQueue = AssertAllKeysHandled<Extract<ParkArmQueueProjected, ParkArmQueueExcluded>>;
148
- type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "writeFace" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "approverPosture" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends" | "autoModeRequested" | "skillListingBudgetFraction" | "skillListingMaxDescChars" | "excludeAllTools";
149
- type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence" | "fileHistory";
148
+ type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "writeFace" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "approverPosture" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends" | "autoModeRequested" | "skillListingBudgetFraction" | "skillListingMaxDescChars" | "excludeAllTools" | "fileHistory";
149
+ type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars" | "peerPresence";
150
150
  type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
151
151
  type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
152
152
  type TaskResultForwarded = "taskId" | "runId" | "sessionId" | "terminal" | "model" | "effectiveReasoning" | "result" | "salvagedOutput" | "remoteEnvFailures" | "apiFailure" | "retryAfterMs" | "degraded" | "structuredOutput" | "rewindNotes" | "haltedOnUserRejection" | "haltedByUser" | "strandedHumanAnswers" | "pendingApproval" | "approvalWaitedMs" | "effectiveReadFace" | "effectiveReadDenyPatterns" | "effectiveWriteFace" | "effectiveMemoryScopes" | "stats" | "editedFiles" | "responseModel" | "compaction";
@@ -213,8 +213,9 @@ type LspOptionsUnwired = "onNotice" | "servers" | "spawn" | "readText" | "resolv
213
213
  type _GuardLspOptions = AssertAllKeysHandled<Exclude<keyof NodeLspManagerOptions, LspOptionsWired | LspOptionsUnwired>>;
214
214
  type _GuardLspOptionsReverse = AssertAllKeysHandled<Exclude<LspOptionsWired | LspOptionsUnwired, keyof NodeLspManagerOptions>>;
215
215
  type _NoDoubleBookLspOptions = AssertAllKeysHandled<Extract<LspOptionsWired, LspOptionsUnwired>>;
216
- type RunnerDepsDroppedPendingS724 = "fileHistoryCapture";
217
- type _GuardFacelessRunnerDepsReverse = AssertAllKeysHandled<Exclude<RunnerDepsDroppedPendingS724, keyof RunnerDeps>>;
216
+ /** ⑯ 账上记过、已接线的 `RunnerDeps` 席(S-724 起 `fileHistoryCapture`)。反向钉:core 改名 / 退役 ⇒ 红。 */
217
+ type RunnerDepsLedgerWired = "fileHistoryCapture";
218
+ type _GuardLedgerRunnerDepsReverse = AssertAllKeysHandled<Exclude<RunnerDepsLedgerWired, keyof RunnerDeps>>;
218
219
  export type CoreKeysetGuards = [
219
220
  _GuardNotification,
220
221
  _GuardNotificationReverse,
@@ -321,7 +322,7 @@ export type CoreKeysetGuards = [
321
322
  _GuardLspOptions,
322
323
  _NoDoubleBookLspOptions,
323
324
  _GuardLspOptionsReverse,
324
- _GuardFacelessRunnerDepsReverse
325
+ _GuardLedgerRunnerDepsReverse
325
326
  ];
326
327
  export {};
327
328
  //# sourceMappingURL=core-keyset-guard.d.ts.map
@@ -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"];
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"];
20
20
  export type EngineNoticeWireCode = (typeof ENGINE_NOTICE_WIRE_CODES)[number];
21
21
  /**
22
22
  * S-690 —— **本仓自铸**的通告码与它们的受众(码 / 铸点 / 受众三者的属主都是本仓)。
@@ -40,6 +40,8 @@ export declare const SERVER_NOTICE_AUDIENCE: {
40
40
  * `detail: { layers, remedy }` —— 层名(挂载面的直接子目录名)与那一句补救;条目名 / 内容不进。
41
41
  */
42
42
  readonly "memory.layer_locked_legacy": "operator";
43
+ /** S-612 —— 会话存档部分记录被跳过(唯一铸点 `src/session-records-skipped-notice.ts`);受众 user,理由见白名单键注。 */
44
+ readonly "session.records_skipped": "user";
43
45
  };
44
46
  export type ServerNoticeCode = keyof typeof SERVER_NOTICE_AUDIENCE;
45
47
  /** 该码是否本仓自铸(属主判据单点:门与投递面都读这一个)。 */
@@ -26,10 +26,12 @@ export const ENGINE_NOTICE_WIRE_CODES = [
26
26
  "approval.read_root_granted",
27
27
  "approval.read_root_grant_rejected",
28
28
  "instructions.source_changed",
29
+ "session.records_skipped",
29
30
  ];
30
31
  export const SERVER_NOTICE_AUDIENCE = {
31
32
  "instructions.source_changed": "user",
32
33
  "memory.layer_locked_legacy": "operator",
34
+ "session.records_skipped": "user",
33
35
  };
34
36
  export function isServerNoticeCode(code) {
35
37
  return Object.hasOwn(SERVER_NOTICE_AUDIENCE, code);
@@ -188,7 +188,7 @@ export declare function screenedGate(v: unknown): GateOutcome | undefined;
188
188
  * ## 读腿与写腿现在是**同一只**筛(7.74.0 起,零双读)
189
189
  * 7.72.0–7.73.0 期间读腿多一步「认本服务上一版写下的退役词旧拼法」(`unresolvable` → `ancestor_marked`),
190
190
  * 那是一条**长期双读臂**:同一份字节在写腿与读腿上有两种理解,而且没有到期日。7.74.0 把它换成**一次性
191
- * 迁移**(启动期把存量字节原地改名,`plugins/one-time-migrations.ts`,随 7.75.0 删)⇒ 读腿退回写腿那只
191
+ * 迁移**(启动期把存量字节原地改名,`plugins/one-time-migrations.ts` 的 M3;窗口见 `plugins/one-time-migrations.ts` 登记表)⇒ 读腿退回写腿那只
192
192
  * 严格筛子,集外词一律整条 withhold + 计数,不再有第二种读法。
193
193
  *
194
194
  * ## 与写腿脱敏汇点的关系(别再走回头路)
@@ -554,10 +554,19 @@ export function wiringManifestEventData(ev) {
554
554
  return dropProjectedFace("wiring_manifest.teamRole is outside core TEAM_ROLES");
555
555
  return m.teamRole;
556
556
  })();
557
+ const fileHistory = (() => {
558
+ if (m.fileHistory === undefined)
559
+ return undefined;
560
+ const word = str(m.fileHistory);
561
+ if (word === undefined)
562
+ return dropProjectedFace("wiring_manifest.fileHistory is not a non-empty string");
563
+ return word;
564
+ })();
557
565
  return {
558
566
  ...(num(m.schemaVersion) !== undefined ? { schemaVersion: m.schemaVersion } : {}),
559
567
  ...(str(legIn.kind) !== undefined ? { leg: { kind: legIn.kind } } : {}),
560
568
  ...(teamRole !== undefined ? { teamRole } : {}),
569
+ ...(fileHistory !== undefined ? { fileHistory } : {}),
561
570
  ...(ask !== undefined ? { ask } : {}),
562
571
  ...(question !== undefined ? { question } : {}),
563
572
  ...(str(interactionIn.posture) !== undefined ? { interaction: { posture: interactionIn.posture } } : {}),
@@ -16,7 +16,8 @@
16
16
  * `NotificationExcluded` 直接读成本表这一行的 `excluded`,于是「哪几个键有意不上 wire」全仓**只有一个写者**。
17
17
  * 旧形是两个:守卫文件一簿、`test/wire-whitelist-exhaustiveness.test.ts` 的 `KnownExcluded` 一簿,那份门注
18
18
  * 自己写着「两处必须同时改」—— 而「必须同时改」正是 [ref] 要下线的东西(对账门不算替代,单写者才算)。
19
- * 本表是纯数据、无行为,被守卫文件以 `typeof import(...)` 的**类型查询**读取(编译期擦除,零运行期边)。
19
+ * 本表是纯数据、无行为(唯一的派生是 `wiring_manifest_operator` 行的 `excluded` 取值器 = 租户行减具名常量,S-724),
20
+ * 被守卫文件以 `typeof import(...)` 的**类型查询**读取(编译期擦除,零运行期边)。
20
21
  *
21
22
  * ## 门在哪里
22
23
  * `test/wire-whitelist-exhaustiveness.test.ts`:对每一行喂一枚 core 型全键 fixture,真的跑一遍构造器,
@@ -54,6 +55,12 @@ export interface WireProjectionFace {
54
55
  export declare const FRAME_ENVELOPE_KEYS: readonly ["type", "sourceTaskId", "bgAgentId"];
55
56
  /** 探针文件要从 `@sema-agent/core` 具名导入的类型(`coreType` 表达式里用到的全部名字)。 */
56
57
  export declare const WIRE_PROJECTION_CORE_IMPORTS: readonly ["TaskEvent", "TaskNotificationPayload", "WiringManifest", "EngineNotice", "MemoryImportReport", "OriginClearanceRow", "OriginClearanceEvent", "MemoryEntryOrigin"];
58
+ /**
59
+ * `wiring_manifest` 的 **operator 面多投的那几段**(`wiringManifestOperatorEventData` 在租户形之上加的段)。
60
+ * operator 行的 `excluded` = 租户行减本常量(派生);四段各自的受众裁定在 `core-keyset-guard.ts` ⑥b。
61
+ * operator 构造器新投一段 ⇒ 同批加进本常量(否则 operator 行会把它登记成「不投」,键集等式门当场红)。
62
+ */
63
+ export declare const WIRING_MANIFEST_OPERATOR_ONLY_SECTIONS: readonly ["governance", "configFingerprint", "autoConsolidation", "readDeny"];
57
64
  /**
58
65
  * 全部帧投影面。键 = 面名(门的报错文案与负控都按它称呼这一行)。
59
66
  *
@@ -161,14 +168,14 @@ export declare const WIRE_PROJECTION_FACES: {
161
168
  readonly wiring_manifest_tenant: {
162
169
  readonly projector: "wiringManifestEventData";
163
170
  readonly coreType: `WiringManifest & ${string}`;
164
- readonly excluded: readonly ["governance", "configFingerprint", "autoConsolidation", "readDeny", "peerPresence", "fileHistory"];
171
+ readonly excluded: readonly ["governance", "configFingerprint", "autoConsolidation", "readDeny", "peerPresence"];
165
172
  readonly unwrapped: readonly ["manifest"];
166
173
  readonly minted: readonly [];
167
174
  };
168
175
  readonly wiring_manifest_operator: {
169
176
  readonly projector: "wiringManifestOperatorEventData";
170
177
  readonly coreType: `WiringManifest & ${string}`;
171
- readonly excluded: readonly ["peerPresence", "fileHistory"];
178
+ readonly excluded: readonly string[];
172
179
  readonly unwrapped: readonly ["manifest"];
173
180
  readonly minted: readonly [];
174
181
  };
@@ -10,6 +10,10 @@ export const WIRE_PROJECTION_CORE_IMPORTS = [
10
10
  "MemoryEntryOrigin",
11
11
  ];
12
12
  const arm = (name) => `Extract<TaskEvent, { type: "${name}" }>`;
13
+ export const WIRING_MANIFEST_OPERATOR_ONLY_SECTIONS = ["governance", "configFingerprint", "autoConsolidation", "readDeny"];
14
+ function withoutSections(all, drop) {
15
+ return all.filter((k) => !drop.includes(k));
16
+ }
13
17
  export const WIRE_PROJECTION_FACES = {
14
18
  task_progress: { projector: "taskProgressEventData", coreType: arm("task_progress"), excluded: [], minted: [] },
15
19
  tool_start: { projector: "toolStartEventData", coreType: arm("tool_start"), excluded: [], minted: [] },
@@ -36,14 +40,16 @@ export const WIRE_PROJECTION_FACES = {
36
40
  wiring_manifest_tenant: {
37
41
  projector: "wiringManifestEventData",
38
42
  coreType: `WiringManifest & ${arm("wiring_manifest")}`,
39
- excluded: ["governance", "configFingerprint", "autoConsolidation", "readDeny", "peerPresence", "fileHistory"],
43
+ excluded: ["governance", "configFingerprint", "autoConsolidation", "readDeny", "peerPresence"],
40
44
  unwrapped: ["manifest"],
41
45
  minted: [],
42
46
  },
43
47
  wiring_manifest_operator: {
44
48
  projector: "wiringManifestOperatorEventData",
45
49
  coreType: `WiringManifest & ${arm("wiring_manifest")}`,
46
- excluded: ["peerPresence", "fileHistory"],
50
+ get excluded() {
51
+ return withoutSections(WIRE_PROJECTION_FACES.wiring_manifest_tenant.excluded, WIRING_MANIFEST_OPERATOR_ONLY_SECTIONS);
52
+ },
47
53
  unwrapped: ["manifest"],
48
54
  minted: [],
49
55
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.103.1",
3
+ "version": "7.104.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.33.6",
64
+ "@sema-agent/core": "7.33.8",
65
65
  "@sema-agent/settings-schema": "6.3.0",
66
66
  "e2b": "^2.28.0",
67
67
  "libsodium-wrappers": "^0.8.4",
@@ -77,7 +77,7 @@
77
77
  "sharp": "^0.35.3"
78
78
  },
79
79
  "devDependencies": {
80
- "@sema-agent/sdk": "11.3.0",
80
+ "@sema-agent/sdk": "12.0.1",
81
81
  "@types/libsodium-wrappers": "^0.7.14",
82
82
  "@types/node": "22.10.2",
83
83
  "@types/pg": "^8.20.0",