@sema-agent/server 7.55.0 → 7.57.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 +3 -3
- package/README.md +14 -7
- package/README.zh-CN.md +10 -6
- package/USAGE.md +207 -59
- package/deploy/sema-up/chart/values.yaml +1 -1
- package/dist/approval-ask-audit-store.d.ts +100 -1
- package/dist/approval-ask-audit-store.js +103 -2
- package/dist/approval-card.d.ts +148 -13
- package/dist/approval-card.js +82 -13
- package/dist/approval-content-kind.d.ts +22 -0
- package/dist/approval-content-kind.js +5 -0
- package/dist/approval.d.ts +31 -0
- package/dist/approval.js +18 -0
- package/dist/auto-mode-face.d.ts +111 -0
- package/dist/auto-mode-face.js +99 -0
- package/dist/bench/s1/arms.js +5 -5
- package/dist/bench/s1/live-deps.js +11 -11
- package/dist/bench/s1/run-firm.js +3 -0
- package/dist/bench/s1/runner-ctx.d.ts +4 -0
- package/dist/bench/s1/runner-ctx.js +7 -0
- package/dist/boot/config-center.js +23 -1
- package/dist/boot/coordinators.js +13 -1
- package/dist/boot/leader.d.ts +21 -0
- package/dist/boot/leader.js +6 -0
- package/dist/boot/parked-revive-gate.d.ts +7 -2
- package/dist/boot/parked-revive-gate.js +12 -1
- package/dist/boot/resolve-spec.d.ts +3 -0
- package/dist/boot/resolve-spec.js +3 -1
- package/dist/boot/runner-deps.d.ts +12 -0
- package/dist/boot/runner-deps.js +28 -3
- package/dist/boot/runtime-caps.d.ts +19 -8
- package/dist/boot/runtime-caps.js +50 -20
- package/dist/boot/session-shell-gate-registry.d.ts +39 -0
- package/dist/boot/session-shell-gate-registry.js +21 -0
- package/dist/config-catalog.js +14 -5
- package/dist/config-center/types.d.ts +2 -1
- package/dist/config-provider.js +1 -0
- package/dist/config-types.d.ts +41 -10
- package/dist/config.d.ts +21 -0
- package/dist/config.js +40 -5
- package/dist/http/route-ctx.d.ts +90 -3
- package/dist/http/routes/a2a-serve.js +5 -3
- package/dist/http/routes/approvals-assistant.js +21 -8
- package/dist/http/routes/capabilities.js +21 -4
- package/dist/http/routes/leader.js +2 -2
- package/dist/http/routes/memory-origin.d.ts +2 -2
- package/dist/http/routes/rules.js +3 -2
- package/dist/http/routes/runs.d.ts +0 -14
- package/dist/http/routes/runs.js +20 -8
- package/dist/http/routes/tasks.js +34 -8
- package/dist/http/routes/workflows.js +20 -5
- package/dist/http/server.d.ts +11 -1
- package/dist/http/server.js +220 -34
- package/dist/http/wire-types.d.ts +9 -4
- package/dist/leader/diffout.js +2 -1
- package/dist/leader/endpoint.js +49 -15
- package/dist/leader/fanout.js +6 -5
- package/dist/leader/leader.js +17 -14
- package/dist/leader/merge.js +17 -12
- package/dist/leader/planner.js +3 -2
- package/dist/leader/repair-oracle.js +6 -4
- package/dist/leader/repair-wire.js +5 -3
- package/dist/leader/wire.d.ts +80 -1
- package/dist/leader/wire.js +55 -26
- package/dist/main.js +44 -8
- package/dist/observability/err-text.d.ts +6 -0
- package/dist/observability/err-text.js +10 -0
- package/dist/observability/fail-open.d.ts +40 -0
- package/dist/observability/fail-open.js +19 -0
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal-log.d.ts +120 -0
- package/dist/observability/run-terminal-log.js +360 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +9 -0
- package/dist/orchestration/workflow-completion-inbox.js +8 -0
- package/dist/permission-rule-vocab.d.ts +40 -0
- package/dist/permission-rule-vocab.js +17 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +26 -0
- package/dist/plugins/checkpoint-store-sql.js +23 -4
- package/dist/plugins/file-run-store.d.ts +3 -34
- package/dist/plugins/file-run-store.js +21 -0
- package/dist/plugins/local-checkpoint-store.d.ts +10 -0
- package/dist/plugins/local-checkpoint-store.js +5 -0
- package/dist/plugins/local-session-store.d.ts +9 -9
- package/dist/plugins/local-session-store.js +5 -3
- package/dist/plugins/memory-run-store.d.ts +3 -15
- package/dist/plugins/memory-run-store.js +21 -0
- package/dist/plugins/permission-rule-store-file.d.ts +22 -4
- package/dist/plugins/permission-rule-store-file.js +25 -11
- package/dist/plugins/permission-rule-store-sql.d.ts +60 -30
- package/dist/plugins/permission-rule-store-sql.js +39 -23
- package/dist/plugins/pg-session-storage.js +17 -13
- package/dist/plugins/remote-scratchpad.js +3 -2
- package/dist/plugins/run-store-sql.d.ts +3 -42
- package/dist/plugins/run-store-sql.js +36 -5
- package/dist/plugins/store-backend.d.ts +1 -1
- package/dist/plugins/store-contracts.d.ts +70 -1
- package/dist/plugins/tidb-session-store.js +18 -14
- package/dist/plugins/workflow-run-store-sql.d.ts +5 -0
- package/dist/plugins/workflow-run-store-sql.js +10 -2
- package/dist/rules-consent.d.ts +5 -4
- package/dist/rules-consent.js +25 -46
- package/dist/run-cancel-context.d.ts +13 -0
- package/dist/run-cancel-context.js +15 -0
- package/dist/run-local.js +2 -2
- package/dist/runs.d.ts +4 -1
- package/dist/runs.js +41 -10
- package/dist/runtime-caps-resolver.d.ts +133 -17
- package/dist/runtime-caps-resolver.js +44 -4
- package/dist/security.d.ts +7 -0
- package/dist/task-settings.d.ts +57 -3
- package/dist/task-settings.js +78 -5
- package/dist/task-workflow.d.ts +32 -2
- package/dist/task-workflow.js +8 -3
- package/dist/tool-approval.d.ts +38 -63
- package/dist/tool-approval.js +102 -74
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/ledger-sink.d.ts +13 -4
- package/dist/trace/ledger-sink.js +14 -6
- package/dist/trace/project.d.ts +13 -0
- package/dist/trace/project.js +10 -1
- package/dist/trace/redact.d.ts +22 -11
- package/dist/trace/redact.js +655 -20
- package/dist/trace/sema-provenance.d.ts +26 -0
- package/dist/trace/sema-provenance.js +11 -0
- package/dist/turn-activity.d.ts +32 -2
- package/dist/turn-activity.js +29 -4
- package/package.json +3 -3
- package/skills/find-skills.md +1 -1
- package/skills/loop.md +1 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { RunRecord } from "../plugins/store-contracts.js";
|
|
2
|
+
/** 一次终局写口交给观测面的原料(store 侧装配,**未脱敏** —— 脱敏在本模块内做,见 {@link notifyRunTerminal})。 */
|
|
3
|
+
export interface RunTerminalRecord {
|
|
4
|
+
/** 这条 run 的账本 id = `GET /v1/runs/:id` 的 id(本仓 run 行主键名为 taskId,同一个值)。 */
|
|
5
|
+
runId: string;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
owner: string | null;
|
|
8
|
+
status: RunRecord["status"];
|
|
9
|
+
/** 终局结果里的引擎侧 taskId —— 只在与 {@link RunTerminalRecord.runId} **不同**时进日志(sync 腿上 core 会把它
|
|
10
|
+
* 铸成 sessionId,两者同值时重复一遍只是噪音)。 */
|
|
11
|
+
resultTaskId?: string | undefined;
|
|
12
|
+
model?: string | undefined;
|
|
13
|
+
errorCode?: string | undefined;
|
|
14
|
+
errorMessage?: string | undefined;
|
|
15
|
+
/** 行创建到终局的墙钟毫秒(store 侧用行的 createdAt 算;算不出=不传,键缺席)。 */
|
|
16
|
+
elapsedMs?: number | undefined;
|
|
17
|
+
}
|
|
18
|
+
/** 观测者收到的是**已判级 + 已脱敏 + 已截断**的成品——装配层只负责把它送进 logger。 */
|
|
19
|
+
export type RunTerminalObserver = (level: "error" | "info", fields: Record<string, unknown>) => void;
|
|
20
|
+
/**
|
|
21
|
+
* 终局行的级别判据。`undefined` = 不打行。
|
|
22
|
+
*
|
|
23
|
+
* `blocked` 刻意不打:它是**治理判决**(门拒绝了这次执行),不是失败——它有自己的门遥测与
|
|
24
|
+
* `GET /v1/runs/:id` 读面,混进 `run_failed` 会把「模型/网络出事了」这条读法冲淡。
|
|
25
|
+
*/
|
|
26
|
+
export declare function runTerminalLogLevel(status: RunRecord["status"], errorCode: string | undefined): "error" | "info" | undefined;
|
|
27
|
+
/** 装配点(`main.ts` 唯一)。传 `undefined` 卸载(测试隔离)。 */
|
|
28
|
+
export declare function setRunTerminalObserver(fn: RunTerminalObserver | undefined): void;
|
|
29
|
+
/**
|
|
30
|
+
* store 的终局写口在 **CAS 赢了之后**调用它。未装配观测者 ⇒ 无操作(纯内存/无 boot 的单测车道)。
|
|
31
|
+
*
|
|
32
|
+
* 脱敏:`errorMessage` 过 `redactSecrets` 再截断到 {@link MESSAGE_CAP} —— provider 的错误体
|
|
33
|
+
* 会原样回显请求片段(取证里的 403 体带订阅链接;别的 provider 会回显 Authorization 头前缀),
|
|
34
|
+
* 而引擎日志是运维面、常被整条采集走。`errorCode` / `model` 是闭集式短标量,verbatim。
|
|
35
|
+
*/
|
|
36
|
+
export declare function notifyRunTerminal(rec: RunTerminalRecord): void;
|
|
37
|
+
/** S-105(test [ref] 旁生):终态**持久化**的 `errorMessage` 与日志副本同一脱敏口——provider 错误体(403 体带回显请求头/
|
|
38
|
+
* 订阅链接)里的密钥形不得原文落 run 行再由 `GET /v1/runs/:id` 直出。三 run store 的 `setTerminal` 写入前同调此函数;
|
|
39
|
+
* `undefined` 透传(缺席仍缺席),不截断(日志上限是日志的事,行上保留全文供取证)。 */
|
|
40
|
+
/** r4 最终轮 [medium]:`redactSecrets` 的关键字/前缀臂对 ≥2^23 单段仍会抛 `RangeError`(既有,S-106 登记);S-105 把脱敏
|
|
41
|
+
* 挪到 `setTerminal` **入口**后,这个抛会先于 CAS/claim 释放 ⇒ run 卡 `running`、会话 claim 悬挂到 reaper。这里 **fail-closed
|
|
42
|
+
* 不抛**:脱敏失败 ⇒ 落固定占位 `«redacted:oversized»`(绝不落原文,也绝不阻断终态翻转),并走既有 fail-open 遥测 tag。 */
|
|
43
|
+
export declare const OVERSIZED_REDACTION_PLACEHOLDER = "\u00ABredacted:oversized\u00BB";
|
|
44
|
+
/** S-107 窄范围重扫 [medium]:`redactSecrets` 对 PEM 起始栅栏密集的多 MB 文案**不抛但慢**(实测 1MB 1.4s / 8MB 7.9s 同步阻塞
|
|
45
|
+
* 事件循环),而终态座每次写要扫两遍(appendEvent + setTerminal 入口)、每次读再扫一遍。超过本上限的自由文本**整段**换占位
|
|
46
|
+
* (fail-closed:不截断——截断会把密钥切成半截,[ref] 的病;不扫——O(1)),取证面丢的是「>256KB 的错误文案」这一档。 */
|
|
47
|
+
/** codex r2 [medium]:上限按**边界最坏耗时**定——PEM 栅栏密集形 256KB 一次扫 0.25–0.46s、终态一次写扫两遍再加读面,一行
|
|
48
|
+
* 能让副本卡 ~1s;64KB 时同形 ≤ ~0.1s/次。正常 provider 错误体 < 10KB,>64KB 的错误文案只剩取证价值(整段占位丢弃)。 */
|
|
49
|
+
export declare const REDACT_INPUT_CAP = 65536;
|
|
50
|
+
/** 第十七轮重扫 [medium]:TiDB `task_run.error` 是 TEXT(65,535 **字节**),而上限按**字符**(65,536)量,多字节 / 占位膨胀(`«»` 各 2 字节、
|
|
51
|
+
* `«redacted:key»` 比短匹配长)都能让脱后串超列 ⇒ UPDATE 抛 ER_DATA_TOO_LONG、事务回滚、claim 不释放、run 卡 running 到 reaper。
|
|
52
|
+
* 脱敏**之后**按字节封顶(占位已替换完,切在占位中间不是泄漏)并带标记。 */
|
|
53
|
+
export declare const REDACT_OUTPUT_MAX_BYTES = 65000;
|
|
54
|
+
export declare function redactErrorMessage(raw: string | undefined): string | undefined;
|
|
55
|
+
/** 同口:终态 `result` 整块也持久化(`GET /v1/runs/:id` 直出 `result.errorMessage`)——写入前把内层 `errorMessage` 脱敏,
|
|
56
|
+
* 其余键逐字不动;`undefined`/`null` 透传。 */
|
|
57
|
+
export declare function redactTerminalResult<T extends {
|
|
58
|
+
errorMessage?: string | undefined;
|
|
59
|
+
blockedReason?: string | undefined;
|
|
60
|
+
remoteEnvFailures?: readonly {
|
|
61
|
+
message?: string | undefined;
|
|
62
|
+
}[] | undefined;
|
|
63
|
+
} | null | undefined>(result: T): T;
|
|
64
|
+
/** S-107(第五轮合并重扫 wf_f00c00bc 两根 [high]):同一个 `TaskResult` 除 run 行外还被逐字追加进 run_events 的 `done` 行
|
|
65
|
+
* (bg/verify/cascade/stream/resume 五腿),`failed` 行带原文 `errorMessage`,`GET /v1/runs/:id/events` 原样回放。
|
|
66
|
+
* 三孪生 `appendEvent` 入口 + 事件读面(旧行纵深)同调本函数:只认 `done.result`(整块走 `redactTerminalResult`)与
|
|
67
|
+
* `failed.errorMessage`,其余事件类型/键逐字不动(它们各有自己的 sink 侧脱敏);形不合(非对象)原样返回,不抛。 */
|
|
68
|
+
export declare function redactLedgerEventData(type: string, data: unknown): unknown;
|
|
69
|
+
/** S-107 第三轮窄范围重扫 [high]×2:leader 面的三处原文座——①`WorkerReport.error`(生产路径经 `runWorker` 构造,`fanout.ts`
|
|
70
|
+
* 的 runTaskStream 回退座不是生产座);②`LeaderResult.error`(plan/provision 失败带 `e.message`);③`merge.reason`
|
|
71
|
+
* (coordinator push 失败把 `git clone <带凭据的 durableRemote>` 命令行整段回显)。全部随 leader_run.result JSON 落库并经
|
|
72
|
+
* `GET /v1/leader/:id` 直出。修=**咽喉**:fanOut 边界对每份报告脱一次(不问谁产的),endpoint 落库前 + 读面各脱一次
|
|
73
|
+
* (幂等;旧行纵深)。只碰这三个自由文本键,其余逐字。 */
|
|
74
|
+
export declare function redactWorkerReportError<T extends {
|
|
75
|
+
error?: string | undefined;
|
|
76
|
+
checkpointGate?: unknown;
|
|
77
|
+
}>(report: T): T;
|
|
78
|
+
/** codex r5 [high]:`WorkerReport.checkpointGate`(core `CheckpointGate`)整块随 LeaderResult 落库/直出——`reason` 与
|
|
79
|
+
* `riskDescriptor.summary`/`shadowedRule` 是自由文本([ref] 实测 summary 带 PEM 明文;[ref] R3 登记),按类型表脱这三叶;
|
|
80
|
+
* `kind`/`toolName`/`axes`/`severity`/`touchedPaths` 闭集或路径,逐字。非对象形原样返回。 */
|
|
81
|
+
export declare function redactCheckpointGate(gate: unknown): unknown;
|
|
82
|
+
/** 工件字节(patch / *.rej 拒绝块):codex r6 [high]——拒绝块能带 `git clone https://u:TOKEN@host` 或 `_authToken=` 行,
|
|
83
|
+
* 「工件保真」不能让原凭据上元数据读面。做**原位密钥形替换**(与预览同一张规则表,字节只在密钥处变)、**不截断**;
|
|
84
|
+
* 单份上限 256KB(`ARTIFACT_INPUT_CAP`,第八轮重扫:不封顶=worker 可控 DoS)——超过整份换占位,这是有意的取证丢弃
|
|
85
|
+
* (原字节仍在 worker 分支 / merge 咽喉);扫描器抛 ⇒ 占位(fail-closed)。server 无任何消费方用持久化的这份字节回放。 */
|
|
86
|
+
/** 诊断文本(stdout/stderr/err.message/模型输出)的「先脱后切」——第九轮重扫 [medium]×3 + 第十轮 [high]×2 + codex r9 [high]:
|
|
87
|
+
* ①不能把整段喂进 64KB 上限臂(长测试日志整段占位、灌水计数);②**固定 PAD 的窗不是 fail-closed**:比 PAD 长的密钥形
|
|
88
|
+
* (>4096 的 URL 密码 / JSON 值 / PEM 体)锚点落在窗外,窗内只剩无锚的体,任何规则都认不出;窗在脱敏下缩短时残片还会
|
|
89
|
+
* 落进最终切片。所以**不开窗**:文本 ≤ 256KB ⇒ 整段扫(guard,不封顶于 64KB;PEM 密集形最坏 ~0.4s/座)再切;> 256KB ⇒
|
|
90
|
+
* 整段换占位并带原长(fail-closed 取证丢弃,单独计数:普查第 59 行)。 */
|
|
91
|
+
export declare const DIAG_INPUT_CAP = 262144;
|
|
92
|
+
export declare const OVERSIZED_DIAG_PLACEHOLDER = "\u00ABredacted:oversized-diagnostic\u00BB";
|
|
93
|
+
export declare function redactDiagnosticText(text: string): string;
|
|
94
|
+
export declare function redactHead(text: string, keep: number): string;
|
|
95
|
+
/** 进模型的 worker 补丁头(collapse 目标 / 冲突解决目标):**不开窗**。第十三轮为保留大 diff 的可复用头部开了 256KB 头窗,
|
|
96
|
+
* 此后 codex r12–r15 与第十五至十七轮重扫五轮各证一次「窗上的裁切守卫可被打穿」(无上界的 JSON 值/URL 密码臂;脱前/脱后坐标
|
|
97
|
+
* 错位;PEM 逐块;前面一处脱敏会**吃掉**守卫所依赖的锚点)——同 S-103 的教训:猜边界的规则总有反例。终形=补丁 ≤256KB 整段扫
|
|
98
|
+
* 再切 keep(完全正确);>256KB **不内联**(占位带原长,tag `patch-head-cut`):冲突解决者仍有工作树里的冲突标记,collapse solo
|
|
99
|
+
* 对 lockfile 级大 diff 失去内联上下文(worker 分支仍在)——这是有意成文的安全轴取舍。 */
|
|
100
|
+
export declare const OVERSIZED_PATCH_PLACEHOLDER = "\u00ABredacted:oversized-patch-not-inlined\u00BB";
|
|
101
|
+
export declare function redactPatchHead(patch: string, keep: number): string;
|
|
102
|
+
export declare function redactTail(text: string, keep: number): string;
|
|
103
|
+
export declare const ARTIFACT_INPUT_CAP = 262144;
|
|
104
|
+
export declare const OVERSIZED_ARTIFACT_PLACEHOLDER = "\u00ABredacted:oversized-artifact\u00BB";
|
|
105
|
+
export declare function redactArtifactText(raw: string): string;
|
|
106
|
+
export declare function redactLeaderResult(result: unknown): unknown;
|
|
107
|
+
/** 同上,附带「有没有任何叶子变过」——读面记忆用它区分「本就干净的行」(只记 id,不留体)与「旧脏行」(留脱后体)。 */
|
|
108
|
+
export declare function redactLeaderResultWithFlag(result: unknown): {
|
|
109
|
+
value: unknown;
|
|
110
|
+
changed: boolean;
|
|
111
|
+
};
|
|
112
|
+
export declare function memoRedactedRead<T>(seat: string, raw: T, redact: (v: T) => T): T;
|
|
113
|
+
/** run 终态 result 的读面记忆只按**可脱敏的三叶**(errorMessage / blockedReason / remoteEnvFailures[].message)做键与留体——
|
|
114
|
+
* 第十六轮重扫:按整块 result 摘要每次轮询要 stringify 一份可能数 MB 的 result,且大 result 的脏行永远存不进去。三叶缺席
|
|
115
|
+
* ⇒ O(1) 直返(`redactTerminalResult` 本就同引用)。 */
|
|
116
|
+
export declare function memoRedactTerminalResult<T extends Parameters<typeof redactTerminalResult>[0]>(seat: string, result: T): T;
|
|
117
|
+
export declare function diffScanShadowCountForTest(): number;
|
|
118
|
+
export declare function resetDiffScanShadowCountForTest(): void;
|
|
119
|
+
export declare function resetReadRedactionMemoForTest(): void;
|
|
120
|
+
//# sourceMappingURL=run-terminal-log.d.ts.map
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { redactSecrets } from "../trace/redact.js";
|
|
3
|
+
import { errorText } from "./err-text.js";
|
|
4
|
+
import { recordFailOpen } from "./fail-open.js";
|
|
5
|
+
const MESSAGE_CAP = 1024;
|
|
6
|
+
export function runTerminalLogLevel(status, errorCode) {
|
|
7
|
+
switch (status) {
|
|
8
|
+
case "failed":
|
|
9
|
+
return errorCode === "cancelled" ? "info" : "error";
|
|
10
|
+
case "blocked":
|
|
11
|
+
case "completed":
|
|
12
|
+
case "running":
|
|
13
|
+
case "suspended":
|
|
14
|
+
case "needs_review":
|
|
15
|
+
return undefined;
|
|
16
|
+
default: {
|
|
17
|
+
const unhandled = status;
|
|
18
|
+
void unhandled;
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
let observer;
|
|
24
|
+
export function setRunTerminalObserver(fn) {
|
|
25
|
+
observer = fn;
|
|
26
|
+
}
|
|
27
|
+
export function notifyRunTerminal(rec) {
|
|
28
|
+
const level = runTerminalLogLevel(rec.status, rec.errorCode);
|
|
29
|
+
if (level === undefined || !observer)
|
|
30
|
+
return;
|
|
31
|
+
const raw = rec.errorMessage;
|
|
32
|
+
const message = raw === undefined ? undefined : (redactErrorMessage(raw) ?? "").slice(0, MESSAGE_CAP);
|
|
33
|
+
observer(level, {
|
|
34
|
+
runId: rec.runId,
|
|
35
|
+
sessionId: rec.sessionId,
|
|
36
|
+
...(rec.owner !== null ? { owner: rec.owner } : {}),
|
|
37
|
+
status: rec.status,
|
|
38
|
+
...(rec.resultTaskId !== undefined && rec.resultTaskId !== rec.runId ? { taskId: rec.resultTaskId } : {}),
|
|
39
|
+
...(rec.model !== undefined ? { model: rec.model } : {}),
|
|
40
|
+
...(rec.errorCode !== undefined ? { errorCode: rec.errorCode } : {}),
|
|
41
|
+
...(message !== undefined ? { errorMessage: message } : {}),
|
|
42
|
+
...(rec.elapsedMs !== undefined ? { elapsedMs: rec.elapsedMs } : {}),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export const OVERSIZED_REDACTION_PLACEHOLDER = "«redacted:oversized»";
|
|
46
|
+
export const REDACT_INPUT_CAP = 65_536;
|
|
47
|
+
export const REDACT_OUTPUT_MAX_BYTES = 65_000;
|
|
48
|
+
const OUTPUT_TRUNCATED_MARK = "…[truncated: exceeds store column]";
|
|
49
|
+
function fitOutputBytes(s) {
|
|
50
|
+
if (Buffer.byteLength(s) <= REDACT_OUTPUT_MAX_BYTES)
|
|
51
|
+
return s;
|
|
52
|
+
recordFailOpen("server.run-terminal.output-truncated", `bytes=${Buffer.byteLength(s)} cap=${REDACT_OUTPUT_MAX_BYTES}`);
|
|
53
|
+
const budget = REDACT_OUTPUT_MAX_BYTES - Buffer.byteLength(OUTPUT_TRUNCATED_MARK);
|
|
54
|
+
let cut = Buffer.from(s).subarray(0, budget).toString();
|
|
55
|
+
if (cut.endsWith("\uFFFD"))
|
|
56
|
+
cut = cut.slice(0, -1);
|
|
57
|
+
return cut + OUTPUT_TRUNCATED_MARK;
|
|
58
|
+
}
|
|
59
|
+
export function redactErrorMessage(raw) {
|
|
60
|
+
if (raw === undefined)
|
|
61
|
+
return undefined;
|
|
62
|
+
let len = -1;
|
|
63
|
+
try {
|
|
64
|
+
const text = typeof raw === "string" ? raw : String(raw);
|
|
65
|
+
len = text.length;
|
|
66
|
+
if (len > REDACT_INPUT_CAP) {
|
|
67
|
+
recordFailOpen("server.run-terminal.redact-capped", `len=${len} cap=${REDACT_INPUT_CAP}`);
|
|
68
|
+
return OVERSIZED_REDACTION_PLACEHOLDER;
|
|
69
|
+
}
|
|
70
|
+
return fitOutputBytes(redactSecrets(text));
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
recordFailOpen("server.run-terminal.redact-threw", `len=${len} err=${errorText(err)}`);
|
|
74
|
+
return OVERSIZED_REDACTION_PLACEHOLDER;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export function redactTerminalResult(result) {
|
|
78
|
+
if (result === undefined || result === null)
|
|
79
|
+
return result;
|
|
80
|
+
const r = result;
|
|
81
|
+
let changed = false;
|
|
82
|
+
const out = { ...r };
|
|
83
|
+
if (r.errorMessage !== undefined) {
|
|
84
|
+
out.errorMessage = redactErrorMessage(r.errorMessage);
|
|
85
|
+
changed = true;
|
|
86
|
+
}
|
|
87
|
+
if (r.blockedReason !== undefined) {
|
|
88
|
+
out.blockedReason = redactErrorMessage(r.blockedReason);
|
|
89
|
+
changed = true;
|
|
90
|
+
}
|
|
91
|
+
if (r.remoteEnvFailures !== undefined) {
|
|
92
|
+
out.remoteEnvFailures = Array.isArray(r.remoteEnvFailures)
|
|
93
|
+
? r.remoteEnvFailures.map((f) => (f && typeof f === "object" && typeof f.message === "string" ? { ...f, message: redactErrorMessage(f.message) } : f))
|
|
94
|
+
: r.remoteEnvFailures;
|
|
95
|
+
changed = true;
|
|
96
|
+
}
|
|
97
|
+
return changed ? out : result;
|
|
98
|
+
}
|
|
99
|
+
export function redactLedgerEventData(type, data) {
|
|
100
|
+
if (data === null || typeof data !== "object" || Array.isArray(data))
|
|
101
|
+
return data;
|
|
102
|
+
if (type === "done") {
|
|
103
|
+
const result = data.result;
|
|
104
|
+
if (result === null || typeof result !== "object")
|
|
105
|
+
return data;
|
|
106
|
+
return { ...data, result: redactTerminalResult(result) };
|
|
107
|
+
}
|
|
108
|
+
if (type === "failed") {
|
|
109
|
+
const msg = data.errorMessage;
|
|
110
|
+
if (typeof msg !== "string")
|
|
111
|
+
return data;
|
|
112
|
+
return { ...data, errorMessage: redactErrorMessage(msg) };
|
|
113
|
+
}
|
|
114
|
+
return data;
|
|
115
|
+
}
|
|
116
|
+
export function redactWorkerReportError(report) {
|
|
117
|
+
const gate = redactCheckpointGate(report.checkpointGate);
|
|
118
|
+
const error = report.error === undefined ? undefined : (redactErrorMessage(report.error) ?? "");
|
|
119
|
+
if (error === report.error && gate === report.checkpointGate)
|
|
120
|
+
return report;
|
|
121
|
+
return { ...report, ...(error !== report.error ? { error } : {}), ...(gate !== report.checkpointGate ? { checkpointGate: gate } : {}) };
|
|
122
|
+
}
|
|
123
|
+
export function redactCheckpointGate(gate) {
|
|
124
|
+
if (gate === null || typeof gate !== "object" || Array.isArray(gate))
|
|
125
|
+
return gate;
|
|
126
|
+
const g = gate;
|
|
127
|
+
let changed = false;
|
|
128
|
+
const out = { ...g };
|
|
129
|
+
const str = (v) => { const o = redactErrorMessage(v) ?? ""; if (o !== v)
|
|
130
|
+
changed = true; return o; };
|
|
131
|
+
const strs = (v) => (Array.isArray(v) ? v.map((x) => (typeof x === "string" ? str(x) : x)) : v);
|
|
132
|
+
if (typeof g.reason === "string")
|
|
133
|
+
out.reason = str(g.reason);
|
|
134
|
+
const rd = g.riskDescriptor;
|
|
135
|
+
if (rd && typeof rd === "object" && !Array.isArray(rd)) {
|
|
136
|
+
const r = rd;
|
|
137
|
+
const nr = { ...r };
|
|
138
|
+
for (const k of ["summary", "shadowedRule", "probeReason"])
|
|
139
|
+
if (typeof r[k] === "string")
|
|
140
|
+
nr[k] = str(r[k]);
|
|
141
|
+
if (Array.isArray(r.touchedPaths))
|
|
142
|
+
nr.touchedPaths = strs(r.touchedPaths);
|
|
143
|
+
const pc = r.probeCause;
|
|
144
|
+
if (pc && typeof pc === "object" && !Array.isArray(pc)) {
|
|
145
|
+
const p = pc;
|
|
146
|
+
const np = { ...p };
|
|
147
|
+
for (const k of ["roots", "further"]) {
|
|
148
|
+
const ops = p[k];
|
|
149
|
+
if (ops && typeof ops === "object" && !Array.isArray(ops) && Array.isArray(ops.shown))
|
|
150
|
+
np[k] = { ...ops, shown: strs(ops.shown) };
|
|
151
|
+
}
|
|
152
|
+
nr.probeCause = np;
|
|
153
|
+
}
|
|
154
|
+
out.riskDescriptor = nr;
|
|
155
|
+
}
|
|
156
|
+
return changed ? out : gate;
|
|
157
|
+
}
|
|
158
|
+
export const DIAG_INPUT_CAP = 262_144;
|
|
159
|
+
export const OVERSIZED_DIAG_PLACEHOLDER = "«redacted:oversized-diagnostic»";
|
|
160
|
+
export function redactDiagnosticText(text) {
|
|
161
|
+
if (text.length > DIAG_INPUT_CAP) {
|
|
162
|
+
recordFailOpen("server.run-terminal.diag-capped", `len=${text.length} cap=${DIAG_INPUT_CAP}`);
|
|
163
|
+
return `${OVERSIZED_DIAG_PLACEHOLDER} [len=${text.length}]`;
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
return redactSecrets(text);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
recordFailOpen("server.run-terminal.redact-threw", `diag len=${text.length} err=${err instanceof Error ? err.name : String(err)}`);
|
|
170
|
+
return OVERSIZED_REDACTION_PLACEHOLDER;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export function redactHead(text, keep) {
|
|
174
|
+
return redactDiagnosticText(text).slice(0, keep);
|
|
175
|
+
}
|
|
176
|
+
export const OVERSIZED_PATCH_PLACEHOLDER = "«redacted:oversized-patch-not-inlined»";
|
|
177
|
+
function diffScanRefusal(patch, direct) {
|
|
178
|
+
if (/^diff --(?:cc|combined) |^@{3,} /m.test(patch))
|
|
179
|
+
return "combined-diff";
|
|
180
|
+
const fence = /-{4,5} ?BEGIN[^\n-]*PRIVATE KEY(?: BLOCK)?[^\n-]*-{4,5}/;
|
|
181
|
+
if (fence.test(direct))
|
|
182
|
+
return "pem-fence-survived";
|
|
183
|
+
const ppkField = /Private-(?:MAC|Hash): [0-9a-fA-F]{32}/;
|
|
184
|
+
const ppkLinesField = /Private-Lines: \d+/;
|
|
185
|
+
const b64Line = /^[ +-]?[A-Za-z0-9+/=]{16,}[ \t]*$/m;
|
|
186
|
+
const ppkAnchorInSections = (t) => t.split(/^(?=diff --git )/m).some((sec) => ppkLinesField.test(sec) && b64Line.test(sec));
|
|
187
|
+
if (ppkAnchorInSections(patch))
|
|
188
|
+
return "ppk-field-survived";
|
|
189
|
+
if (ppkField.test(direct))
|
|
190
|
+
return "ppk-field-survived";
|
|
191
|
+
const strip1 = (t) => t.replace(/^[+-]/gm, "");
|
|
192
|
+
const stripFull = (t) => t.replace(/^[+-]+/gm, "");
|
|
193
|
+
const layers = [["1", strip1]];
|
|
194
|
+
if (/^[+-]{2}/m.test(patch))
|
|
195
|
+
layers.push(["full", stripFull]);
|
|
196
|
+
for (const [name, strip] of layers) {
|
|
197
|
+
shadowScans += 1;
|
|
198
|
+
const shadow = redactSecrets(strip(patch), { observe: false });
|
|
199
|
+
if (strip(direct) !== shadow)
|
|
200
|
+
return `shadow-diverged@${name}`;
|
|
201
|
+
if (fence.test(shadow))
|
|
202
|
+
return "pem-fence-survived";
|
|
203
|
+
if (ppkField.test(shadow))
|
|
204
|
+
return "ppk-field-survived";
|
|
205
|
+
}
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
export function redactPatchHead(patch, keep) {
|
|
209
|
+
if (patch.length > DIAG_INPUT_CAP) {
|
|
210
|
+
recordFailOpen("server.run-terminal.patch-head-cut", `not-inlined len=${patch.length} keep=${keep}`);
|
|
211
|
+
return `${OVERSIZED_PATCH_PLACEHOLDER} [len=${patch.length}]`;
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
const direct = redactSecrets(patch);
|
|
215
|
+
const refusal = diffScanRefusal(patch, direct);
|
|
216
|
+
if (refusal !== undefined) {
|
|
217
|
+
recordFailOpen("server.run-terminal.patch-head-cut", `${refusal} len=${patch.length} keep=${keep}`);
|
|
218
|
+
return `${OVERSIZED_PATCH_PLACEHOLDER} [len=${patch.length}]`;
|
|
219
|
+
}
|
|
220
|
+
return direct.slice(0, keep);
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
recordFailOpen("server.run-terminal.redact-threw", `patch-head len=${patch.length} err=${err instanceof Error ? err.name : String(err)}`);
|
|
224
|
+
return OVERSIZED_REDACTION_PLACEHOLDER;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
export function redactTail(text, keep) {
|
|
228
|
+
return redactDiagnosticText(text).slice(-keep);
|
|
229
|
+
}
|
|
230
|
+
export const ARTIFACT_INPUT_CAP = 262_144;
|
|
231
|
+
export const OVERSIZED_ARTIFACT_PLACEHOLDER = "«redacted:oversized-artifact»";
|
|
232
|
+
export function redactArtifactText(raw) {
|
|
233
|
+
if (raw.length > ARTIFACT_INPUT_CAP) {
|
|
234
|
+
recordFailOpen("server.run-terminal.artifact-capped", `len=${raw.length} cap=${ARTIFACT_INPUT_CAP}`);
|
|
235
|
+
return OVERSIZED_ARTIFACT_PLACEHOLDER;
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
const direct = redactSecrets(raw);
|
|
239
|
+
const refusal = diffScanRefusal(raw, direct);
|
|
240
|
+
if (refusal !== undefined) {
|
|
241
|
+
recordFailOpen("server.run-terminal.artifact-capped", `${refusal} len=${raw.length}`);
|
|
242
|
+
return `${OVERSIZED_ARTIFACT_PLACEHOLDER} [${refusal} len=${raw.length}]`;
|
|
243
|
+
}
|
|
244
|
+
return direct;
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
recordFailOpen("server.run-terminal.redact-threw", `artifact len=${raw.length} err=${err instanceof Error ? err.name : String(err)}`);
|
|
248
|
+
return OVERSIZED_REDACTION_PLACEHOLDER;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
export function redactLeaderResult(result) {
|
|
252
|
+
return redactLeaderResultWithFlag(result).value;
|
|
253
|
+
}
|
|
254
|
+
export function redactLeaderResultWithFlag(result) {
|
|
255
|
+
if (result === null || typeof result !== "object" || Array.isArray(result))
|
|
256
|
+
return { value: result, changed: false };
|
|
257
|
+
let changed = false;
|
|
258
|
+
const str = (v) => { if (typeof v !== "string")
|
|
259
|
+
return undefined; const o = redactErrorMessage(v) ?? ""; if (o !== v)
|
|
260
|
+
changed = true; return o; };
|
|
261
|
+
const artMemo = new Map();
|
|
262
|
+
const art = (v) => { if (typeof v !== "string")
|
|
263
|
+
return v; let o = artMemo.get(v); if (o === undefined) {
|
|
264
|
+
o = redactArtifactText(v);
|
|
265
|
+
artMemo.set(v, o);
|
|
266
|
+
} if (o !== v)
|
|
267
|
+
changed = true; return o; };
|
|
268
|
+
const obj = (v) => (v && typeof v === "object" && !Array.isArray(v) ? v : undefined);
|
|
269
|
+
const r = result;
|
|
270
|
+
const out = { ...r };
|
|
271
|
+
if (Array.isArray(r.reports))
|
|
272
|
+
out.reports = r.reports.map((w) => {
|
|
273
|
+
if (!obj(w))
|
|
274
|
+
return w;
|
|
275
|
+
const nw = redactWorkerReportError(w);
|
|
276
|
+
if (nw !== w)
|
|
277
|
+
changed = true;
|
|
278
|
+
return nw;
|
|
279
|
+
});
|
|
280
|
+
if (typeof r.error === "string")
|
|
281
|
+
out.error = str(r.error);
|
|
282
|
+
const replan = obj(r.replan);
|
|
283
|
+
if (replan && typeof replan.collapsed === "string")
|
|
284
|
+
out.replan = { ...replan, collapsed: str(replan.collapsed) };
|
|
285
|
+
const merge = obj(r.merge);
|
|
286
|
+
if (merge) {
|
|
287
|
+
const m = { ...merge };
|
|
288
|
+
for (const k of ["reason", "details"])
|
|
289
|
+
if (typeof merge[k] === "string")
|
|
290
|
+
m[k] = str(merge[k]);
|
|
291
|
+
for (const k of ["rej", "candidatePatch"])
|
|
292
|
+
if (typeof merge[k] === "string")
|
|
293
|
+
m[k] = art(merge[k]);
|
|
294
|
+
const measure = obj(merge.measure);
|
|
295
|
+
if (measure && typeof measure.reason === "string")
|
|
296
|
+
m.measure = { ...measure, reason: str(measure.reason) };
|
|
297
|
+
out.merge = m;
|
|
298
|
+
}
|
|
299
|
+
const cp = obj(r.candidatePatch);
|
|
300
|
+
if (cp)
|
|
301
|
+
out.candidatePatch = { ...cp, ...(typeof cp.reason === "string" ? { reason: str(cp.reason) } : {}), ...(typeof cp.patch === "string" ? { patch: art(cp.patch) } : {}) };
|
|
302
|
+
if (Array.isArray(r.salvaged))
|
|
303
|
+
out.salvaged = r.salvaged.map((x) => (obj(x) && typeof x.patch === "string" ? { ...x, patch: art(x.patch) } : x));
|
|
304
|
+
return { value: changed ? out : result, changed };
|
|
305
|
+
}
|
|
306
|
+
const READ_MEMO_CLEAN_CAP = 8192;
|
|
307
|
+
const READ_MEMO_DIRTY_CAP = 512;
|
|
308
|
+
const READ_MEMO_DIRTY_MAX_ENTRY_BYTES = 262_144;
|
|
309
|
+
const READ_MEMO_DIRTY_MAX_TOTAL_BYTES = 16 * 1_048_576;
|
|
310
|
+
const readMemoClean = new Set();
|
|
311
|
+
const readMemoDirty = new Map();
|
|
312
|
+
let readMemoDirtyBytes = 0;
|
|
313
|
+
export function memoRedactedRead(seat, raw, redact) {
|
|
314
|
+
const key = `${seat}\u0000${createHash("sha256").update(typeof raw === "string" ? raw : JSON.stringify(raw) ?? "").digest("hex")}`;
|
|
315
|
+
if (readMemoClean.has(key))
|
|
316
|
+
return raw;
|
|
317
|
+
const hit = readMemoDirty.get(key);
|
|
318
|
+
if (hit)
|
|
319
|
+
return hit.value;
|
|
320
|
+
const out = redact(raw);
|
|
321
|
+
const clean = out === raw || (typeof out === "string" && out === raw) || JSON.stringify(out) === JSON.stringify(raw);
|
|
322
|
+
if (clean) {
|
|
323
|
+
if (readMemoClean.size >= READ_MEMO_CLEAN_CAP)
|
|
324
|
+
readMemoClean.delete(readMemoClean.values().next().value);
|
|
325
|
+
readMemoClean.add(key);
|
|
326
|
+
return out;
|
|
327
|
+
}
|
|
328
|
+
const bytes = Buffer.byteLength(typeof out === "string" ? out : JSON.stringify(out));
|
|
329
|
+
if (bytes <= READ_MEMO_DIRTY_MAX_ENTRY_BYTES) {
|
|
330
|
+
while (readMemoDirty.size > 0 && (readMemoDirty.size >= READ_MEMO_DIRTY_CAP || readMemoDirtyBytes + bytes > READ_MEMO_DIRTY_MAX_TOTAL_BYTES)) {
|
|
331
|
+
const oldest = readMemoDirty.keys().next().value;
|
|
332
|
+
readMemoDirtyBytes -= readMemoDirty.get(oldest).bytes;
|
|
333
|
+
readMemoDirty.delete(oldest);
|
|
334
|
+
}
|
|
335
|
+
readMemoDirty.set(key, { value: out, bytes });
|
|
336
|
+
readMemoDirtyBytes += bytes;
|
|
337
|
+
}
|
|
338
|
+
return out;
|
|
339
|
+
}
|
|
340
|
+
export function memoRedactTerminalResult(seat, result) {
|
|
341
|
+
if (result === undefined || result === null)
|
|
342
|
+
return result;
|
|
343
|
+
const r = result;
|
|
344
|
+
if (r.errorMessage === undefined && r.blockedReason === undefined && r.remoteEnvFailures === undefined)
|
|
345
|
+
return result;
|
|
346
|
+
const leaves = { errorMessage: r.errorMessage, blockedReason: r.blockedReason, remoteEnvFailures: r.remoteEnvFailures };
|
|
347
|
+
const redacted = memoRedactedRead(seat, leaves, (l) => redactTerminalResult(l));
|
|
348
|
+
if (redacted === leaves)
|
|
349
|
+
return result;
|
|
350
|
+
return { ...result, ...redacted };
|
|
351
|
+
}
|
|
352
|
+
let shadowScans = 0;
|
|
353
|
+
export function diffScanShadowCountForTest() { return shadowScans; }
|
|
354
|
+
export function resetDiffScanShadowCountForTest() { shadowScans = 0; }
|
|
355
|
+
export function resetReadRedactionMemoForTest() {
|
|
356
|
+
readMemoClean.clear();
|
|
357
|
+
readMemoDirty.clear();
|
|
358
|
+
readMemoDirtyBytes = 0;
|
|
359
|
+
}
|
|
360
|
+
//# sourceMappingURL=run-terminal-log.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SemaProvenanceInput } from "../trace/sema-provenance.js";
|
|
1
2
|
import type { SystemInjectionPriority } from "@sema-agent/core";
|
|
2
3
|
import type { LedgerEventType } from "../trace/ledger-events.js";
|
|
3
4
|
/** One pending completion, scoped to the session that must be told about it. `summary` is ALREADY
|
|
@@ -259,6 +260,14 @@ export declare function taskNotificationInboxEntry(sessionId: string, owner: str
|
|
|
259
260
|
resumable?: boolean;
|
|
260
261
|
/** core 1.353([ref]):诊断槽(journal 坐标+教句,引擎值单源)——park 透传,drain 帧原样带。 */
|
|
261
262
|
diagnostics?: string;
|
|
263
|
+
/** [ref] [ref] 片6 d1(core 7.2.0):子代→主代 uplink 帧的渲染判别成员 + typed 归因侧记。park 是
|
|
264
|
+
* **空闲态**(父 leg 已收尾)到达的那条路——恰是 [ref](b) 的场景;不过桥 ⇒ drain 帧无判别键、cli 渲老形卡。
|
|
265
|
+
* body = UNTRUSTED 子代文本 → redact+cap(与 result 同姿势);provenance 成员逐挑 verbatim(引擎值)。 */
|
|
266
|
+
agentMessage?: {
|
|
267
|
+
from: string;
|
|
268
|
+
body: string;
|
|
269
|
+
};
|
|
270
|
+
_sema_provenance?: SemaProvenanceInput;
|
|
262
271
|
}, enqueuedAt: number,
|
|
263
272
|
/** lead-b (dual-ID audit): the PARENT run's durable taskId (the uuid the shell's footer rows
|
|
264
273
|
* key on) — rides the drained frame so a consumer can bind the a*-keyed child notification to its uuid-keyed
|
|
@@ -2,6 +2,7 @@ import { mkdirSync, readFileSync, existsSync, openSync, writeSync, fsyncSync, cl
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { redactSecrets } from "../trace/redact.js";
|
|
4
4
|
import { injectionTierOrAbsent } from "../trace/injection-tier.js";
|
|
5
|
+
import { semaProvenanceOrAbsent } from "../trace/sema-provenance.js";
|
|
5
6
|
export const MAX_PENDING_PER_SESSION = 100;
|
|
6
7
|
export const COMPACT_EVERY = 512;
|
|
7
8
|
export const PURGE_FENCE_MS = 10 * 60 * 1000;
|
|
@@ -314,6 +315,13 @@ export function taskNotificationInboxEntry(sessionId, owner, n, enqueuedAt, pare
|
|
|
314
315
|
...(n.resumable !== undefined ? { resumable: n.resumable } : {}),
|
|
315
316
|
...(n.result ? { result: cap(redactSecrets(n.result), 4000) } : {}),
|
|
316
317
|
...(n.output_file ? { output_file: cap(redactSecrets(n.output_file), 1024) } : {}),
|
|
318
|
+
...(n.agentMessage !== undefined ? { agentMessage: { from: n.agentMessage.from, body: cap(redactSecrets(n.agentMessage.body), 4000) } } : {}),
|
|
319
|
+
...(() => {
|
|
320
|
+
if (n._sema_provenance === undefined)
|
|
321
|
+
return {};
|
|
322
|
+
const p = semaProvenanceOrAbsent(n._sema_provenance);
|
|
323
|
+
return p !== undefined ? { _sema_provenance: p } : {};
|
|
324
|
+
})(),
|
|
317
325
|
};
|
|
318
326
|
return {
|
|
319
327
|
sessionId,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [ref] + [ref](core `e298c9e0`,随 7.2.x 提货生效;[ref])—— 规则车道**闭词表的单一属主**。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要一个模块而不是各处各写一份 `z.enum([...])`:同一张词表此前在四处各抄一份字面量
|
|
5
|
+
* (`approval-card.ts` 两腿的 offer schema、`http/routes/rules.ts` 的直通票口行形、
|
|
6
|
+
* `plugins/permission-rule-store-sql.ts` 的 `rules_json` 读/写形),core 给 `PersistedRuleMatch` 加两员
|
|
7
|
+
* (wildcard / subpath)、给 `PersistedRuleTool` 加 Read 时,四份抄件**没有一份**会编译红 —— zod 的 enum
|
|
8
|
+
* 只是运行期的一个 Set,静默把一次上游语义扩张翻成一整类合法行的拒收([ref] 词表纪律说的正是这形:
|
|
9
|
+
* 未知词必须是**编译**错,不是运行期一个安静的 false)。
|
|
10
|
+
*
|
|
11
|
+
* 🔴 每张表都带一条**双向全等钉**(`Exact<>`):元组的成员并 ≡ core 的联合型。core 加员 ⇒ 这里编译红,
|
|
12
|
+
* 由人裁定新词在每个消费面的处置(收 / 拒 / 剥),而不是让它在某一处静默落进「不认识」臂;本仓多写一个
|
|
13
|
+
* core 没有的词 ⇒ 同样红(词表属主是 core,本仓只转录)。
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ 转录**不是**判据:一个词在表里只说「这是 core 定义的一员」,不说「本仓每条腿都接受它」。逐腿的收窄
|
|
16
|
+
* (例如卡编辑臂对 `subpath` 的拒)写在那条腿自己的穷举 `switch` 里,并各带理由。
|
|
17
|
+
*/
|
|
18
|
+
import type { RuleApprovalRecord } from "@sema-agent/core";
|
|
19
|
+
/** `PersistedRuleTool`([ref] §2.2:`"Read"` 只说一种文法 —— `Read(//abs-dir/**)` 目录只读授权)。 */
|
|
20
|
+
export declare const PERSISTED_RULE_TOOLS: readonly ["Bash", "Read"];
|
|
21
|
+
/**
|
|
22
|
+
* `PersistedRuleMatch` 四员(core 顶注逐字):`exact` / `prefix`(历史 colon-star,永久兼容读、复合前缀形的
|
|
23
|
+
* 唯一拼写)/ `wildcard`([ref] 尾 space-star,单命令体上与 prefix **同一谓词**,建议面自此铸它)/
|
|
24
|
+
* `subpath`(Read 目录形,永不容纳任何命令)。
|
|
25
|
+
*/
|
|
26
|
+
export declare const PERSISTED_RULE_MATCHES: readonly ["exact", "prefix", "wildcard", "subpath"];
|
|
27
|
+
/** [ref] §2.3 B3:batch offer 成员的判别位(`command` = 历史逐段 Bash 规则;`directoryRead` = cd 段铸的目录只读授权)。 */
|
|
28
|
+
export declare const RULE_OFFER_BATCH_MEMBER_KINDS: readonly ["command", "directoryRead"];
|
|
29
|
+
/** [ref] §3.5:一段「为什么仍未被覆盖」的闭三词集(`redirection` 永久逐次 / `no_rule_form` 无可覆盖形 / `cap_overflow` 去重+帽挤出)。 */
|
|
30
|
+
export declare const UNCOVERED_SEGMENT_REASONS: readonly ["redirection", "no_rule_form", "cap_overflow"];
|
|
31
|
+
/** [ref] §3.3-5:卡编辑面的 breadth 警告码(记录 `edited.warnings` 存的就是这两个字面)。 */
|
|
32
|
+
export declare const EDITED_RULE_BREADTH_WARNING_CODES: readonly ["broad_prefix", "compound_prefix"];
|
|
33
|
+
/**
|
|
34
|
+
* [ref] §4.5 / [ref] §3.5 —— 审批记录的**形版本号**(core `RuleApprovalRecord.schema`,本批 2→3)。
|
|
35
|
+
* 类型绑 core 的字面型:core 再升号 ⇒ 这里编译红,两只 durable 店(SQL 双方言 + File)的 stale 判别与写侧钉
|
|
36
|
+
* 同批随之;判别是**等式**(`!==`)不是下限 —— 旧号的行按 stale 信封交还(不迁移、不 widen、不丢行),
|
|
37
|
+
* pending 卡短命,重触发即取新卡(B7/B8 既诺)。
|
|
38
|
+
*/
|
|
39
|
+
export declare const RULE_APPROVAL_RECORD_SCHEMA: RuleApprovalRecord["schema"];
|
|
40
|
+
//# sourceMappingURL=permission-rule-vocab.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const PERSISTED_RULE_TOOLS = ["Bash", "Read"];
|
|
2
|
+
const TOOLS_PIN = true;
|
|
3
|
+
void TOOLS_PIN;
|
|
4
|
+
export const PERSISTED_RULE_MATCHES = ["exact", "prefix", "wildcard", "subpath"];
|
|
5
|
+
const MATCHES_PIN = true;
|
|
6
|
+
void MATCHES_PIN;
|
|
7
|
+
export const RULE_OFFER_BATCH_MEMBER_KINDS = ["command", "directoryRead"];
|
|
8
|
+
const MEMBER_KINDS_PIN = true;
|
|
9
|
+
void MEMBER_KINDS_PIN;
|
|
10
|
+
export const UNCOVERED_SEGMENT_REASONS = ["redirection", "no_rule_form", "cap_overflow"];
|
|
11
|
+
const REASONS_PIN = true;
|
|
12
|
+
void REASONS_PIN;
|
|
13
|
+
export const EDITED_RULE_BREADTH_WARNING_CODES = ["broad_prefix", "compound_prefix"];
|
|
14
|
+
const WARNING_CODES_PIN = true;
|
|
15
|
+
void WARNING_CODES_PIN;
|
|
16
|
+
export const RULE_APPROVAL_RECORD_SCHEMA = 3;
|
|
17
|
+
//# sourceMappingURL=permission-rule-vocab.js.map
|
|
@@ -120,6 +120,18 @@ export interface PendingCheckpoint {
|
|
|
120
120
|
* 展示/分诊用,永不参与 resume / gate / CAS(core 同款 ECHO-ONLY 定性)。
|
|
121
121
|
*/
|
|
122
122
|
hasBidiControls?: true;
|
|
123
|
+
/**
|
|
124
|
+
* [ref]([ref]② core 点名 / [ref] 认领):**内容问句分型键** —— `"content_ask"` 当这条 tool_approval
|
|
125
|
+
* park 门住的是保留工具 AskUserQuestion(问用户的问题,不是副作用工具),消费端(cli/壳审批面)据它
|
|
126
|
+
* 渲问句 UI 而不用嗅探 `toolName`。词与判据 = core `summarizeCheckpoint` 的同一行规则(行上 core 铸的
|
|
127
|
+
* `toolName` 的**全函数**派生;词属主 `approval-content-kind.ts`,parity 钉看漂移)——**不是**
|
|
128
|
+
* `hasBidiControls` 那类「server 不重算」的扫描位:派生 ≠ 重扫。
|
|
129
|
+
* 🔴 `"content_ask"` 或**缺席**([ref]② OMIT 纪律,恒不铸 `null`/`false`):缺席 = 非内容问句门
|
|
130
|
+
* (普通工具门 / 非工具 park / toolName 缺席的存量行)。展示/分诊用,永不参与 resume / gate / CAS。
|
|
131
|
+
* 两条 durable 读面(`GET /v1/approvals` 行 + `/stream` `pending` 帧)经同一投影同时携带;live 腿的
|
|
132
|
+
* 同名键在 `LivePendingRow.contentKind`(同一词属主)。
|
|
133
|
+
*/
|
|
134
|
+
contentKind?: "content_ask";
|
|
123
135
|
}
|
|
124
136
|
/**
|
|
125
137
|
* [ref]([ref] / DEBTS [ref]):session 键读口(`findPendingTokenBySession` / `peekPendingScope`)的**行绑定**。
|
|
@@ -641,6 +653,20 @@ export declare class SqlCheckpointStore implements CheckpointStore {
|
|
|
641
653
|
* caller may only decide a checkpoint in their OWN scope. Returns `undefined` (no pending row), `null`
|
|
642
654
|
* (anonymous/dev session — open), or the owner scope string.
|
|
643
655
|
*/
|
|
656
|
+
/**
|
|
657
|
+
* [ref]:**按 token** 的 scope 轻读 —— `undefined`(无该行)/ `null`(匿名/开放)/ 属主 scope 串。
|
|
658
|
+
*
|
|
659
|
+
* 为什么必须有这一口(codex [ref] r1-[medium],验真后修):`peekPendingScope` 是**按 session** 的读,答的
|
|
660
|
+
* 是「此刻绑定下最新的那一行」;而 `get(token)` 读的是**那一个** token。表无 per-session pending 唯一
|
|
661
|
+
* 约束、两读又非事务 ⇒ 并发轮换窗内两者可以是不同的行。于是「按 session 轻读出的属主」不能拿来当
|
|
662
|
+
* 「可否把 `get(token)` 的失败细节回显给这位调用方」的准入位 —— 那正是 [ref] 要封的存在性 oracle 在
|
|
663
|
+
* TOCTOU 面上的复活形。本口把属主证明与**将要载入的那个 token** 原子绑定(同一 `WHERE token = ?`)。
|
|
664
|
+
*
|
|
665
|
+
* 刻意**不带** `status='pending'` 谓词:判据是「这一行是不是你的」,与它此刻处在哪一态无关(`get()`
|
|
666
|
+
* 自己也是裸 `WHERE token = ?`);加了反而在「刚被并发消费」的窗里把属主自己的诊断吞掉。
|
|
667
|
+
* 只 SELECT `scope` 一列 ⇒ 版本闸/坏 blob 都影响不到它(那是 `get()` 读 `version`/`checkpoint` 的事)。
|
|
668
|
+
*/
|
|
669
|
+
peekScopeByToken(token: CheckpointToken): Promise<string | null | undefined>;
|
|
644
670
|
peekPendingScope(sessionId: string, binding?: PendingRowBinding): Promise<string | null | undefined>;
|
|
645
671
|
/** Upsert the resume rebuild inputs for a session (written at SUBMIT when durable mode is on). */
|
|
646
672
|
putCtx(sessionId: string, ctx: unknown): Promise<void>;
|