@sema-agent/server 7.143.0 → 7.145.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 (139) hide show
  1. package/MIGRATION.md +295 -0
  2. package/README.md +3 -0
  3. package/README.zh-CN.md +1 -0
  4. package/USAGE.md +34 -16
  5. package/dist/approval-card.d.ts +31 -14
  6. package/dist/approval-card.js +8 -8
  7. package/dist/audit.d.ts +2 -21
  8. package/dist/audit.js +0 -9
  9. package/dist/boot/execution-env.js +1 -1
  10. package/dist/boot/runner-deps.d.ts +10 -5
  11. package/dist/boot/runner-deps.js +5 -4
  12. package/dist/boot/session-faces.d.ts +1 -1
  13. package/dist/boot/session-faces.js +3 -3
  14. package/dist/boot/shutdown.d.ts +1 -1
  15. package/dist/boot/shutdown.js +4 -2
  16. package/dist/boot/stage-01-config.js +2 -0
  17. package/dist/boot/stage-07-capability-layer.js +1 -1
  18. package/dist/boot/stage-09-leader.d.ts +1 -1
  19. package/dist/boot/stage-10-http-server.d.ts +1 -1
  20. package/dist/boot/stage-10-http-server.js +4 -9
  21. package/dist/capabilities/scenarios.d.ts +14 -18
  22. package/dist/capabilities/scenarios.js +1 -2
  23. package/dist/config-d-migration.d.ts +3 -0
  24. package/dist/delegated-session-owner.d.ts +33 -151
  25. package/dist/delegated-session-owner.js +1 -83
  26. package/dist/entry-mode-clamp-notice.d.ts +1 -1
  27. package/dist/execution-placement.d.ts +7 -4
  28. package/dist/fleet/fleet-bus.d.ts +13 -3
  29. package/dist/fleet/fleet-bus.js +17 -3
  30. package/dist/hooks/classic-stations.d.ts +3 -1
  31. package/dist/hooks/classic-stations.js +2 -0
  32. package/dist/http/admission.js +2 -1
  33. package/dist/http/checkpoint-ctx.d.ts +1 -0
  34. package/dist/http/checkpoint-ctx.js +1 -0
  35. package/dist/http/durable-run-leg.js +7 -6
  36. package/dist/http/principal-gate.d.ts +1 -1
  37. package/dist/http/refusal-body.d.ts +7 -3
  38. package/dist/http/refusal-body.js +1 -0
  39. package/dist/http/resume-legs.js +16 -7
  40. package/dist/http/route-ctx.d.ts +15 -0
  41. package/dist/http/routes/approvals-assistant.d.ts +20 -1
  42. package/dist/http/routes/approvals-assistant.js +28 -6
  43. package/dist/http/routes/rules.d.ts +1 -1
  44. package/dist/http/routes/rules.js +5 -2
  45. package/dist/http/routes/session-sync.js +12 -0
  46. package/dist/http/routes/tasks.js +12 -9
  47. package/dist/http/run-meta.d.ts +14 -7
  48. package/dist/http/run-meta.js +5 -2
  49. package/dist/http/server.d.ts +13 -5
  50. package/dist/http/server.js +3 -0
  51. package/dist/http/submit-ctx.d.ts +12 -0
  52. package/dist/http/submit-ctx.js +23 -0
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.js +1 -0
  55. package/dist/lane-home-probe.d.ts +15 -0
  56. package/dist/lane-home-probe.js +4 -0
  57. package/dist/observability/corrupt-read-seat.d.ts +5 -9
  58. package/dist/observability/corrupt-read-seat.js +1 -2
  59. package/dist/observability/fail-open.d.ts +26 -10
  60. package/dist/observability/fail-open.js +26 -10
  61. package/dist/observability/run-terminal.d.ts +1 -1
  62. package/dist/observability/run-terminal.js +2 -2
  63. package/dist/parked-revive-surface.d.ts +6 -7
  64. package/dist/peer-inbound-face.d.ts +29 -34
  65. package/dist/peer-inbound-face.js +28 -63
  66. package/dist/plugins/approval-ask-store-file.d.ts +16 -2
  67. package/dist/plugins/approval-ask-store-file.js +21 -1
  68. package/dist/plugins/approval-ask-store-memory.d.ts +8 -2
  69. package/dist/plugins/approval-ask-store-memory.js +11 -8
  70. package/dist/plugins/caching-session-store.d.ts +3 -3
  71. package/dist/plugins/caching-session-store.js +3 -3
  72. package/dist/plugins/checkpoint-store-sql.d.ts +62 -37
  73. package/dist/plugins/checkpoint-store-sql.js +123 -40
  74. package/dist/plugins/file-run-store.d.ts +13 -1
  75. package/dist/plugins/file-run-store.js +11 -1
  76. package/dist/plugins/fork-routing-session-store.d.ts +8 -1
  77. package/dist/plugins/fork-routing-session-store.js +6 -0
  78. package/dist/plugins/local-checkpoint-store.d.ts +17 -8
  79. package/dist/plugins/local-checkpoint-store.js +26 -7
  80. package/dist/plugins/local-session-store.d.ts +105 -34
  81. package/dist/plugins/local-session-store.js +168 -65
  82. package/dist/plugins/local-session-window.d.ts +4 -2
  83. package/dist/plugins/memory-run-store.d.ts +2 -0
  84. package/dist/plugins/memory-run-store.js +6 -0
  85. package/dist/plugins/one-time-migrations.d.ts +12 -12
  86. package/dist/plugins/one-time-migrations.js +13 -13
  87. package/dist/plugins/pg-session-storage.d.ts +1 -9
  88. package/dist/plugins/pg-session-storage.js +2 -19
  89. package/dist/plugins/remote-env-host.d.ts +8 -4
  90. package/dist/plugins/remote-env-host.js +7 -9
  91. package/dist/plugins/remote-shell.js +12 -2
  92. package/dist/plugins/run-store-sql.d.ts +4 -0
  93. package/dist/plugins/run-store-sql.js +3 -0
  94. package/dist/plugins/schema-consistency.d.ts +5 -1
  95. package/dist/plugins/session-fork.d.ts +1 -1
  96. package/dist/plugins/session-ownership.d.ts +9 -0
  97. package/dist/plugins/session-ownership.js +6 -0
  98. package/dist/plugins/session-store.d.ts +13 -16
  99. package/dist/plugins/session-store.js +9 -10
  100. package/dist/plugins/sql-json-column.d.ts +24 -2
  101. package/dist/plugins/sql-json-column.js +38 -3
  102. package/dist/plugins/store-backend.js +3 -2
  103. package/dist/plugins/tidb-session-store.d.ts +2 -14
  104. package/dist/plugins/tidb-session-store.js +2 -20
  105. package/dist/prompt-screen.d.ts +21 -0
  106. package/dist/prompt-screen.js +8 -0
  107. package/dist/rules-consent.d.ts +63 -51
  108. package/dist/rules-consent.js +6 -10
  109. package/dist/run-acceptance.d.ts +40 -10
  110. package/dist/run-acceptance.js +38 -5
  111. package/dist/run-local.js +2 -1
  112. package/dist/runs.js +5 -1
  113. package/dist/security.d.ts +10 -7
  114. package/dist/session-agent.d.ts +39 -0
  115. package/dist/session-agent.js +16 -0
  116. package/dist/session-error-wire.d.ts +25 -26
  117. package/dist/session-error-wire.js +4 -5
  118. package/dist/session-incarnation.d.ts +2 -2
  119. package/dist/session-notify-door.d.ts +2 -2
  120. package/dist/session-notify-door.js +5 -1
  121. package/dist/tool-approval.d.ts +3 -1
  122. package/dist/tool-approval.js +47 -18
  123. package/dist/trace/core-keyset-guard.d.ts +1 -1
  124. package/dist/trace/engine-notice-wire.d.ts +1 -1
  125. package/dist/trace/engine-notice-wire.js +1 -1
  126. package/dist/trace/ledger-sink.js +12 -4
  127. package/dist/trace/model-segment.d.ts +26 -6
  128. package/dist/trace/model-segment.js +58 -9
  129. package/dist/trace/project.d.ts +8 -2
  130. package/dist/trace/project.js +6 -4
  131. package/dist/trace/redact.d.ts +77 -2
  132. package/dist/trace/redact.js +234 -99
  133. package/dist/trace/self-minted-wire.d.ts +160 -9
  134. package/dist/trace/self-minted-wire.js +52 -3
  135. package/package.json +3 -3
  136. package/wire-changes/server@7.144.0.json +291 -0
  137. package/wire-changes/server@7.145.0.json +146 -0
  138. package/dist/session-records-skipped-notice.d.ts +0 -21
  139. package/dist/session-records-skipped-notice.js +0 -27
@@ -16,6 +16,11 @@ const REASONING_SEGMENT_ID_MARK = "~reasoning_end";
16
16
  export function reasoningSegmentEventId(anchor) {
17
17
  return typeof anchor === "string" && anchor.length > 0 ? `${anchor}${REASONING_SEGMENT_ID_MARK}` : undefined;
18
18
  }
19
+ export function blockText(deltas, end) {
20
+ if (deltas !== "" || end === undefined)
21
+ return deltas;
22
+ return typeof end.content === "string" ? end.content : "";
23
+ }
19
24
  export function reasoningDeltaBytes(ev) {
20
25
  return typeof ev.delta === "string" && ev.delta !== "" ? ev.delta : undefined;
21
26
  }
@@ -34,25 +39,48 @@ function release(s) {
34
39
  return s.text.takeSettled();
35
40
  }
36
41
  catch (err) {
37
- recordFailOpen("server.run-terminal.redact-threw", `live-hold len=${s.text.fed.length} err=${errorKindOf(err)}`);
42
+ recordFailOpen("server.run-terminal.redact-threw", `live-hold len=${s.text.length} err=${errorKindOf(err)}`);
38
43
  return "";
39
44
  }
40
45
  }
46
+ function deliverBlock(s) {
47
+ try {
48
+ return s.text.takeAll();
49
+ }
50
+ catch (err) {
51
+ recordFailOpen("server.run-terminal.redact-threw", `live-hold len=${s.text.length} err=${errorKindOf(err)}`);
52
+ s.text.skip();
53
+ return OVERSIZED_REDACTION_PLACEHOLDER;
54
+ }
55
+ }
56
+ function closeBlock(s) {
57
+ try {
58
+ s.text.endBlock();
59
+ }
60
+ catch (err) {
61
+ recordFailOpen("server.run-terminal.redact-threw", `live-hold len=${s.text.length} err=${errorKindOf(err)}`);
62
+ }
63
+ }
41
64
  function remainderOf(family, s) {
42
65
  try {
43
66
  const r = s.text.flush();
44
67
  if (r.diverged)
45
- recordFailOpen("server.live-text.hold-diverged", `family=${family} len=${s.text.fed.length}`);
68
+ recordFailOpen("server.live-text.hold-diverged", `family=${family} len=${s.text.length}`);
46
69
  return r.rest;
47
70
  }
48
71
  catch (err) {
49
- recordFailOpen("server.run-terminal.redact-threw", `live-hold len=${s.text.fed.length} err=${errorKindOf(err)}`);
72
+ recordFailOpen("server.run-terminal.redact-threw", `live-hold len=${s.text.length} err=${errorKindOf(err)}`);
50
73
  return OVERSIZED_REDACTION_PLACEHOLDER;
51
74
  }
52
75
  }
53
76
  export function createLiveModelText() {
54
77
  const open = { text: new Map(), reasoning: new Map() };
55
78
  const deltaType = (family) => (family === "text" ? "text_delta" : "reasoning_delta");
79
+ const openSegment = (family, key, ident, deliver) => {
80
+ const s = { ident, text: createRedactionStream(), delivering: deliver };
81
+ open[family].set(key, s);
82
+ return s;
83
+ };
56
84
  const close = (family, key, deliver) => {
57
85
  const s = open[family].get(key);
58
86
  if (s === undefined)
@@ -84,10 +112,8 @@ export function createLiveModelText() {
84
112
  const ident = identOf(ev);
85
113
  const key = streamKeyOf(ident);
86
114
  let s = open[family].get(key);
87
- if (s === undefined && ev.delta !== "") {
88
- s = { ident, text: createRedactionStream(), delivering: deliver };
89
- open[family].set(key, s);
90
- }
115
+ if (s === undefined && ev.delta !== "")
116
+ s = openSegment(family, key, ident, deliver);
91
117
  if (s !== undefined) {
92
118
  if (deliver && !s.delivering) {
93
119
  s.delivering = true;
@@ -111,8 +137,31 @@ export function createLiveModelText() {
111
137
  return feed("reasoning", ev, deliver);
112
138
  const key = streamKeyOf(identOf(ev));
113
139
  if (ev.type === "text_end") {
114
- const out = close("text", key, deliver);
115
- const end = deliver ? textEndEventData(ev) : null;
140
+ let s = open.text.get(key);
141
+ const out = [];
142
+ const before = s === undefined ? "" : s.text.blockContext();
143
+ const arrived = s === undefined ? "" : s.text.blockBytes();
144
+ const unfed = blockText(arrived, ev).slice(arrived.length);
145
+ const endOnly = unfed !== "" ? unfed : undefined;
146
+ if (s === undefined && endOnly !== undefined)
147
+ s = openSegment("text", key, identOf(ev), deliver);
148
+ if (s !== undefined) {
149
+ if (deliver && !s.delivering) {
150
+ s.delivering = true;
151
+ s.text.skip();
152
+ }
153
+ else if (!deliver)
154
+ s.delivering = false;
155
+ if (endOnly !== undefined)
156
+ s.text.feed(endOnly);
157
+ if (deliver) {
158
+ const rest = deliverBlock(s);
159
+ if (rest !== "" && endOnly === undefined)
160
+ out.push({ type: "text_delta", delta: rest, ...liveIdentityFields(s.ident) });
161
+ }
162
+ closeBlock(s);
163
+ }
164
+ const end = deliver ? textEndEventData(ev, before) : null;
116
165
  if (end !== null)
117
166
  out.push({ type: "text_end", ...end });
118
167
  return out;
@@ -219,15 +219,17 @@ export declare function screenedGate(v: unknown): GateOutcome | undefined;
219
219
  * 不改原对象(调用方与账本共享引用):有变更时换一只信封,原记录一个字节不动。
220
220
  */
221
221
  export declare function screenedStoredEventData(type: string, data: unknown): unknown;
222
- /** `text_end` 段末帧的**唯一**铸点(三腿共用:top stream / 转发子代腿 / 子代 tail 面)。content = 段权威全文 ⇒ 与
222
+ /** `text_end` 段末帧的**唯一**铸点(三腿共用:top stream / 转发子代腿 / 子代 tail 面)。content = 引擎这一块的权威全文 ⇒ 与
223
223
  * durable 面(`result` / 账本 `text` 行)同一只 {@link redactModelText}(认出才改写、抛则占位、不设上限)。
224
+ * `before` = 本段在这一块之前已喂入的模型文本(引擎的文字块是段的切片,段 = 账本 `text` 行的单位):这一块**接在本段前文之后**判、只交这一块那一截
225
+ * (锚在前一块、值在这一块 ⇒ 照样认得出);由登记册在块的交付点给出,缺省 = 这一块就是段首。
224
226
  * 此前三处各自逐字透传 —— 同一份文本毫秒后在 `done.result.result` 上已脱,live 面却在凭据生成瞬间明文([ref])。
225
227
  * 逐块的 `text_delta` 由 `trace/model-segment.ts` 的登记册铸(7.143.0 起经同一只脱敏器的放行窗,一段的逐块拼接 = 本帧 content);
226
228
  * 本函数由登记册在段末调,三腿不再各自调。
227
229
  * 坏形(content 非串/空串)⇒ null(丢帧,不铸「权威空段」:core 契约明言空段不发本帧,消费端收到空段会清掉已攒 delta)。
228
230
  * 身份**四键**随行(live 现状带全四键,消费端可能已依赖);值经 {@link identityKey} —— 非空 string 才上 wire
229
231
  * ([ref] 起不再逐字透传:坏形身份键会在 live 写口上铸出假游标)。 */
230
- export declare function textEndEventData(ev: Record<string, unknown>): SegmentEndFields | null;
232
+ export declare function textEndEventData(ev: Record<string, unknown>, before?: string): SegmentEndFields | null;
231
233
  /**
232
234
  * live 面的**身份四键**(core `TaskEventIdentity` 全集)—— 一个属主,两处共用({@link segmentEndFields}
233
235
  * 与 sync 腿白名单 catch-all 的 `ident`;此前是两份手抄)。与 durable 面的 {@link identityFields} 的差别
@@ -985,6 +987,9 @@ export declare function wiringManifestFaceEventData(ev: {
985
987
  * 路径或名字(引擎原样回声,不是解析后的落点 —— 多租户沙箱车道上它是这次 run 自己工作区里的相对 / 绝对路径串,帧上不出现任何别的租户
986
988
  * 或宿主盘的信息),与已展示的工具实参 / `workspace_changed.cwd` 同一信任级(UNTRUSTED 的路径串)⇒ 过 `redactSecrets`。长度界由引擎定
987
989
  * (`USER_ATTACHMENT_LABEL_MAX`),本仓不另截。非对象的成员丢、集外的成员键不投(同 `actor` 的白名单理由)。
990
+ * · `rewrittenBy[]`(core 7.62.0 [ref],只在腿的 prompt 屏 `prompt.submit` 改写过这条输入时在场)—— 改写它的扩展注册名,按改写序 ⇒ verbatim:
991
+ * 名字的形由引擎在注册入口判(`MOD_REGISTRATION_NAME`,能引号 / 括号 / 折句的名在入口拒),与会话行上同名位(`audit.ts` 的 `rewrittenBy: "verbatim"`)
992
+ * 同一读法。非数组不投、非串 / 空串成员丢、丢完为空不铸键(缺席 = 原样送达,不是空数组)。
988
993
  *
989
994
  * `actor` 逐键挑(禁 spread):core 已声明 `ActorAssertion` 是冻结三键形,但白名单的意义正是「core 加第
990
995
  * 四键时由人处置」——身份对象上多出来的键裸上线是本文件反复登记的病族。
@@ -1001,6 +1006,7 @@ export declare function humanInputEventData(ev: {
1001
1006
  principal?: unknown;
1002
1007
  entryId?: unknown;
1003
1008
  attachments?: unknown;
1009
+ rewrittenBy?: unknown;
1004
1010
  eventId?: unknown;
1005
1011
  parentToolCallId?: unknown;
1006
1012
  }): FrameData<{
@@ -210,10 +210,10 @@ function projectSettlement(st) {
210
210
  ...(typeof st.note === "string" ? { note: redactSecrets(st.note).slice(0, SETTLEMENT_NOTE_MAX) } : {}),
211
211
  };
212
212
  }
213
- export function textEndEventData(ev) {
213
+ export function textEndEventData(ev, before = "") {
214
214
  if (typeof ev.content !== "string" || ev.content.length === 0)
215
215
  return null;
216
- return segmentEndFields(ev.content, ev);
216
+ return segmentEndFields(ev.content, ev, before);
217
217
  }
218
218
  export function liveIdentityFields(ev) {
219
219
  return {
@@ -223,8 +223,8 @@ export function liveIdentityFields(ev) {
223
223
  ...(identityKey(ev.bgAgentId) !== undefined ? { bgAgentId: identityKey(ev.bgAgentId) } : {}),
224
224
  };
225
225
  }
226
- function segmentEndFields(content, ev) {
227
- return { content: redactModelText(content), ...liveIdentityFields(ev) };
226
+ function segmentEndFields(content, ev, before = "") {
227
+ return { content: redactModelText(content, before), ...liveIdentityFields(ev) };
228
228
  }
229
229
  export function reasoningEndEventData(ev) {
230
230
  if (typeof ev.content !== "string" || ev.content.trim().length === 0)
@@ -811,6 +811,7 @@ export function humanInputEventData(ev) {
811
811
  ...(str(m.label) !== undefined ? { label: seatText("trace-frame-text", String(m.label)) } : {}),
812
812
  }))
813
813
  : undefined;
814
+ const rewrittenBy = Array.isArray(ev.rewrittenBy) ? ev.rewrittenBy.filter((n) => str(n) !== undefined) : [];
814
815
  const actor = {
815
816
  ...(str(a.id) !== undefined ? { id: seatText("trace-frame-text", String(a.id)) } : {}),
816
817
  ...(typeof a.hostAsserted === "boolean" ? { hostAsserted: a.hostAsserted } : {}),
@@ -826,6 +827,7 @@ export function humanInputEventData(ev) {
826
827
  ...(Object.keys(actor).length > 0 ? { actor } : {}),
827
828
  ...(str(ev.entryId) !== undefined ? { entryId: ev.entryId } : {}),
828
829
  ...(attachments !== undefined ? { attachments } : {}),
830
+ ...(rewrittenBy.length > 0 ? { rewrittenBy } : {}),
829
831
  ...identityFields(ev),
830
832
  };
831
833
  }
@@ -38,6 +38,10 @@ export declare function armPoolBudgetForTest(): {
38
38
  readings: number;
39
39
  coverageVisits: number;
40
40
  rowVisits: number;
41
+ scannedUnits: number;
42
+ windowAdvances: number;
43
+ windowRefusals: number;
44
+ windowEvaluations: number;
41
45
  };
42
46
  export declare function resetArmPoolBudgetForTest(): void;
43
47
  /**
@@ -99,6 +103,23 @@ export declare function structuralScannerForTest(arm: "json-secret-field" | "pem
99
103
  /** 测试可见:扫描器 indexOf 实际扫过的字节数(摊还线性 ⇒ ≤ 常数 × 输入长)。 */
100
104
  export declare function ppkScanBytesForTest(): number;
101
105
  export declare function resetPpkScanBytesForTest(): void;
106
+ /**
107
+ * 🔴 **一条臂向前读多远**(候选池成员身份的必填一格 {@link MemberIdentity.prior};闭集两形,读者一律穷尽 `switch`)—— 流式口扫描窗的两张字母表
108
+ * (地平线不数的间隔码元、被丢前缀里不许有的定界字面)由这一格现算({@link STREAM_WINDOW_ALPHABETS}),窗不另持一份。
109
+ * · `bounded`:判定一个位置时向前读的部分有界 —— 判定距离之内({@link MemberIdentity.reach})、同一行之内,或隔着构造两部分之间一段**任意长**的间隔 run;
110
+ * `gap` 答「哪些码元可以出现在这段间隔里」(按码元判,原文读法 `json = false` —— 流式口只这样读)。没有任意长间隔的臂给 {@link NO_GAP}。
111
+ * 正则臂由构造者现算:词法关键字臂 = 它的分隔段类({@link KeywordTail.isSeparator},造正则的同一份类源);其余正则臂的「无界原子之后还有必需的东西」在
112
+ * {@link regexDecisionReach} 已加载期抛 ⇒ 没有间隔。手写扫描器给它自己读间隔时调的那只谓词(同一只函数,不抄字符表)。
113
+ * · `anchored`:读**整段之前任意远**的前文(孤立收尾栅栏看「之前有过起始栅栏」、无头体起于前面某一枚收尾之后),但只从这几枚**字面锚**起读 —— 容器的定界字面;
114
+ * 前文里没有它们,这条臂就没有可读的前文。间隔不必声明:窗不越过任何一枚字面锚(这一形的构造整只在锚之后)。
115
+ */
116
+ export type PriorReading = {
117
+ readonly kind: "bounded";
118
+ readonly gap: (unit: number) => boolean;
119
+ } | {
120
+ readonly kind: "anchored";
121
+ readonly anchors: readonly [string, ...string[]];
122
+ };
102
123
  /** Redact unambiguous secret formats from reasoning/CoT before it is persisted to the durable trace log. */
103
124
  /** S24 (SILENT-FALLBACK P1): optional redaction observer — rewrites previously had zero fingerprint, so
104
125
  * over-redaction (mangling legit content) and under-redaction (a pattern that never fires) were both
@@ -165,14 +186,20 @@ export declare const WHOLE_EXTENT: RedactionExtent;
165
186
  * `from`:出门区与余量**各自从自己的起点数**(窗 = 出门区〔从头数半窗〕+ 余量〔从出门区终点起再数半窗〕),不是各自从头数「第一个 ≥ units 的终点」—— 后者遇到不可见填充会按翻倍
166
187
  * 跳到同一个终点,余量塌成 0,被窗尾切开的凭据在出门区里露出半截(修复验证 r3 high)。
167
188
  * 取舍:不可见填充很大的文本,窗随之延伸(最坏延伸到整段 = 整段读法的代价),换来「窗里的读法单元数恒 ≥ `units`」这一个不变量 —— 窗里每一读的视图都不短于 `units`。
168
- * 终点不落在转义写法中间:前缀末尾还没写完的转义写法({@link trailingPartialEscapeStart},在读法后文本上判,{@link shortestReading})不计数、窗延伸到它写完 —— 出门区终点与窗尾都因此落在转义的边上,余量从干净的边起数。
189
+ * 终点不落在转义写法中间:前缀末尾还没写完的转义写法(转义写法读者的截断读数,在读法后文本上判,{@link shortestReading})不计数、窗延伸到它写完 —— 出门区终点与窗尾都因此落在转义的边上,余量从干净的边起数。
169
190
  * 复杂度:**第一趟**若可见占比 ≥ 一半(少量换行 / 色码)按占比补足缺口,均匀的文本一两趟落在终点附近;补不足(或占比低)的趟一律至少翻倍 —— 总趟数 O(log 终点)、总功 O(终点),
170
191
  * 不存在逐趟只前进几个码元的爬行(4095 个可见 + 4000 个控制字符曾因此平方,K=4000 约 0.2–0.3 s)。判据与读法格同源(同一批切除点收集器,零第二份「什么算不可见」)。
171
192
  */
172
193
  export declare function readingHeadEnd(text: string, layout: RedactionLayout, units: number, from?: number): number;
194
+ /**
195
+ * `opts.before` = 这段文本的**前文**(同一段里先到、已经交出去的那一截):判定在 `before + text` 上做(前文里的锚照样认得出后文的值),交出的只是 `text` 那一截的读法 ——
196
+ * 与整段读法逐字节同一份,除了一条起头在前文里、止点在 `text` 里的跨度(它的原文字节一个不出,整枚换记号,与流式口 {@link renderRange} 同一条渲染律)。
197
+ * 命中只计止点在前文之后的跨度(前文那一截上一次已计)。读者:段末帧 `text_end` 的正文(引擎的一块接在本段前文之后判,`observability/run-terminal.ts` 的 `redactModelText`)。
198
+ */
173
199
  export declare function redactSecrets(text: string, opts?: {
174
200
  observe?: boolean;
175
201
  json?: boolean;
202
+ before?: string;
176
203
  }): string;
177
204
  /**
178
205
  * 脱敏 + **这一遍命中了哪些档位的臂**({@link RedactionArm} 闭集两词)—— 与 {@link redactSecrets} 同一只
@@ -248,23 +275,59 @@ export declare function pendingTailReachForTest(): ReadonlyArray<{
248
275
  readonly order: number;
249
276
  readonly reach: number;
250
277
  }>;
278
+ /** 测试可见:扫描窗的两张字母表 —— 全池成员的声明再加 `extra`(造一条新臂的声明),与生产同一只派生、同一份自证(不成立照样抛)。 */
279
+ export declare function streamWindowAlphabetsForTest(extra?: readonly PriorReading[]): {
280
+ readonly isBlank: (unit: number) => boolean;
281
+ readonly containerDelimiters: readonly string[];
282
+ };
283
+ /** 测试可见:转义写法文法本身 —— `read(s)` = 读者对 `s`(引导 run 起头)的读数(码点 / `-1` 读不出 / `-2` 截断)与写法末尾;`isBodyUnit` = 字面锚自证读的那只本体谓词。 */
284
+ export declare function escapeGrammarForTest(): {
285
+ readonly read: (s: string) => {
286
+ readonly value: number;
287
+ readonly end: number;
288
+ };
289
+ readonly isBodyUnit: (unit: number) => boolean;
290
+ };
291
+ /** 测试可见:扫描窗的前移点候选(`-1` = 窗尾有一枚还没写完的转义写法;`0` = 没有),字母表按全池成员的声明再加 `extra` 现算({@link streamWindowAlphabetsForTest})。 */
292
+ export declare function windowAdvancePointForTest(text: string, units: number, extra?: readonly PriorReading[]): number;
293
+ /** 测试可见:扫描窗前移点的认证(生产里 `q` 恒是行首;单测拿非行首的 `q` 钉住「名字在 `q` 之前、值在 `q` 之后」「跨度横跨 `q`」两形各自被认证挡下)。 */
294
+ export declare function windowIndependentAtForTest(text: string, q: number): boolean;
251
295
  /**
252
296
  * 🔴 **流式口 —— 同一只脱敏器的三口**(live 面逐块到达的模型文本 `text_delta` / `reasoning_delta` 的唯一放行器;登记册在 `trace/model-segment.ts`)。
253
297
  * 同一只 {@link runRedaction}、同一张表,只多一件事:知道文本还在长。
254
298
  * · **喂入**({@link RedactionStream.feed}):原文追加进本段;
255
299
  * · **取已判定前缀**({@link RedactionStream.takeSettled}):从上次取到的位置起,已判定的那一截的脱敏读法 —— head 读法,出门区终点 = 末尾 {@link PENDING_TAIL_UNITS}
256
300
  * 个读法单元之前;切点再退到横跨它的那一条跨度的起点(一枚可能还在长、或判定在更远处的凭据整条不出)与码点边界。按重扫节律({@link streamRescanStep})才真扫;
301
+ * · **交付点**({@link RedactionStream.takeAll}):已喂入的字节全部交出 —— 整段读法、文本到此为止(引擎关了一个文字块:那一块现在就要交付,不等后文);流不结束,
302
+ * 之后喂入的字节仍在整段上下文里判(前文的锚认得出后文的值)。一条凭据形恰横跨交付点 ⇒ 交付时那一截按到此为止判(读得出的半形出记号),之后横跨的那一条整枚换记号、
303
+ * 冲刷时的核对对不上(`diverged`,一段计一次)—— 判定读不到交付点之后的字节,这是交付点的定义,不是第二套规则;
257
304
  * · **收尾冲刷**({@link RedactionStream.flush}):整段读法交出其余字节,并核对已取出的字节在整段读法下是不是同一份。
258
305
  * 律:各次取出依次拼接 + 冲刷 = 整段读法(`redactSecrets(全段)`)逐字节 —— 只喂一次再冲刷就是整段口本身。前提是 {@link PENDING_TAIL_UNITS} 读对了每条臂的判定距离;
259
306
  * 读错的那一形在冲刷时现形(`diverged`),调用方响亮留痕。
260
307
  * 延迟上界:一个字节在它之后又到 {@link PENDING_TAIL_UNITS} 个读法单元之后的下一次重扫时交出;盖住它的跨度判定之前(或冲刷之前)不交出。
261
308
  * 不喂观察者(`observe:false`,与影子重扫同口):命中只在段末那一次整段脱敏里计(段末帧的铸点),逐块重扫不重复计数。扫描器抛 ⇒ 原样抛(放行位不动)。
309
+ *
310
+ * 🔴 **扫描窗**(多块长段的扫描量;与上面「押在窗里的字节」的窗不是一回事 —— 那是还没交出的未决尾,这是判定要读的前文):一段里每个块的交付点都要「前文 + 本块」一起判(锚在前一块、值在这一块),整段重扫则一段 N 块的总扫描量随 N 平方涨。流式口因此只留判定还需要的那一截前文 ——
311
+ * **扫描窗** = 全段的一个后缀 `[base, 全段长)`:取已判定前缀、交付点与前文({@link RedactionStream.blockContext})都只在窗上扫;只有收尾冲刷仍是整段一遍(一段一次),窗若曾判错,对不上在这里现形(`diverged`)。
312
+ * 扫描窗只在块界({@link RedactionStream.endBlock})前移,前移点 `q` 同时满足:① 是行首(其前一码元是换行 —— 行内向左读的臂不会读过它);② 其后至少留
313
+ * {@link STREAM_WINDOW_HORIZON} × 未决尾个**非空白**读法单元({@link windowAdvancePoint}:任何判定距离之内的候选与它们的证据邻居都在窗里;键与冒号已到、值的起引号还隔着一段任意长的空白在后文里的锚,
314
+ * 此刻不显现为任何跨度,只有「后文接在窗尾」能补全它 —— 空白不挤掉它们);③ 已交出到段尾(没有未交出的字节落在 `q` 之前);
315
+ * ④ 被丢的前缀里没有容器类臂的定界字面({@link STREAM_WINDOW_ALPHABETS} 的 `containerDelimiters`:孤立收尾、无头体、「前面有过起始栅栏」读的是整段之前任意远的前文);
316
+ * ⑤ 经 {@link windowIndependentAt} 认证(head 读法下:没有跨度横跨 `q`;从 `q` 起单独扫的跨度与整窗里同一截逐条相同)。
317
+ * 没有满足这五条的点(没有换行的超长单行、一枚始终没收尾的多块结构、整段里出现过密钥栅栏 / PuTTY 密钥头、整窗只有一行 …)⇒ 窗不动、读法不变,代价退回「窗长」(= 与改前同阶)。扫描窗能前移时,每块的扫描量是 O(块长 + 地平线 + 最长行),与段长无关。
318
+ * 🔴 **五条规则健全的三条前提,都由构造成立、不靠手抄**(臂集一变,窗随之变;没有一份要人记得同步的镜像):
319
+ * ① 每条臂任意长间隔里的字符,地平线都不数 —— 地平线不数的字符**就是**全池 `bounded` 声明的间隔之并({@link STREAM_WINDOW_ALPHABETS},{@link PriorReading});
320
+ * ② 读整段之前任意远前文的臂,都只从字面锚起读,窗不越过这些字面 —— 定界字面表**就是**全池 `anchored` 声明的字面锚之并(加载期自证每枚字面对删除单调:字面里没有一读会删的码元、首码元不是转义写法本体能读进的码元;不成立即抛);
321
+ * ③ 读法里「多码元、补全后消失」的写法只有转义写法,窗尾的半枚由**同一只**转义写法读者判({@link readEscapeElement} 的截断读数,{@link collectSpelledCuts} 的 `open`)—— 文法一份,新写法只改一处。
322
+ * 原生代理对拼成的星平面格式字符已到的那一半只占一个读法单元,撑不起地平线;原生控制字符逐码元删(不是一串整只消失)。
262
323
  */
263
324
  export interface RedactionStream {
264
325
  /** 喂入下一块原文。 */
265
326
  feed(chunk: string): void;
266
327
  /** 取已判定前缀(可为 `""`)。 */
267
328
  takeSettled(): string;
329
+ /** 交付点:已喂入、还没交出的字节全部按整段读法(文本到此为止)交出(可为 `""`);流不结束、不核对(核对在冲刷,一段一次)。 */
330
+ takeAll(): string;
268
331
  /** 收尾冲刷:其余字节的整段读法 + 已取出的字节与整段读法对不对得上。冲刷之后本流不再用。 */
269
332
  flush(): {
270
333
  readonly rest: string;
@@ -272,10 +335,22 @@ export interface RedactionStream {
272
335
  };
273
336
  /** 跳过:已喂入的字节不再交出(没有收件人;tail 面没人在听时),之后的判定仍按整段上下文。 */
274
337
  skip(): void;
275
- /** 已喂入的整段原文。 */
338
+ /** 块界:已喂入的字节到此为一块(引擎的 `text_end`)。扫描窗在此尝试前移;之后的 {@link RedactionStream.blockContext} / {@link RedactionStream.blockBytes} 从这里起算。认证扫描抛 ⇒ 原样抛(块界已记、窗不动)。 */
339
+ endBlock(): void;
340
+ /** 本块**之前**的前文(扫描窗内那一截;只作判定上下文,`text_end` 的正文接它判)。 */
341
+ blockContext(): string;
342
+ /** 本块已到的字节(上一个块界之后喂入的)。 */
343
+ blockBytes(): string;
344
+ /** 已喂入的整段原文(扫描窗之前丢出去的前缀也在;读时拼接)。 */
276
345
  readonly fed: string;
346
+ /** 已喂入的整段长度(码元)。 */
347
+ readonly length: number;
277
348
  }
278
349
  export declare function createRedactionStream(): RedactionStream;
350
+ /** 测试可见:同一只流式口,扫描窗永不前移(= 改前的读法:每次扫描都读整段)—— 差分钉用它当参照,断言有窗与无窗交出的帧逐字节相同。 */
351
+ export declare function createRedactionStreamWithoutWindowForTest(): RedactionStream;
352
+ /** 测试可见:同一只流式口,前移点的认证换成调用方给的函数(造「认证扫描抛」的形:真扫描器造不出来;断言抛出后窗与块界的状态一个字节没改)。 */
353
+ export declare function createRedactionStreamWithCertifierForTest(certify: (text: string, q: number) => boolean): RedactionStream;
279
354
  /** 测试可见:同一只流式口,未决尾按调用方给的数(比 {@link PENDING_TAIL_UNITS} 小 ⇒ 律被打破的那一形,冲刷时 `diverged` 现形 —— 响亮臂的触发器;生产调用方只有上面那一只)。 */
280
355
  export declare function createRedactionStreamWithTailForTest(units: number): RedactionStream;
281
356
  /**