@sema-agent/server 7.104.0 → 7.106.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.
- package/MIGRATION.md +114 -0
- package/USAGE.md +193 -52
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +2 -0
- package/deploy/sema-up/cluster-up.sh +15 -2
- package/deploy/sema-up/kube-up.sh +8 -3
- package/deploy/sema-up/lib/flagshape.sh +29 -0
- package/deploy/sema-up/lib/parse-machines.py +4 -1
- package/deploy/sema-up/sema-up.sh +11 -9
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +3 -1
- package/dist/approval-card.d.ts +1 -1
- package/dist/approval.d.ts +6 -1
- package/dist/approval.js +4 -4
- package/dist/auth-bridge.d.ts +14 -0
- package/dist/auth-bridge.js +7 -0
- package/dist/auto-mode-face.d.ts +49 -40
- package/dist/auto-mode-face.js +6 -68
- package/dist/bench/s1/live-deps.d.ts +9 -2
- package/dist/bench/s1/live-deps.js +13 -8
- package/dist/boot/budget-tracing.js +3 -2
- package/dist/boot/config-center.d.ts +4 -0
- package/dist/boot/config-center.js +95 -37
- package/dist/boot/coordinators.d.ts +1 -0
- package/dist/boot/coordinators.js +3 -1
- package/dist/boot/device-lane.d.ts +2 -1
- package/dist/boot/execution-env.d.ts +11 -0
- package/dist/boot/execution-env.js +30 -6
- package/dist/boot/execution-lane.js +2 -3
- package/dist/boot/leader.d.ts +2 -1
- package/dist/boot/leader.js +2 -0
- package/dist/boot/parked-revive-gate.d.ts +10 -7
- package/dist/boot/parked-revive-gate.js +4 -11
- package/dist/boot/resolve-spec.d.ts +1 -4
- package/dist/boot/resolve-spec.js +44 -21
- package/dist/boot/runner-deps.d.ts +5 -8
- package/dist/boot/runner-deps.js +5 -10
- package/dist/boot/side-query-lane.d.ts +2 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +2 -1
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +3 -2
- package/dist/boot/stage-06-runners.js +2 -5
- package/dist/boot/stage-07-capability-layer.d.ts +2 -2
- package/dist/boot/stage-07-capability-layer.js +1 -2
- package/dist/boot/stage-08-reapers.d.ts +2 -2
- package/dist/boot/stage-09-leader.d.ts +2 -2
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +2 -2
- package/dist/boot/stage-10-http-server.js +11 -7
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +6 -4
- package/dist/brain.d.ts +24 -2
- package/dist/brain.js +29 -10
- package/dist/budget.js +3 -0
- package/dist/capabilities/center-wiring.d.ts +64 -0
- package/dist/capabilities/center-wiring.js +24 -0
- package/dist/capabilities/composite-members.d.ts +89 -0
- package/dist/capabilities/composite-members.js +20 -0
- package/dist/capabilities/execution-lane.d.ts +3 -0
- package/dist/capabilities/execution-lane.js +5 -2
- package/dist/config-catalog.d.ts +30 -5
- package/dist/config-catalog.js +75 -26
- package/dist/config-center/apply-effective.d.ts +1 -1
- package/dist/config-center/apply-effective.js +7 -8
- package/dist/config-center/apply-ledger.d.ts +17 -0
- package/dist/config-center/apply-ledger.js +5 -0
- package/dist/config-center/center-request.d.ts +17 -0
- package/dist/config-center/center-request.js +4 -0
- package/dist/config-center/pull-state.d.ts +29 -0
- package/dist/config-center/pull-state.js +2 -0
- package/dist/config-types.d.ts +41 -14
- package/dist/config-types.js +1 -0
- package/dist/config.d.ts +50 -2
- package/dist/config.js +111 -23
- package/dist/cross-session-settings.d.ts +1 -1
- package/dist/deployment-governance.d.ts +12 -1
- package/dist/deployment-governance.js +2 -0
- package/dist/execution-lane-caps.d.ts +30 -2
- package/dist/execution-lane-caps.js +12 -0
- package/dist/file-history-disclosure.d.ts +7 -31
- package/dist/file-history-disclosure.js +12 -0
- package/dist/fleet/fleet-bus.d.ts +5 -1
- package/dist/fleet-client.d.ts +18 -13
- package/dist/fleet-client.js +32 -22
- package/dist/fleet-lease.d.ts +72 -47
- package/dist/fleet-lease.js +66 -14
- package/dist/governance-http-status.d.ts +0 -1
- package/dist/governance-http-status.js +0 -1
- package/dist/hosted-posture.d.ts +6 -17
- package/dist/hosted-posture.js +11 -6
- package/dist/http/admission.d.ts +1 -1
- package/dist/http/admission.js +14 -9
- package/dist/http/approval-sweeps.js +13 -1
- package/dist/http/dispatch.d.ts +18 -0
- package/dist/http/dispatch.js +18 -5
- package/dist/http/durable-run-leg.js +2 -0
- package/dist/http/request-guards.d.ts +25 -0
- package/dist/http/request-guards.js +25 -5
- package/dist/http/resume-legs.d.ts +1 -1
- package/dist/http/resume-legs.js +19 -11
- package/dist/http/route-ctx.d.ts +9 -0
- package/dist/http/route-table.d.ts +15 -4
- package/dist/http/route-table.js +6 -5
- package/dist/http/routes/approvals-assistant.js +0 -1
- package/dist/http/routes/capabilities.js +12 -11
- package/dist/http/routes/config-catalog.js +2 -1
- package/dist/http/routes/diagnostics.js +2 -0
- package/dist/http/routes/fleet.d.ts +3 -2
- package/dist/http/routes/images.js +0 -1
- package/dist/http/routes/runs.js +2 -8
- package/dist/http/routes/session-sync.js +6 -1
- package/dist/http/routes/sessions.js +5 -2
- package/dist/http/routes/side-query.js +8 -1
- package/dist/http/routes/tasks.js +4 -1
- package/dist/http/routes/trace-usage.d.ts +2 -0
- package/dist/http/routes/trace-usage.js +4 -9
- package/dist/http/server.d.ts +29 -8
- package/dist/http/server.js +4 -1
- package/dist/http/session-inherit-env.d.ts +37 -0
- package/dist/http/session-inherit-env.js +6 -0
- package/dist/http/sse-lifecycle.d.ts +1 -1
- package/dist/http/sse-log.d.ts +12 -9
- package/dist/http/sse-log.js +1 -8
- package/dist/http/wire-types.d.ts +19 -28
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/key-resolver.d.ts +74 -19
- package/dist/key-resolver.js +65 -7
- package/dist/leader/leader.d.ts +6 -0
- package/dist/leader/leader.js +2 -1
- package/dist/leader/repair-wire.js +2 -1
- package/dist/leader/wire.d.ts +9 -1
- package/dist/leader/wire.js +10 -6
- package/dist/observability/fail-open.d.ts +9 -1
- package/dist/observability/fail-open.js +9 -1
- package/dist/observability/metrics.js +3 -1
- package/dist/observability/permission-rule-events.d.ts +1 -1
- package/dist/observability/permission-rule-events.js +1 -1
- package/dist/observability/run-terminal.d.ts +82 -9
- package/dist/observability/run-terminal.js +112 -9
- package/dist/observability/secret-env-scrub.js +1 -1
- package/dist/observability/tool-trace.d.ts +2 -0
- package/dist/operator-ask.d.ts +29 -1
- package/dist/operator-ask.js +6 -0
- package/dist/parked-decide.d.ts +14 -1
- package/dist/parked-decide.js +8 -1
- package/dist/plugins/image-bake-store-sql.d.ts +0 -2
- package/dist/plugins/image-bake-store-sql.js +0 -5
- package/dist/plugins/retention-store-sql.js +1 -0
- package/dist/plugins/run-store-sql.d.ts +3 -2
- package/dist/plugins/schema-indexes.js +1 -0
- package/dist/plugins/session-policy-store-sql.d.ts +38 -7
- package/dist/plugins/session-policy-store-sql.js +73 -18
- package/dist/plugins/sql-driver.d.ts +1 -1
- package/dist/plugins/store-backend.d.ts +17 -6
- package/dist/plugins/store-backend.js +16 -5
- package/dist/plugins/tidb-pool.js +5 -4
- package/dist/read-face-posture.d.ts +25 -4
- package/dist/read-face-posture.js +9 -0
- package/dist/request-key-closure.js +2 -2
- package/dist/request-value-closure.d.ts +17 -5
- package/dist/request-value-closure.js +12 -0
- package/dist/router/route-orchestration.d.ts +11 -0
- package/dist/router/route-orchestration.js +7 -6
- package/dist/rules-consent.js +0 -11
- package/dist/run-local.js +4 -2
- package/dist/runs.d.ts +2 -1
- package/dist/runs.js +4 -4
- package/dist/runtime-caps-resolver.d.ts +4 -10
- package/dist/runtime-governance.d.ts +8 -11
- package/dist/runtime-governance.js +10 -41
- package/dist/sealed-key.d.ts +0 -40
- package/dist/sealed-key.js +0 -20
- package/dist/server-secret-env.d.ts +11 -12
- package/dist/session-policy-wire.d.ts +100 -12
- package/dist/session-policy-wire.js +71 -8
- package/dist/session-titler.d.ts +11 -0
- package/dist/session-titler.js +6 -2
- package/dist/sql-engine-posture.d.ts +2 -2
- package/dist/store-posture.d.ts +53 -0
- package/dist/store-posture.js +32 -0
- package/dist/task-settings.d.ts +34 -45
- package/dist/task-settings.js +46 -25
- package/dist/tool-approval.d.ts +51 -22
- package/dist/tool-approval.js +44 -8
- package/dist/tool-declaration-http-status.d.ts +12 -0
- package/dist/tool-declaration-http-status.js +12 -0
- package/dist/trace/core-keyset-guard.d.ts +7 -7
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +5 -0
- package/dist/trace/project.d.ts +13 -7
- package/dist/trace/project.js +54 -6
- package/dist/trace/redact.d.ts +7 -0
- package/dist/trace/redact.js +153 -61
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/unattended-permission-mode.d.ts +110 -0
- package/dist/unattended-permission-mode.js +65 -0
- package/package.json +3 -3
- package/dist/boot/session-shell-gate-registry.d.ts +0 -39
- package/dist/boot/session-shell-gate-registry.js +0 -21
- package/dist/plugins/memory-session-policy-store.d.ts +0 -46
- package/dist/plugins/memory-session-policy-store.js +0 -52
- package/dist/roster-bound-http-status.d.ts +0 -6
- package/dist/roster-bound-http-status.js +0 -6
|
@@ -78,10 +78,12 @@ export declare function notifyRunTerminal(rec: RunTerminalRecord): void;
|
|
|
78
78
|
/** S-105(test [ref] 旁生):终态**持久化**的 `errorMessage` 与日志副本同一脱敏口——provider 错误体(403 体带回显请求头/
|
|
79
79
|
* 订阅链接)里的密钥形不得原文落 run 行再由 `GET /v1/runs/:id` 直出。三 run store 的 `setTerminal` 写入前同调此函数;
|
|
80
80
|
* `undefined` 透传(缺席仍缺席),不截断(日志上限是日志的事,行上保留全文供取证)。 */
|
|
81
|
-
/** r4 最终轮 [medium]:`redactSecrets`
|
|
82
|
-
*
|
|
81
|
+
/** r4 最终轮 [medium]:`redactSecrets` 曾会抛 `RangeError`(关键字/前缀臂那一形由 S-219 的 `atLeast` 铸点闭掉;JSON 秘密字段臂的
|
|
82
|
+
* 转义对循环与 PEM 栅栏臂的标签交替循环由 S-265 换成手写线性扫描器闭掉 —— 已知形全部闭掉);S-105 把脱敏
|
|
83
83
|
* 挪到 `setTerminal` **入口**后,这个抛会先于 CAS/claim 释放 ⇒ run 卡 `running`、会话 claim 悬挂到 reaper。这里 **fail-closed
|
|
84
|
-
* 不抛**:脱敏失败 ⇒ 落固定占位 `«redacted:oversized»`(绝不落原文,也绝不阻断终态翻转),并走既有 fail-open 遥测 tag。
|
|
84
|
+
* 不抛**:脱敏失败 ⇒ 落固定占位 `«redacted:oversized»`(绝不落原文,也绝不阻断终态翻转),并走既有 fail-open 遥测 tag。
|
|
85
|
+
* 🔴 S-265 之后这条 throw 臂**照留**(结构性兜底,不依赖「脱敏器今天抛不抛」):它守的是「终态翻转不被一次脱敏异常拦下」,
|
|
86
|
+
* 而脱敏器是一张会长新臂的表 —— 计数 `server.run-terminal.redact-threw` 恒零是证据,不归零就是新形,运维按 detail 回溯。 */
|
|
85
87
|
export declare const OVERSIZED_REDACTION_PLACEHOLDER = "\u00ABredacted:oversized\u00BB";
|
|
86
88
|
/** S-107 窄范围重扫 [medium]:`redactSecrets` 对 PEM 起始栅栏密集的多 MB 文案**不抛但慢**(实测 1MB 1.4s / 8MB 7.9s 同步阻塞
|
|
87
89
|
* 事件循环),而终态座每次写要扫两遍(appendEvent + setTerminal 入口)、每次读再扫一遍。超过本上限的自由文本**整段**换占位
|
|
@@ -111,20 +113,63 @@ export declare function redactErrorMessage(raw: string | undefined): string | un
|
|
|
111
113
|
* ⚠️ **命中那一次仍然给剥后文本**(S-103 的「先扫描后渲染」不动摇):把零宽字符塞进密钥中间的那条文本,
|
|
112
114
|
* 渲染的必须是剥后那份,否则消费端把残片拼回原钥。
|
|
113
115
|
* ② **扫描器抛 ⇒ 登记过的占位(fail-closed),不把整条 run 拖下水**(codex r1 [high],亲核实测后修)。
|
|
114
|
-
* `redactSecrets` 的 JSON
|
|
116
|
+
* 立案时 `redactSecrets` 的 JSON 秘密字段臂含多字符循环体(`(?:\\.[^"\\]*)*`),实测
|
|
115
117
|
* `{"password":"<400 万个 \\n 转义对>"}`(8MB)抛 `RangeError`。`result` 接进来之前这条路不存在;接进来之后,
|
|
116
118
|
* 裸调用会让异常穿过 `redactTerminalResult` → 三孪生 `setTerminal`,**一条跑成功的 run 落成 failed + result:null**
|
|
117
119
|
* (实测读数)。⇒ 与本文件既有的 {@link redactErrorMessage} / `redactDiagnosticText` / `redactArtifactText`
|
|
118
120
|
* 三座**同一条**处置:抛就换占位、计数、留痕([ref] 的 loud,不是静默 fail-open)。丢的是这一条病态文本,
|
|
119
|
-
* 不是这条 run。根因(那条正则)
|
|
121
|
+
* 不是这条 run。根因(那条正则)由 S-265(7.105.0)换成手写线性扫描器闭掉(同批闭掉同类第二例 PEM 栅栏标签循环);
|
|
122
|
+
* 本处置**照留** —— 理由同 {@link OVERSIZED_REDACTION_PLACEHOLDER} 顶注末句。
|
|
120
123
|
* ③ **不设体量上限**(裁定):`result` 住在 `result` JSON/JSONB 列(亲核 `plugins/tidb-pool.ts` 与
|
|
121
124
|
* `plugins/pg-pool.ts` 的 DDL),数 MB 终稿是正常形 —— {@link redactErrorMessage} 的两道上限是为
|
|
122
125
|
* `task_run.error` 那一列 `TEXT(65 535 字节)` 定的,套到这里等于用静默的数据损坏换一次脱敏。
|
|
123
126
|
*/
|
|
124
127
|
export declare function redactModelText(raw: string): string;
|
|
125
|
-
/**
|
|
126
|
-
*
|
|
127
|
-
|
|
128
|
+
/**
|
|
129
|
+
* 🔴 **provider 回声字段的形状闸 —— 唯一铸点**(S-283,7.105.0;test [ref] 附带;动作经 S-889 与 core 源头闸归一)。
|
|
130
|
+
*
|
|
131
|
+
* **回声字段是什么**:模型服务商(上游模型 API / 中间网关)在响应里给、引擎**逐字转交**的不透明标识 ——
|
|
132
|
+
* `apiFailure.requestId`(响应头 `request-id` / `x-request-id`,core `brain/errors.js` 的 `readProviderRequestId`)、
|
|
133
|
+
* `responseModel`(服务商自报的模型名,与账本 `turn_end.responseModel` 是孪生)、工具调用 id(`strandedHumanAnswers[].toolCallId` /
|
|
134
|
+
* `stats.humanReview.gates[].toolCallId`;core `brain/openai.js` / `brain/anthropic.js` 取 `tc.id` / `cb.id`,只在缺席时
|
|
135
|
+
* `mintFallbackToolCallId` 自铸兜底)。字节由**部署方接的那个端点**决定,本栈一个都没铸 ⇒ **不可信**。
|
|
136
|
+
*
|
|
137
|
+
* 🔴 **本闸只施加在「这个值出现的每一张脸都被覆盖到」的回声上**(codex r1 F1,实测成立的回归后改判):工具调用 id 是
|
|
138
|
+
* **跨面 join 键** —— 同一枚 id 同时逐字出现在 `tool_start` / `tool_end` / 审批卡等逐帧面上。只在结果面动它 ⇒ 结果面与逐帧面
|
|
139
|
+
* join 不上。所以它的处置词是 `"provider-join-key"`:信任类仍是 provider 回声,但形状闸只能在**全部面的共同入口**一次施加
|
|
140
|
+
* (独立一车,已登记为债),本面逐字。`requestId` / `responseModel` 的每一张脸(结果面四处 + 账本孪生 + 子代通告 + `POST /v1/side-query` 200 体的 `responseModel`,
|
|
141
|
+
* 后者 7.105.0 合并复审 X1 补上 —— `http/routes/side-query.ts` 的 `shapeSideQueryEcho`)本闸都覆盖到了。
|
|
142
|
+
*
|
|
143
|
+
* **只做形状校验,不过脱敏器**(本车裁,与 S-270 / clay [ref] ② 的 `structuredOutput` 逐字回声面同一方向):回声值
|
|
144
|
+
* 语义上是**不透明 id**(工单号 / join 键),脱敏器的值臂会把形似密钥的 id 改坏(`sk-…` 形的 id 整段成 `«redacted:key»`),
|
|
145
|
+
* 而 id 被改坏 = 下游 join / 支持工单对不上;该防的是**形**(超长串撑爆终局体与日志、控制字符 / 终端转义序列改写
|
|
146
|
+
* 显示、bidi / 非 ASCII 冒充),不是「里面像不像密钥」。
|
|
147
|
+
*
|
|
148
|
+
* 🔴 **形状与动作 = core 源头闸同一条规则**(S-889,主会话裁;core **#1204**,原排 7.34.1、随 7.35.0 到货,包根导出 `PROVIDER_ECHO_SHAPE` +
|
|
149
|
+
* `isProviderEchoShape(value: string): boolean`,字母表采本仓提出的这一张):
|
|
150
|
+
* · **形内** = 字符串、长度 1–128、每个码元都是可见 ASCII(0x21–0x7E;空格、空串与一切控制 / 非 ASCII 码元都在外)⇒ 原样;
|
|
151
|
+
* · **越界**(含非串值)⇒ **缺席**(键删掉 / 不铸)+ 计数 `server.run-terminal.provider-echo-shaped`(detail 只带座位与原长 / 类型,
|
|
152
|
+
* **不带值**)。不截断、不带记号 —— 首版的「截成 127 + `…`」与 core 的「越界 ⇒ 缺席」是同一个键上两种处置,那一形 7.105.0 不发;
|
|
153
|
+
* 截断记号、「截过的形是不动点」那套逻辑随之删除(规则变少)。缺席本来就是这几个键在 wire 上的一等状态(`apiFailure` 留下
|
|
154
|
+
* `{}` = 「provider 故障、无可补充标签」,core 契约原句),消费端不需要新读法。
|
|
155
|
+
* · `undefined` 与**空串** = 缺席本身(服务商什么都没说),不计数、不铸键 —— 空串一臂是 S-877 / core 7.35.0 #1204 的读法
|
|
156
|
+
* (`brain/errors.d.ts` 的 `providerEchoReading` 逐字「a non-string or the empty string states nothing (`absent` — no field,
|
|
157
|
+
* nothing counted)」;修前本仓把空串算越界计一次)。非串值本仓**照旧计数**:core 那句说的是 brain 读服务商原始响应,本口读的是
|
|
158
|
+
* 持久行 / 非 core 的 Runner 给的形,一个非串的回声座是坏行,响亮([ref])。
|
|
159
|
+
* ✅ 谓词 = core 包根导出的 `isProviderEchoShape`(S-877 起换调;本地那两行定义已删 —— 字母表同源,本仓不留第二份)。
|
|
160
|
+
* 🔴 **core 7.35.0 起 core 的 brain 在写侧先判一次**(`brain/stream-shared.js` `noteResponseModel` / `brain/errors.js`
|
|
161
|
+
* `readProviderRequestId`:越界 ⇒ 不写、run 里发 trace `brain.echo_dropped {field}`)⇒ 真 brain 链上越界值到不了本口,本口的
|
|
162
|
+
* 计数只剩「不经 core brain 写侧」的 producer(存量行、宿主自己的 Brain / 自接 `sideQuery` 席)。
|
|
163
|
+
* **不进模型上下文**:本仓没有任何一处把这几片回声喂进提示词(它们只上 wire / 落库 / 进日志)——这一闸管的就是那几面。
|
|
164
|
+
*/
|
|
165
|
+
/** 回声座上的值留不留:形内 ⇒ `true`(原样留);`undefined` / 空串 ⇒ `false`(缺席本身,不计数);其余越界 ⇒ `false` + 计数。
|
|
166
|
+
* 调用方按 `false` 删键 / 不铸键 —— 本函数是全部回声座(结果面 / 账本孪生 / 子代通告 / side-query 200 体)的**唯一**判决口。 */
|
|
167
|
+
export declare function admitProviderEcho(value: unknown, seat: string): value is string;
|
|
168
|
+
/** {@link redactTerminalResult} 会碰的全部顶层键 = 现行形里**不是** `"verbatim"` 的行(`"redacted"` ∪ S-283 的
|
|
169
|
+
* `"provider-echo"`)∪ 旧形平面键;`memoRedactTerminalResult` 的记忆键由它派生(两处同源,见 `RESULT_KEY_TREATMENT` 顶注)。
|
|
170
|
+
* 🔴 S-283 起改名(原 `REDACTABLE_RESULT_LEAVES`):回声键不脱敏但**会被改**(越界 ⇒ 删键),漏进记忆键集 = 读面记忆腿对存量行
|
|
171
|
+
* O(1) 原样直返,读面纵深对那几片恒不生效([ref] 立案时 `editedFiles` 那一格的同一个病)。 */
|
|
172
|
+
export declare const TREATED_RESULT_LEAVES: readonly string[];
|
|
128
173
|
export declare function redactTerminalResult<T extends {
|
|
129
174
|
terminal?: unknown;
|
|
130
175
|
/** 旧形(≤7.63.0)的三个顶层座;形上取 `unknown` —— 读盘面对盘上字节不作假设。 */
|
|
@@ -144,6 +189,10 @@ export declare function redactTerminalResult<T extends {
|
|
|
144
189
|
editedFiles?: unknown;
|
|
145
190
|
/** S-394:形上同样 `unknown` —— 本函数是读盘面,`stats` 的树形逐层现判(见 {@link redactStats})。 */
|
|
146
191
|
stats?: unknown;
|
|
192
|
+
/** S-283:provider 回声三键(形上 `unknown`,读盘面纪律同上;处置见 {@link PROVIDER_ECHO_SHAPERS})。 */
|
|
193
|
+
apiFailure?: unknown;
|
|
194
|
+
strandedHumanAnswers?: unknown;
|
|
195
|
+
responseModel?: unknown;
|
|
147
196
|
} | null | undefined>(result: T): T;
|
|
148
197
|
/**
|
|
149
198
|
* 🔴 **S-173 —— run 行 `result` 列的唯一入口**:先过出口封口({@link sealResultForExit}:摘恢复凭据 + 折域外码),
|
|
@@ -202,7 +251,7 @@ export declare function redactLeaderResultWithFlag(result: unknown): {
|
|
|
202
251
|
changed: boolean;
|
|
203
252
|
};
|
|
204
253
|
export declare function memoRedactedRead<T>(seat: string, raw: T, redact: (v: T) => T): T;
|
|
205
|
-
/** run 终态 result
|
|
254
|
+
/** run 终态 result 的读面记忆只按**本函数会改字节的那几叶**({@link TREATED_RESULT_LEAVES}:脱敏叶 ∪ S-283 回声叶 ∪ 旧形平面键,属主是
|
|
206
255
|
* `RESULT_KEY_TREATMENT`)做键与留体——
|
|
207
256
|
* 第十六轮重扫:按整块 result 摘要每次轮询要 stringify 一份可能数 MB 的 result,且大 result 的脏行永远存不进去。叶全缺席
|
|
208
257
|
* ⇒ O(1) 直返(`redactTerminalResult` 本就同引用)。
|
|
@@ -215,6 +264,30 @@ export declare function memoRedactedRead<T>(seat: string, raw: T, redact: (v: T)
|
|
|
215
264
|
* `editedFiles` 那一格的病)。读数:8MB result 的一次摘要 ~10ms 量级,远小于一次全扫;干净行仍只摘要
|
|
216
265
|
* 一次即进 clean 集,脏行超过 {@link READ_MEMO_DIRTY_MAX_ENTRY_BYTES} 时退化为每次重扫(既有形)。 */
|
|
217
266
|
export declare function memoRedactTerminalResult<T extends Parameters<typeof redactTerminalResult>[0]>(seat: string, result: T): T;
|
|
267
|
+
/**
|
|
268
|
+
* 🔴 **账本行的读面口 —— 全部耐久读腿共用这一只**(S-269,7.105.0)。
|
|
269
|
+
*
|
|
270
|
+
* 脱敏的**属主**仍是 {@link redactLedgerEventData}(写读共用的唯一汇点,规则一张:哪些行型有自由文本座、怎么脱);
|
|
271
|
+
* 本函数**不是第二只脱敏器**,只在汇点外面加一层**按型记忆**(读面每次轮询 / 每个观看者 / 每次重连都重放同一批
|
|
272
|
+
* 不可变的行):
|
|
273
|
+
* · `done` 行 —— 只按 `result` 的可脱敏叶记忆({@link memoRedactTerminalResult};整块 result 可能数 MB);
|
|
274
|
+
* · `failed` 行与模型自由文本两行(`text` / `reasoning`,词表属主 `trace/ledger-events.ts`)—— 按内容摘要记忆({@link memoRedactedRead});
|
|
275
|
+
* · 其余行型 —— 直调汇点(park 行只有一个小 gate,扫一次的代价远低于摘要 + 记忆表位;其余型汇点逐字透传)。
|
|
276
|
+
*
|
|
277
|
+
* **三条耐久读腿同调**:`GET /v1/runs/:id/events`(账本裸回放)、`GET /v1/tasks/:id/turns`(`projectEvents`)、
|
|
278
|
+
* trace SSE `GET /v1/tasks/:id/stream`(`mapTraceEvent`)。此前只有第一条在 `formatEvent` 里手写了这段分派,后两条
|
|
279
|
+
* **直吃原行** —— 新行在三孪生 `appendEvent` 落库前已脱,所以只打在**存量行**上:7.75.1(B-106)之前 `text` 行的写口
|
|
280
|
+
* 不脱,那批行在两条 trace 腿上原字节回放(S-269,车EC U-2)。分派收进本函数、三条腿同调 ⇒ 读面纵深一条规则。
|
|
281
|
+
*
|
|
282
|
+
* **性能取舍(读实现后定)**:汇点对已脱行在**输出**上幂等(`redactModelText` 认不出凭据形就回同一引用),但**代价不是
|
|
283
|
+
* 空过** —— 每次都整段重扫(`redactSecrets` 全池一遍)。所以读面走记忆:同一行第二次起只付一次 sha256 摘要 + 集合查找
|
|
284
|
+
* (车QO 本机实测,8.6MB 散文 min-of-5:整段重扫 112ms vs 摘要〔含 `JSON.stringify`〕31ms,约 3.6×);记忆表有界(干净 8192 条 / 脏 512 条且总字节封顶,见 {@link memoRedactedRead})。
|
|
285
|
+
* **没**选「行上打已脱标记 ⇒ 跳过」:那要在三孪生写口加一个持久字段,而需要读面纵深的恰恰是**没有**标记的存量行 ——
|
|
286
|
+
* 标记只能省新行的扫描;记忆对新旧行一视同仁,且不新开一张持久面。
|
|
287
|
+
* trace 两腿对 `done` 行只投 `seq`(不投 `result`),它的摘要是**白付**的那一份;**刻意**不按「这条腿投哪些键」裁掉 ——
|
|
288
|
+
* 那等于在读腿里手抄一份汇点的行型表([ref]),汇点将来加一种带自由文本的行型时读腿静默漏。
|
|
289
|
+
*/
|
|
290
|
+
export declare function redactLedgerEventForRead(type: string, data: unknown): unknown;
|
|
218
291
|
export declare function diffScanShadowCountForTest(): number;
|
|
219
292
|
export declare function resetDiffScanShadowCountForTest(): void;
|
|
220
293
|
export declare function resetReadRedactionMemoForTest(): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { redactSecrets, normalizeForRedaction } from "../trace/redact.js";
|
|
3
|
+
import { isProviderEchoShape } from "@sema-agent/core";
|
|
3
4
|
import { isModelTextLedgerEventType, isParkLedgerEventType } from "../trace/ledger-events.js";
|
|
4
5
|
import { isUnsuccessfulRunStatus } from "../plugins/store-contracts.js";
|
|
5
6
|
import { sealResultForExit } from "../terminal.js";
|
|
@@ -97,28 +98,68 @@ export function redactModelText(raw) {
|
|
|
97
98
|
return OVERSIZED_REDACTION_PLACEHOLDER;
|
|
98
99
|
}
|
|
99
100
|
}
|
|
101
|
+
export function admitProviderEcho(value, seat) {
|
|
102
|
+
if (value === undefined || value === "")
|
|
103
|
+
return false;
|
|
104
|
+
if (typeof value === "string" && isProviderEchoShape(value))
|
|
105
|
+
return true;
|
|
106
|
+
const what = typeof value === "string" ? `len=${value.length}` : `type=${value === null ? "null" : typeof value}`;
|
|
107
|
+
recordFailOpen("server.run-terminal.provider-echo-shaped", `seat=${seat} ${what}`);
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
function shapeEchoMembers(node, table, seat) {
|
|
111
|
+
if (node === null || typeof node !== "object" || Array.isArray(node))
|
|
112
|
+
return node;
|
|
113
|
+
const n = node;
|
|
114
|
+
let out;
|
|
115
|
+
for (const [key, treatment] of Object.entries(table)) {
|
|
116
|
+
const v = n[key];
|
|
117
|
+
if (v === undefined)
|
|
118
|
+
continue;
|
|
119
|
+
switch (treatment) {
|
|
120
|
+
case "verbatim":
|
|
121
|
+
case "provider-join-key":
|
|
122
|
+
break;
|
|
123
|
+
case "provider-echo":
|
|
124
|
+
if (!admitProviderEcho(v, `${seat}.${key}`))
|
|
125
|
+
delete (out ??= { ...n })[key];
|
|
126
|
+
break;
|
|
127
|
+
default: {
|
|
128
|
+
const unhandled = treatment;
|
|
129
|
+
throw new Error(`unhandled echo-member treatment: ${String(unhandled)}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return out ?? node;
|
|
134
|
+
}
|
|
135
|
+
function shapeEchoRows(rows, table, seat) {
|
|
136
|
+
if (!Array.isArray(rows))
|
|
137
|
+
return rows;
|
|
138
|
+
const next = rows.map((row) => shapeEchoMembers(row, table, seat));
|
|
139
|
+
return next.every((x, i) => x === rows[i]) ? rows : next;
|
|
140
|
+
}
|
|
100
141
|
const MAX_EDITED_FILES = 1000;
|
|
101
142
|
const RESULT_KEY_TREATMENT = {
|
|
102
143
|
terminal: "redacted",
|
|
103
144
|
remoteEnvFailures: "redacted",
|
|
104
145
|
editedFiles: "redacted",
|
|
146
|
+
responseModel: "provider-echo",
|
|
147
|
+
apiFailure: "provider-echo",
|
|
148
|
+
strandedHumanAnswers: "provider-echo",
|
|
105
149
|
taskId: "verbatim",
|
|
106
150
|
runId: "verbatim",
|
|
107
151
|
sessionId: "verbatim",
|
|
108
152
|
model: "verbatim",
|
|
109
|
-
responseModel: "verbatim",
|
|
110
153
|
compaction: "verbatim",
|
|
111
154
|
effectiveReasoning: "verbatim",
|
|
112
155
|
result: "redacted",
|
|
113
156
|
salvagedOutput: "redacted",
|
|
114
|
-
apiFailure: "verbatim",
|
|
115
157
|
retryAfterMs: "verbatim",
|
|
116
158
|
degraded: "verbatim",
|
|
117
159
|
structuredOutput: "verbatim",
|
|
118
160
|
rewindNotes: "redacted",
|
|
119
161
|
haltedOnUserRejection: "verbatim",
|
|
120
162
|
haltedByUser: "verbatim",
|
|
121
|
-
strandedHumanAnswers: "verbatim",
|
|
122
163
|
pendingApproval: "verbatim",
|
|
123
164
|
approvalWaitedMs: "verbatim",
|
|
124
165
|
effectiveReadFace: "verbatim",
|
|
@@ -127,9 +168,28 @@ const RESULT_KEY_TREATMENT = {
|
|
|
127
168
|
effectiveMemoryScopes: "verbatim",
|
|
128
169
|
stats: "redacted",
|
|
129
170
|
};
|
|
171
|
+
const API_FAILURE_TREATMENT = {
|
|
172
|
+
status: "verbatim",
|
|
173
|
+
requestId: "provider-echo",
|
|
174
|
+
};
|
|
175
|
+
const STRANDED_ANSWER_TREATMENT = {
|
|
176
|
+
deliveryId: "verbatim",
|
|
177
|
+
toolCallId: "provider-join-key",
|
|
178
|
+
};
|
|
179
|
+
const PROVIDER_ECHO_SHAPERS = {
|
|
180
|
+
responseModel: (v) => (admitProviderEcho(v, "responseModel") ? v : undefined),
|
|
181
|
+
apiFailure: (v) => shapeEchoMembers(v, API_FAILURE_TREATMENT, "apiFailure"),
|
|
182
|
+
strandedHumanAnswers: (v) => shapeEchoRows(v, STRANDED_ANSWER_TREATMENT, "strandedHumanAnswers[]"),
|
|
183
|
+
};
|
|
184
|
+
{
|
|
185
|
+
const judged = Object.entries(RESULT_KEY_TREATMENT).filter(([, t]) => t === "provider-echo").map(([k]) => k).sort();
|
|
186
|
+
const wired = Object.keys(PROVIDER_ECHO_SHAPERS).sort();
|
|
187
|
+
if (judged.join(",") !== wired.join(","))
|
|
188
|
+
throw new Error(`run-terminal: provider-echo classification [${judged.join(",")}] ≠ shaper table [${wired.join(",")}]`);
|
|
189
|
+
}
|
|
130
190
|
const LEGACY_RESULT_LEAVES = ["errorMessage", "blockedReason", "checkpointGate"];
|
|
131
|
-
export const
|
|
132
|
-
...Object.entries(RESULT_KEY_TREATMENT).filter(([, t]) => t
|
|
191
|
+
export const TREATED_RESULT_LEAVES = [
|
|
192
|
+
...Object.entries(RESULT_KEY_TREATMENT).filter(([, t]) => t !== "verbatim").map(([k]) => k),
|
|
133
193
|
...LEGACY_RESULT_LEAVES,
|
|
134
194
|
];
|
|
135
195
|
export function redactTerminalResult(result) {
|
|
@@ -235,6 +295,20 @@ export function redactTerminalResult(result) {
|
|
|
235
295
|
changed = true;
|
|
236
296
|
}
|
|
237
297
|
}
|
|
298
|
+
const rr = result;
|
|
299
|
+
for (const [key, shape] of Object.entries(PROVIDER_ECHO_SHAPERS)) {
|
|
300
|
+
const v = rr[key];
|
|
301
|
+
if (v === undefined || shape === undefined)
|
|
302
|
+
continue;
|
|
303
|
+
const next = shape(v);
|
|
304
|
+
if (next === v)
|
|
305
|
+
continue;
|
|
306
|
+
if (next === undefined)
|
|
307
|
+
delete out[key];
|
|
308
|
+
else
|
|
309
|
+
out[key] = next;
|
|
310
|
+
changed = true;
|
|
311
|
+
}
|
|
238
312
|
return changed ? out : result;
|
|
239
313
|
}
|
|
240
314
|
export function sealRunRowResult(result) {
|
|
@@ -267,8 +341,18 @@ export function redactLedgerEventData(type, data) {
|
|
|
267
341
|
const next = redactCheckpointGate(gate);
|
|
268
342
|
return next === gate ? data : { ...data, gate: next };
|
|
269
343
|
}
|
|
344
|
+
if (type === "turn_end")
|
|
345
|
+
return shapeEchoMembers(data, TURN_END_ECHO_TREATMENT, "turn_end");
|
|
346
|
+
if (type === "task_notification") {
|
|
347
|
+
const af = data.apiFailure;
|
|
348
|
+
if (af === undefined)
|
|
349
|
+
return data;
|
|
350
|
+
const next = shapeEchoMembers(af, API_FAILURE_TREATMENT, "task_notification.apiFailure");
|
|
351
|
+
return next === af ? data : { ...data, apiFailure: next };
|
|
352
|
+
}
|
|
270
353
|
return data;
|
|
271
354
|
}
|
|
355
|
+
const TURN_END_ECHO_TREATMENT = { responseModel: "provider-echo" };
|
|
272
356
|
export function redactWorkerReportError(report) {
|
|
273
357
|
const gate = redactCheckpointGate(report.checkpointGate);
|
|
274
358
|
const error = report.error === undefined ? undefined : (redactErrorMessage(report.error) ?? "");
|
|
@@ -389,7 +473,7 @@ const STATS_GATE_ROW_TREATMENT = {
|
|
|
389
473
|
decision: "verbatim",
|
|
390
474
|
toolName: "verbatim",
|
|
391
475
|
toolArg: "model-text",
|
|
392
|
-
toolCallId: "
|
|
476
|
+
toolCallId: "provider-join-key",
|
|
393
477
|
};
|
|
394
478
|
const STATS_REPETITION_EVENT_TREATMENT = {
|
|
395
479
|
turn: "verbatim",
|
|
@@ -420,6 +504,8 @@ function redactStatsNode(node, table) {
|
|
|
420
504
|
switch (treatment) {
|
|
421
505
|
case "verbatim":
|
|
422
506
|
break;
|
|
507
|
+
case "provider-join-key":
|
|
508
|
+
break;
|
|
423
509
|
case "model-text":
|
|
424
510
|
if (typeof v === "string")
|
|
425
511
|
next = redactModelText(v);
|
|
@@ -674,15 +760,32 @@ export function memoRedactTerminalResult(seat, result) {
|
|
|
674
760
|
if (result === undefined || result === null)
|
|
675
761
|
return result;
|
|
676
762
|
const r = result;
|
|
677
|
-
if (
|
|
763
|
+
if (TREATED_RESULT_LEAVES.every((k) => r[k] === undefined))
|
|
678
764
|
return result;
|
|
679
765
|
const leaves = {};
|
|
680
|
-
for (const k of
|
|
766
|
+
for (const k of TREATED_RESULT_LEAVES)
|
|
681
767
|
leaves[k] = r[k];
|
|
682
768
|
const redacted = memoRedactedRead(seat, leaves, (l) => redactTerminalResult(l));
|
|
683
769
|
if (redacted === leaves)
|
|
684
770
|
return result;
|
|
685
|
-
|
|
771
|
+
const merged = { ...result, ...redacted };
|
|
772
|
+
for (const k of TREATED_RESULT_LEAVES)
|
|
773
|
+
if (!Object.hasOwn(redacted, k))
|
|
774
|
+
delete merged[k];
|
|
775
|
+
return merged;
|
|
776
|
+
}
|
|
777
|
+
export function redactLedgerEventForRead(type, data) {
|
|
778
|
+
if (type === "done" && data !== null && typeof data === "object") {
|
|
779
|
+
const result = data.result;
|
|
780
|
+
if (result && typeof result === "object") {
|
|
781
|
+
return { ...data, result: memoRedactTerminalResult("ev.done.result", result) };
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (type === "failed")
|
|
785
|
+
return memoRedactedRead("ev.failed", data, (d) => redactLedgerEventData(type, d));
|
|
786
|
+
if (isModelTextLedgerEventType(type))
|
|
787
|
+
return memoRedactedRead("ev.model-text", data, (d) => redactLedgerEventData(type, d));
|
|
788
|
+
return redactLedgerEventData(type, data);
|
|
686
789
|
}
|
|
687
790
|
let shadowScans = 0;
|
|
688
791
|
export function diffScanShadowCountForTest() { return shadowScans; }
|
|
@@ -27,7 +27,7 @@ export const SECRET_ENV_SCRUB_SITES = {
|
|
|
27
27
|
},
|
|
28
28
|
};
|
|
29
29
|
const SCRUB_COUNTER = "secret_env_scrubbed_total";
|
|
30
|
-
const SCRUB_COUNTER_HELP = "secret env entries not handed on as written (dropped
|
|
30
|
+
const SCRUB_COUNTER_HELP = "secret env entries not handed on as written (dropped) to a spawned child process, by site/kind/confidence (core scrubSecretEnv rules + server config-catalog secret names + name-list knobs)";
|
|
31
31
|
const DISCLOSED_CAP = 512;
|
|
32
32
|
const SEP = "=";
|
|
33
33
|
let sink = {};
|
|
@@ -14,6 +14,8 @@ import type { Metrics } from "./metrics.js";
|
|
|
14
14
|
* 🔴 BREAKING(server 7.64.0,core 7.6.0 提货):标签名 `source` → `deniedBy`,词表从七词
|
|
15
15
|
* (`policy/hook/safety/shellGate/planMode/classifier/org`)换成八词
|
|
16
16
|
* (`policy/hook/org/classifier/plan_mode/compliance/write_protection/ask_resolution`)。仪表盘/告警规则要改。
|
|
17
|
+
* 🔁 server 7.106.0(S-877 / core 7.34.0 [ref]):`plan_mode` 这个词退役,换成 `mode`(模式自己拒的那一形:`dontAsk` 拒每一只非授权 ask、
|
|
18
|
+
* `bypassPermissions` 拒判不出保护的写)—— 标签值原样取 core 的 `isDeniedBy`,按 `deniedBy="plan_mode"` 写的告警规则改 `mode`。
|
|
17
19
|
*
|
|
18
20
|
* 🔴 **第二个计数器 `auto_mode_classifier_deny_cause_total{cause}`(server 7.70.0 生为
|
|
19
21
|
* `auto_mode_classifier_unavailable_total`,core 7.12.0 [ref];**7.72.0 / core 7.14.0 [ref] 改名、换座、换词**,见本段末)**
|
package/dist/operator-ask.d.ts
CHANGED
|
@@ -34,6 +34,10 @@
|
|
|
34
34
|
* · **问答工具**(`deployment-governance.ts` 的 `AskUserQuestion`)—— core 的谓词把保留问答工具单列豁免。
|
|
35
35
|
*/
|
|
36
36
|
import type { PermissionResult } from "@sema-agent/core";
|
|
37
|
+
/** 本文件两只铸点的返回形 = core `PermissionResult` 的 ask 臂(窄型:`authoredBy` 只长在这一臂上)。 */
|
|
38
|
+
export type OperatorAskResult = Extract<PermissionResult, {
|
|
39
|
+
action: "ask";
|
|
40
|
+
}>;
|
|
37
41
|
/**
|
|
38
42
|
* 铸一条**运维声明产生的** `ask`。
|
|
39
43
|
*
|
|
@@ -42,5 +46,29 @@ import type { PermissionResult } from "@sema-agent/core";
|
|
|
42
46
|
* request」,所以它既不上 wire、也不进卡,写的必须是**真实的那条声明**而不是一句好看的话。
|
|
43
47
|
* @param message 给人看的那句话(照旧上卡)。
|
|
44
48
|
*/
|
|
45
|
-
export declare function operatorAsk(rule: string, message: string):
|
|
49
|
+
export declare function operatorAsk(rule: string, message: string): OperatorAskResult;
|
|
50
|
+
/**
|
|
51
|
+
* 🔴 **S-877 候裁 16 —— 运维声明的「恒问人」**(`APPROVAL_NEVER_AUTO` 的「always human」、耐久资源预算耗尽的「human approval required」):
|
|
52
|
+
* 在 {@link operatorAsk} 之上再盖部署出身 `authoredBy: "deployment"` ⇒ core 类读 `operator_forced`(`ruleClearanceOf`:
|
|
53
|
+
* 「a deployment-authored ask」进强制门 ⇒ `mandated`)—— bypassPermissions 放不掉、**任何**站着的答案(持久规则、会话授权,
|
|
54
|
+
* 任何 scope)退不掉,只有**这一次**的应答能答它。
|
|
55
|
+
*
|
|
56
|
+
* **为什么现在需要**:修前这两类 ask 挡住会话授权靠的是策略层的**次序**(`createDurableAskPolicy` 先判 neverAuto / 预算耗尽,再查
|
|
57
|
+
* 豁免探针)。候裁 16 把会话授权的兑现位收到**审批席**(协调器 `standingGrantSettles`,判据 = `standingAnswerMayRetire`),而审批席
|
|
58
|
+
* 读不到 `matchedAskRule`(core 刻意不上卡)—— 不盖出身,一条 `remember:"session"` 就会在审批席上退役「恒问人」。盖了之后
|
|
59
|
+
* 席的同一只判据按 `mandated` 挡住它,耐久 `/decide` 腿的可记性(park 行 `mandated` 孪生位)同判 ⇒ 回执 `rememberApplied:false`。
|
|
60
|
+
* live 腿修前这里本就是个洞(进程内 `allow_session` 授权可退役 neverAuto 卡),随类收掉。
|
|
61
|
+
*
|
|
62
|
+
* ⚠️ **只**这两类:普通 `APPROVAL_REQUIRE` 仍只经 {@link operatorAsk}(候裁 8 裁 A —— 用户「本会话放行」退役它们是产品设计,不收紧)。
|
|
63
|
+
*/
|
|
64
|
+
export declare function operatorAlwaysHumanAsk(rule: string, message: string): OperatorAskResult;
|
|
65
|
+
/**
|
|
66
|
+
* core 部署出身位 `authoredBy:"deployment"` 的**唯一**盖位函数(S-897 起收成一处):给一只**部署自己的声明**产的 ask 盖上出身 ⇒
|
|
67
|
+
* core 类读 `operator_forced`、`mandated`、不铸 `mandate` 词(表外的门;缺词 = 另有门在)⇒ bypassPermissions 放不掉、站着的答案
|
|
68
|
+
* 退不掉、壳也不会按单个强制词替人答。三个调用者:{@link operatorAlwaysHumanAsk}(`APPROVAL_NEVER_AUTO` / 预算耗尽)、
|
|
69
|
+
* 治理层包装(`runtime-governance.ts` 的 `createGovernanceAskMarkingPolicy`)、SUP 路由姿态(`router/route-orchestration.ts`,
|
|
70
|
+
* 经 {@link operatorAlwaysHumanAsk})。修前前两处各写一份 `{ ...d, authoredBy: "deployment" }`(两个写者),S-897 若再抄就是第三份。
|
|
71
|
+
* 只作用于 ask 臂(类型上就只收 ask);其余键逐字透传。
|
|
72
|
+
*/
|
|
73
|
+
export declare function withDeploymentAuthorship<A extends OperatorAskResult>(ask: A): A;
|
|
46
74
|
//# sourceMappingURL=operator-ask.d.ts.map
|
package/dist/operator-ask.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
export function operatorAsk(rule, message) {
|
|
2
2
|
return { action: "ask", message, decisionReason: "rule", matchedAskRule: rule };
|
|
3
3
|
}
|
|
4
|
+
export function operatorAlwaysHumanAsk(rule, message) {
|
|
5
|
+
return withDeploymentAuthorship(operatorAsk(rule, message));
|
|
6
|
+
}
|
|
7
|
+
export function withDeploymentAuthorship(ask) {
|
|
8
|
+
return { ...ask, authoredBy: "deployment" };
|
|
9
|
+
}
|
|
4
10
|
//# sourceMappingURL=operator-ask.js.map
|
package/dist/parked-decide.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 行永 parked、生命周期分叉)。
|
|
10
10
|
*/
|
|
11
11
|
import type { HostDecision } from "./host-decision.js";
|
|
12
|
-
import type { BackgroundAgentRecord, BackgroundAgentStore, CheckpointStore, QuestionAnswer, ResumeOutcome, TaskRegistry, WorkflowRun, WorkflowRunStore } from "@sema-agent/core";
|
|
12
|
+
import type { BackgroundAgentRecord, BackgroundAgentStore, CheckpointStore, QuestionAnswer, ResumeOutcome, TaskRegistry, ToolExecuteContext, WorkflowRun, WorkflowRunStore } from "@sema-agent/core";
|
|
13
13
|
import type { ParkedReviveSurfaceFactory } from "./parked-revive-surface.js";
|
|
14
14
|
import type { RebuiltInheritedGate } from "./boot/parked-revive-gate.js";
|
|
15
15
|
import { type WorkflowRunParkKey, type WorkflowAgentSessionIndex } from "./orchestration/workflow-agent-session-index.js";
|
|
@@ -142,6 +142,19 @@ export interface ParkedDecideDeps {
|
|
|
142
142
|
rebuildInheritedGate?: (row: BackgroundAgentRecord, identity: {
|
|
143
143
|
principal: string | undefined;
|
|
144
144
|
}) => Promise<RebuiltInheritedGate | undefined>;
|
|
145
|
+
/**
|
|
146
|
+
* 🔴 S-877 四续 ⑪ —— 复活子代**自己的**审批席(`ToolExecuteContext.onAsk`:core 的委派工具把它转给子代,子代自己的引擎问 —— 模式写门 /
|
|
147
|
+
* 形状层 —— 在这里答)。本腿直接调 `execute`,不经 Runner,ctx 由本腿铸;不给 ⇒ 子代自己的问落 subRunner 的 `deps.onAsk`(协调器
|
|
148
|
+
* `ask()` 的无 ALS 上下文臂,没有会话身份 ⇒ 读不了会话授权 ⇒ 每条非只读命令 / 每次写都再 park,`remember:"session"` 对复活子代无效;
|
|
149
|
+
* 子代活着时同一问落父的席、带身份、读得到授权 —— 同一个子代两种答案)。生产 = 协调器的窗 0 席 `boundUnattendedAsk`(与 bg run 腿给父任务
|
|
150
|
+
* 装的同一只):身份 = 行的根会话(与 remember 的授权锚同键 {@link grantRootOf})+ 权威 principal;授权读不中 ⇒ 无人值守政策口(park),
|
|
151
|
+
* 不放宽。缺席(协调器未装的部署)⇒ ctx 不带座,逐字今天。
|
|
152
|
+
*/
|
|
153
|
+
approverSeatFor?: (identity: {
|
|
154
|
+
owner: string | null;
|
|
155
|
+
taskId: string;
|
|
156
|
+
sessionId: string;
|
|
157
|
+
}) => NonNullable<ToolExecuteContext["onAsk"]>;
|
|
145
158
|
warn?: (event: string, fields: Record<string, unknown>) => void;
|
|
146
159
|
}
|
|
147
160
|
export interface ParkedDecideRequest {
|
package/dist/parked-decide.js
CHANGED
|
@@ -62,6 +62,9 @@ export async function findParkedAgentForCheckpoint(agentStore, cp) {
|
|
|
62
62
|
}
|
|
63
63
|
return undefined;
|
|
64
64
|
}
|
|
65
|
+
function grantRootOf(row) {
|
|
66
|
+
return row.rootSessionId ?? row.parentSessionId;
|
|
67
|
+
}
|
|
65
68
|
function mintReviveArgs(row, knownAgentTypes) {
|
|
66
69
|
return {
|
|
67
70
|
prompt: row.description ?? "resume parked agent",
|
|
@@ -148,7 +151,7 @@ function sessionGone(taskId) {
|
|
|
148
151
|
}
|
|
149
152
|
export async function decideParkedAgent(deps, req, match) {
|
|
150
153
|
const row = match.row;
|
|
151
|
-
const rememberRoot = req.grantRemember !== undefined ? (row
|
|
154
|
+
const rememberRoot = req.grantRemember !== undefined ? grantRootOf(row) : undefined;
|
|
152
155
|
if (req.grantRemember !== undefined && rememberRoot === undefined) {
|
|
153
156
|
return { status: 400, body: { error: "remember cannot be applied: this parked agent row carries no root/parent session anchor for the exemption", errorCode: "decide.parked_remember_unsupported", taskId: match.handle } };
|
|
154
157
|
}
|
|
@@ -234,9 +237,13 @@ export async function decideParkedAgent(deps, req, match) {
|
|
|
234
237
|
}
|
|
235
238
|
}
|
|
236
239
|
const outcome = buildParkedResumeOutcome(req);
|
|
240
|
+
const seatRoot = grantRootOf(row);
|
|
237
241
|
const ctx = {
|
|
238
242
|
toolCallId: `drv-${ticket.claimId}`,
|
|
239
243
|
...(surface.mountsCallerTool !== undefined ? { mountsCallerTool: surface.mountsCallerTool } : {}),
|
|
244
|
+
...(deps.approverSeatFor !== undefined && seatRoot !== undefined
|
|
245
|
+
? { onAsk: deps.approverSeatFor({ owner: decodeCheckpointScope(req.scope) ?? null, taskId: match.handle, sessionId: seatRoot }) }
|
|
246
|
+
: {}),
|
|
240
247
|
reviveClaim: {
|
|
241
248
|
row: { ...ticket.row, parkClaimId: ticket.claimId },
|
|
242
249
|
rev: ticket.reservedRev,
|
|
@@ -122,8 +122,6 @@ export declare class SqlImageBake {
|
|
|
122
122
|
private lineOrdExists;
|
|
123
123
|
/** Highest event seq for a bake (0 if none) — the ++seq allocator + the resume cursor head. */
|
|
124
124
|
maxSeq(bakeId: string): Promise<number>;
|
|
125
|
-
/** Earliest retained event seq (0 if none) → the SSE 416 boundary (a resume below this was evicted, P2.9). */
|
|
126
|
-
retainedFrom(bakeId: string): Promise<number>;
|
|
127
125
|
/** Events with seq strictly greater than `afterSeq` (0 for the whole stream), ascending. */
|
|
128
126
|
getEvents(bakeId: string, afterSeq: number): Promise<BakeEvent[]>;
|
|
129
127
|
/**
|
|
@@ -379,11 +379,6 @@ export class SqlImageBake {
|
|
|
379
379
|
const m = rows[0]?.m;
|
|
380
380
|
return m == null ? 0 : Number(m);
|
|
381
381
|
}
|
|
382
|
-
async retainedFrom(bakeId) {
|
|
383
|
-
const { rows } = await this.db.query(this.q("SELECT MIN(seq) AS m FROM image_bake_event WHERE bake_id = ?", "SELECT MIN(seq) AS m FROM image_bake_event WHERE bake_id = $1"), [bakeId]);
|
|
384
|
-
const m = rows[0]?.m;
|
|
385
|
-
return m == null ? 0 : Number(m);
|
|
386
|
-
}
|
|
387
382
|
async getEvents(bakeId, afterSeq) {
|
|
388
383
|
const { rows } = await this.db.query(this.q(`SELECT seq, line_ord, kind, level, ${jsonColumnText("tidb", "data")}, ts FROM image_bake_event WHERE bake_id = ? AND seq > ? ORDER BY seq ASC`, `SELECT seq, line_ord, kind, level, ${jsonColumnText("pg", "data")}, ts FROM image_bake_event WHERE bake_id = $1 AND seq > $2 ORDER BY seq ASC`), [bakeId, afterSeq]);
|
|
389
384
|
return rows.map((r) => ({
|
|
@@ -380,6 +380,7 @@ export class SqlRetentionStore {
|
|
|
380
380
|
["resume_anchor", "session_id"],
|
|
381
381
|
["approval_exemption", "session_id"],
|
|
382
382
|
["session_policy", "session_id"],
|
|
383
|
+
["session_policy_rev_mark", "session_id"],
|
|
383
384
|
...(this.opts.taskAttachmentTable ? [["task_attachment", "session_id"]] : []),
|
|
384
385
|
["file_history_tracked", "scope"],
|
|
385
386
|
["file_history_version", "scope"],
|
|
@@ -248,8 +248,9 @@ export declare class SqlRunStore {
|
|
|
248
248
|
rows: UsageRow[];
|
|
249
249
|
truncated: boolean;
|
|
250
250
|
}>;
|
|
251
|
-
/** Earliest retained event seq for a task (0 if none) → `retainedFrom`
|
|
252
|
-
*
|
|
251
|
+
/** Earliest retained event seq for a task (0 if none) → the `retainedFrom` field of `GET /v1/tasks/:id/turns`.
|
|
252
|
+
* Not an eviction boundary: the ledger has no partial head trim (the only deletion takes the whole session tree),
|
|
253
|
+
* so a value > 1 means head rows whose write failed after consuming their seq (S-211 ①). */
|
|
253
254
|
retainedFrom(taskId: string): Promise<number>;
|
|
254
255
|
/** Events with seq strictly greater than `afterSeq` (use 0 for the whole stream). */
|
|
255
256
|
getEvents(taskId: string, afterSeq: number): Promise<RunEvent[]>;
|
|
@@ -10,6 +10,7 @@ export const CENTRAL_SCHEMA_INDEXES = [
|
|
|
10
10
|
{ table: "approval_nonce", name: "idx_approval_nonce_expires", columns: ["expires_at_ms"] },
|
|
11
11
|
{ table: "sendfile_link", name: "idx_sendfile_link_scope", columns: ["scope", "id"] },
|
|
12
12
|
{ table: "session_policy", name: "idx_session_policy_session", columns: ["session_id"] },
|
|
13
|
+
{ table: "session_policy_rev_mark", name: "idx_session_policy_rev_mark_session", columns: ["session_id"] },
|
|
13
14
|
{ table: "snapshot_blob", name: "idx_snapshot_blob_created", columns: ["created_at"] },
|
|
14
15
|
{ table: "file_history_version", name: "idx_file_history_version_blob", columns: ["blob_hash"] },
|
|
15
16
|
{ table: "file_history_boundary_file", name: "idx_file_history_bf_key", columns: ["scope", "key_hash"] },
|
|
@@ -3,6 +3,7 @@ import type { Pool as MySqlPool } from "mysql2/promise";
|
|
|
3
3
|
import type { Pool as PgPool } from "pg";
|
|
4
4
|
import { type JsonColumnShape } from "./sql-json-column.js";
|
|
5
5
|
import { type SqlDriver } from "./sql-driver.js";
|
|
6
|
+
import type { Logger } from "../observability/logger.js";
|
|
6
7
|
/** `session_policy.policy_key` 的**唯一铸造点**(core 复合键的 sha256)。
|
|
7
8
|
*
|
|
8
9
|
* 🔴 导出而不是让收编腿再抄一份(clay 宪法「schema 单一属主禁复制」):[ref] form b 的身份重绑要
|
|
@@ -32,20 +33,26 @@ export declare const policyKeyFor: (sessionId: string, principal: string | undef
|
|
|
32
33
|
* + 恰好解一次 `decodeJsonColumn` + 折码 `foldJsonColumn`);本店只留「形判据是哪一只」与「坏体折成哪个码」。
|
|
33
34
|
*/
|
|
34
35
|
export declare function readSessionRulesBody(body: unknown): JsonColumnShape<SessionPermissionRules>;
|
|
36
|
+
/** 本店唯一的日志口:抹除封键那一臂(S-891)的点名。缺席 ⇒ 退回 `console.warn`(不静默)。 */
|
|
37
|
+
export type SessionPolicyStoreLogger = Pick<Logger, "warn">;
|
|
35
38
|
/** Dual-dialect durable SessionPolicyStore. See the file header for the dialect-delta ledger. */
|
|
36
39
|
export declare class SqlSessionPolicyStore implements SessionPolicyStore {
|
|
37
40
|
private readonly db;
|
|
38
|
-
|
|
41
|
+
private readonly logger?;
|
|
42
|
+
constructor(db: SqlDriver, logger?: SessionPolicyStoreLogger | undefined);
|
|
39
43
|
/** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
|
|
40
44
|
private q;
|
|
41
45
|
/** 判据属主 = `sql-errors.ts`([ref] P1-①;本站点旧形已是并集形,收编后语义不变)。 */
|
|
42
46
|
private isDupKey;
|
|
43
|
-
/** Read the stored rules for (session, principal-owner); null when none (parity with InMemory).
|
|
47
|
+
/** Read the stored rules for (session, principal-owner); null when none (parity with InMemory).
|
|
48
|
+
* 🔴 S-891(codex r1 [high] ①):同一条语句带上这把键的**封键记号** —— 封着 ⇒ `corrupt`,不答「没有规则」(见 {@link sealedKeyError})。 */
|
|
44
49
|
getRules(sessionId: string, principal?: string): Promise<StoredSessionRules | null>;
|
|
45
50
|
/** 2c session-sync: every (principal, rules) record for a session across ALL principals — getRules is
|
|
46
51
|
* per-(session,principal), but a cross-backend session EXPORT must bundle the WHOLE session's policy (the importer
|
|
47
52
|
* replays each via putRules). principal is read from the DENORMALIZED column (the sha256 policy_key is one-way);
|
|
48
|
-
* NULL → undefined = the session-wide row (parity with core's InMemory `principal ?? undefined`). Rules carry rev.
|
|
53
|
+
* NULL → undefined = the session-wide row (parity with core's InMemory `principal ?? undefined`). Rules carry rev.
|
|
54
|
+
* 🔴 S-891:会话里有**封着的键** ⇒ 整只枚举 `corrupt`(与一行读不出形时整只枚举拒同一口径 —— 导出一份缺了那把键的规则,
|
|
55
|
+
* 导入方就会把「状态未知」当成「这个 principal 没有规则」)。 */
|
|
49
56
|
listBySession(sessionId: string): Promise<SessionRulesRecord[]>;
|
|
50
57
|
/** Operator/owner write: CAS + tighten-only (unless operator) + rev bump, atomic in one txn; first-write race retried. */
|
|
51
58
|
putRules(sessionId: string, principal: string | undefined, rules: SessionPermissionRules, opts?: PutRulesOptions): Promise<StoredSessionRules>;
|
|
@@ -91,7 +98,8 @@ export declare class SqlSessionPolicyStore implements SessionPolicyStore {
|
|
|
91
98
|
* against a CURRENT read (PG is pinned to READ COMMITTED, TiDB's DML is a current read), so a row for a
|
|
92
99
|
* DIFFERENT principal that a concurrent putRules commits BETWEEN ① and ③ would be deleted by a session-wide
|
|
93
100
|
* DELETE while ② never marked it — a silently reset rev line, exactly the defect this batch exists to close.
|
|
94
|
-
* Scoped to the key set, the invariant is exact and checkable: **every row this seam deletes leaves a mark
|
|
101
|
+
* Scoped to the key set, the invariant is exact and checkable: **every row this seam deletes leaves a mark** —
|
|
102
|
+
* a readable row's mark continues its rev line and ends its lineage; an unreadable row's mark SEALS its key (S-891, below).
|
|
95
103
|
* A row born after ① is simply not part of this erase (same as one born a microsecond after ③ — the caller
|
|
96
104
|
* that needs "and nothing came back" re-issues; the seam is idempotent).
|
|
97
105
|
* No rows ⇒ return early: an erase of a session that never had rules writes NO mark (no line, nothing to keep),
|
|
@@ -99,8 +107,31 @@ export declare class SqlSessionPolicyStore implements SessionPolicyStore {
|
|
|
99
107
|
* Why the mark is a separate table and not a tombstone row: see the DDL comment in tidb-pool.ts — a tombstone
|
|
100
108
|
* would keep `session_id` + the DENORMALIZED plaintext `principal` in the table after an E21 purge, i.e. the
|
|
101
109
|
* right-to-delete would degrade to buy rev ordering. The mark keeps only the one-way hash, the session id and
|
|
102
|
-
* an integer.
|
|
110
|
+
* an integer.
|
|
111
|
+
*
|
|
112
|
+
* 🔴 **S-891(7.106.0)—— 抹除恒删行;计数读不出的行留「封键记号」**。一行的计数读不出(三个读点的同一只判词
|
|
113
|
+
* {@link readRowCounters} 判坏:`rev` 不是 ≥ 1 的安全整数 / `gen` 不是 ≥ 0 的安全整数;或下一代的号 `gen + 1` 已出安全整数)
|
|
114
|
+
* ⇒ 这一行**照删**,它的记号元组写 `(键, 会话, {@link SEALED_MARK_REV}, 0, 时刻)`、走**同一句** `GREATEST` 合并:rev 落上界,
|
|
115
|
+
* 世代 = `GREATEST(旧记号.gen + 1, 0)`(有旧记号照抬,没有就是 0)。于是这把键的下一次 `putRules` 算出 `上界 + 1`,被写前那道
|
|
116
|
+
* **同一只** {@link isStoredCounter} 拒(`corrupt`,零写);读面同答 `corrupt`({@link sealedKeyError})—— **封键**,直到运营方
|
|
117
|
+
* 显式解封(USAGE §9.11:原地把记号的 `rev` 归 0、`gen` + 1;不删记号,删了会交回世代)。
|
|
118
|
+
* 两条都不许丢,所以是这个形:
|
|
119
|
+
* · **删得掉** —— 抹除是运营方清坏行的出路(E21 `DELETE /v1/sessions/:id`、2c overwrite-dst 都经这里)。7.105.0 的「拒,一条写
|
|
120
|
+
* 都不发」让一行坏计数把整个会话的删除半程停住、重试恒败;
|
|
121
|
+
* · **不许同世代** —— 修复车 QS 的「照删、不写记号」被 codex 真复现:这把键的血统没在本次抹除上结束,重建行从旧记号起号、
|
|
122
|
+
* 与持有**旧快照**的活读者同世代 ⇒ 读者把重建的宽松行读成延续(deny → allow)。读不出的号不能拿来结束血统,
|
|
123
|
+
* 那就不让这把键再有下一行,直到有人看过。
|
|
124
|
+
* 响亮面:提交**之后**一条 warn(`session_policy_erase_sealed_keys`,点名会话与被封的键 + 两个计数的原文;回滚了就什么都没封,
|
|
125
|
+
* 死锁重试也不重复报)。这**不是** fail-open(没有放行任何东西,方向是 fail-closed),所以不走 `recordFailOpen`;普查登记 P 类终态。
|
|
126
|
+
* 残余(如实):记号里**已有**的世代若被列外改到 BIGINT 上界,冲突臂的 `记号.gen + 1` 在库里溢出 ⇒ 这次抹除响亮失败
|
|
127
|
+
* (驱动错误;那只记号本来就读不回,`putRules` 同样拒)—— 抹除不读记号,这一形不在本判词射程内。 */
|
|
103
128
|
deleteBySession(sessionId: string): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* S-891 封键的响亮面:一次抹除至多一条 warn。日志座**抛** ⇒ 退回 `console.warn` 带原句 —— 走到这里删除已经提交,
|
|
131
|
+
* 一次日志故障不许把它报成失败(E21 会因此 500、overwrite-dst 会因此中止;codex 7.105.0 修复验证轮 [medium] 同形)。
|
|
132
|
+
* 缺日志座(直接 `new` 本店的工具 / 测试)⇒ `console.warn`,不静默。
|
|
133
|
+
*/
|
|
134
|
+
private discloseSealed;
|
|
104
135
|
/**
|
|
105
136
|
* The store's one read-check-write transaction. Its load-bearing read is line ~106's
|
|
106
137
|
* `SELECT rules, rev … FOR UPDATE` — a LOCKING read, therefore a CURRENT read on both engines, which is
|
|
@@ -114,10 +145,10 @@ export declare class SqlSessionPolicyStore implements SessionPolicyStore {
|
|
|
114
145
|
}
|
|
115
146
|
/** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
|
|
116
147
|
export declare class TiDBSessionPolicyStore extends SqlSessionPolicyStore {
|
|
117
|
-
constructor(pool: MySqlPool);
|
|
148
|
+
constructor(pool: MySqlPool, logger?: SessionPolicyStoreLogger);
|
|
118
149
|
}
|
|
119
150
|
/** PostgreSQL binding — historical class name + ctor shape preserved. */
|
|
120
151
|
export declare class PgSessionPolicyStore extends SqlSessionPolicyStore {
|
|
121
|
-
constructor(pool: PgPool);
|
|
152
|
+
constructor(pool: PgPool, logger?: SessionPolicyStoreLogger);
|
|
122
153
|
}
|
|
123
154
|
//# sourceMappingURL=session-policy-store-sql.d.ts.map
|