@sema-agent/server 7.140.0 → 7.141.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 +290 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +75 -13
- package/dist/approval-batch-census.d.ts +27 -0
- package/dist/approval-batch-census.js +27 -0
- package/dist/approval-card.d.ts +31 -16
- package/dist/approval-card.js +6 -3
- package/dist/approval.d.ts +15 -0
- package/dist/approval.js +10 -0
- package/dist/audit.d.ts +4 -2
- package/dist/audit.js +2 -2
- package/dist/boot/device-lane.d.ts +11 -0
- package/dist/boot/device-lane.js +16 -0
- package/dist/boot/engine-lease.d.ts +24 -3
- package/dist/boot/engine-lease.js +9 -2
- package/dist/boot/execution-env.js +8 -4
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/memory-consolidation.d.ts +1 -1
- package/dist/boot/memory-consolidation.js +1 -1
- package/dist/boot/reapers.d.ts +2 -2
- package/dist/boot/resolve-spec.js +7 -4
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +4 -3
- package/dist/boot/shutdown.js +5 -0
- package/dist/boot/stage-05-execution-env.js +3 -1
- package/dist/boot/stage-06-runners.d.ts +0 -2
- package/dist/boot/stage-06-runners.js +1 -1
- package/dist/boot/stage-07-capability-layer.d.ts +0 -1
- package/dist/boot/stage-07-capability-layer.js +8 -10
- package/dist/boot/stage-08-reapers.d.ts +0 -1
- package/dist/boot/stage-09-leader.d.ts +0 -1
- package/dist/boot/stage-10-http-server.d.ts +0 -1
- package/dist/boot/stage-10-http-server.js +1 -0
- package/dist/boot/stores.js +10 -4
- package/dist/boot/web-admin.js +1 -0
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +9 -0
- package/dist/capability-facts.js +9 -0
- package/dist/card-void.d.ts +38 -0
- package/dist/card-void.js +23 -0
- package/dist/config-catalog.js +11 -8
- package/dist/config-provider.d.ts +13 -13
- package/dist/config-provider.js +19 -33
- package/dist/config-types.d.ts +26 -8
- package/dist/config.js +55 -46
- package/dist/delegated-session-owner.d.ts +21 -19
- package/dist/delegated-session-owner.js +1 -1
- package/dist/device-store.d.ts +7 -0
- package/dist/device-store.js +3 -0
- package/dist/execution-config-root.d.ts +22 -0
- package/dist/execution-config-root.js +35 -0
- package/dist/execution-lane-caps.d.ts +49 -2
- package/dist/execution-lane-caps.js +90 -13
- package/dist/execution-placement.d.ts +3 -0
- package/dist/execution-placement.js +7 -0
- package/dist/fleet/drain-entry.d.ts +3 -0
- package/dist/hosted-posture.d.ts +8 -0
- package/dist/hosted-posture.js +4 -0
- package/dist/http/admission.js +3 -3
- package/dist/http/caller-trust.d.ts +3 -2
- package/dist/http/caller-trust.js +2 -1
- package/dist/http/dispatch.js +4 -0
- package/dist/http/live-frame.d.ts +24 -0
- package/dist/http/live-frame.js +16 -0
- package/dist/http/resume-legs.js +37 -20
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/approvals-assistant.js +1 -0
- package/dist/http/routes/capabilities.js +5 -4
- package/dist/http/routes/diagnostics.d.ts +0 -19
- package/dist/http/routes/diagnostics.js +16 -2
- package/dist/http/routes/memory-bundle.d.ts +2 -2
- package/dist/http/routes/memory-bundle.js +1 -1
- package/dist/http/routes/memory-compliance.js +1 -1
- package/dist/http/routes/memory-policy.js +2 -2
- package/dist/http/routes/run-permission-mode.d.ts +75 -0
- package/dist/http/routes/run-permission-mode.js +170 -0
- package/dist/http/routes/session-sync.d.ts +3 -6
- package/dist/http/routes/session-sync.js +7 -2
- package/dist/http/routes/sessions.js +5 -4
- package/dist/http/routes/tasks.js +63 -66
- package/dist/http/run-read-face.js +3 -3
- package/dist/http/send.d.ts +1 -1
- package/dist/http/send.js +2 -2
- package/dist/http/server.d.ts +9 -2
- package/dist/http/sse-log.d.ts +3 -8
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +3 -1
- package/dist/instruction-source-notice.d.ts +7 -0
- package/dist/instruction-source-notice.js +6 -6
- package/dist/mcp-assertion-header.d.ts +4 -0
- package/dist/mcp-assertion-header.js +5 -0
- package/dist/memory-posture.d.ts +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +1 -1
- package/dist/memory-sync-client.d.ts +14 -2
- package/dist/memory-sync-client.js +80 -18
- package/dist/observability/fail-open.d.ts +30 -2
- package/dist/observability/fail-open.js +30 -2
- package/dist/observability/metrics.js +0 -1
- package/dist/observability/run-terminal.d.ts +6 -2
- package/dist/observability/run-terminal.js +25 -26
- package/dist/observability/secret-env-scrub.d.ts +20 -1
- package/dist/observability/secret-env-scrub.js +14 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
- package/dist/orchestration/workflow-completion-inbox.js +13 -5
- package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
- package/dist/orchestration/workflow-notify-journal.js +2 -1
- package/dist/permission-mode-admission.d.ts +68 -0
- package/dist/permission-mode-admission.js +23 -0
- package/dist/permission-rule-list.d.ts +55 -6
- package/dist/permission-rule-list.js +52 -5
- package/dist/plugins/caching-session-store.d.ts +6 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
- package/dist/plugins/checkpoint-store-sql.js +74 -3
- package/dist/plugins/device-store-sql.d.ts +5 -2
- package/dist/plugins/device-store-sql.js +4 -1
- package/dist/plugins/execution-placement-sql.d.ts +11 -1
- package/dist/plugins/execution-placement-sql.js +12 -1
- package/dist/plugins/file-run-store.d.ts +2 -2
- package/dist/plugins/file-run-store.js +1 -1
- package/dist/plugins/fork-routing-session-store.d.ts +10 -32
- package/dist/plugins/fork-routing-session-store.js +2 -61
- package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
- package/dist/plugins/k8s-exec-protocol.js +6 -1
- package/dist/plugins/local-checkpoint-store.d.ts +10 -1
- package/dist/plugins/local-checkpoint-store.js +22 -0
- package/dist/plugins/local-session-store.d.ts +227 -35
- package/dist/plugins/local-session-store.js +518 -37
- package/dist/plugins/local-session-window.d.ts +17 -3
- package/dist/plugins/local-session-window.js +7 -13
- package/dist/plugins/memory-run-store.d.ts +2 -2
- package/dist/plugins/memory-run-store.js +1 -1
- package/dist/plugins/one-time-migrations.d.ts +42 -2
- package/dist/plugins/one-time-migrations.js +65 -3
- package/dist/plugins/pg-pool.js +3 -2
- package/dist/plugins/pg-session-storage.d.ts +2 -0
- package/dist/plugins/pg-session-storage.js +3 -2
- package/dist/plugins/remote-env-e2b.d.ts +24 -3
- package/dist/plugins/remote-env-e2b.js +131 -18
- package/dist/plugins/remote-env-k8s.d.ts +20 -1
- package/dist/plugins/remote-env-k8s.js +148 -25
- package/dist/plugins/remote-env-local-docker.d.ts +25 -11
- package/dist/plugins/remote-env-local-docker.js +87 -21
- package/dist/plugins/run-store-sql.d.ts +3 -3
- package/dist/plugins/run-store-sql.js +4 -3
- package/dist/plugins/sandbox-read-floor.d.ts +273 -0
- package/dist/plugins/sandbox-read-floor.js +608 -0
- package/dist/plugins/session-ownership.d.ts +2 -1
- package/dist/plugins/session-placement.d.ts +17 -4
- package/dist/plugins/session-placement.js +2 -1
- package/dist/plugins/session-store.d.ts +18 -20
- package/dist/plugins/session-store.js +4 -12
- package/dist/plugins/store-backend.d.ts +31 -0
- package/dist/plugins/store-backend.js +21 -4
- package/dist/plugins/store-contracts.d.ts +9 -2
- package/dist/plugins/tidb-pool.js +2 -1
- package/dist/plugins/tidb-session-store.d.ts +4 -1
- package/dist/plugins/tidb-session-store.js +3 -2
- package/dist/run-cancel-context.d.ts +34 -0
- package/dist/run-cancel-context.js +7 -0
- package/dist/runs.d.ts +39 -12
- package/dist/runs.js +68 -53
- package/dist/security.d.ts +2 -2
- package/dist/server-secret-env.d.ts +2 -1
- package/dist/server-secret-env.js +2 -4
- package/dist/session-error-wire.d.ts +22 -4
- package/dist/session-error-wire.js +16 -0
- package/dist/session-incarnation.d.ts +9 -3
- package/dist/session-sync-kernel.d.ts +42 -1
- package/dist/session-sync-kernel.js +17 -0
- package/dist/session-sync.js +16 -4
- package/dist/sql-engine-coordinates.d.ts +49 -0
- package/dist/sql-engine-coordinates.js +27 -0
- package/dist/standing-answer-grant.d.ts +31 -2
- package/dist/standing-answer-grant.js +14 -2
- package/dist/store-posture.d.ts +1 -1
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +52 -25
- package/dist/task-settings.js +91 -28
- package/dist/terminal.d.ts +77 -20
- package/dist/terminal.js +66 -20
- package/dist/trace/core-keyset-guard.d.ts +79 -2
- package/dist/trace/exit-seat.d.ts +7 -0
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/model-segment.d.ts +2 -2
- package/dist/trace/redact.js +323 -90
- package/dist/trace/self-minted-wire.d.ts +168 -2
- package/dist/trace/self-minted-wire.js +63 -0
- package/dist/verifier-pause-card.d.ts +8 -0
- package/dist/verifier-pause-card.js +19 -0
- package/dist/web-admin-client/apply-effective.d.ts +5 -7
- package/dist/web-admin-client/apply-effective.js +2 -2
- package/dist/web-admin-client/candidate-gate.d.ts +1 -1
- package/dist/web-admin-client/read-warnings.d.ts +2 -2
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
- package/package.json +2 -2
- package/wire-changes/server@7.141.0.json +367 -0
|
@@ -27,14 +27,14 @@ export function createInstructionSourceSeat(sessionStore, emit) {
|
|
|
27
27
|
}
|
|
28
28
|
const describeSource = (source) => source ?? "no project instruction file";
|
|
29
29
|
export const BASELINE_IO_DEADLINE_MS = 2_000;
|
|
30
|
-
class
|
|
30
|
+
export class IoDeadlineExceeded extends Error {
|
|
31
31
|
}
|
|
32
|
-
function
|
|
32
|
+
export function withinIoDeadline(op) {
|
|
33
33
|
const running = Promise.resolve().then(op);
|
|
34
34
|
running.catch(() => undefined);
|
|
35
35
|
let timer;
|
|
36
36
|
const deadline = new Promise((_, reject) => {
|
|
37
|
-
timer = setTimeout(() => reject(new
|
|
37
|
+
timer = setTimeout(() => reject(new IoDeadlineExceeded(`no answer within ${BASELINE_IO_DEADLINE_MS}ms`)), BASELINE_IO_DEADLINE_MS);
|
|
38
38
|
timer.unref?.();
|
|
39
39
|
});
|
|
40
40
|
return Promise.race([running, deadline]).finally(() => clearTimeout(timer));
|
|
@@ -42,7 +42,7 @@ function withinDeadline(op) {
|
|
|
42
42
|
export async function announceInstructionSource(seat, sessionId, current) {
|
|
43
43
|
let baseline;
|
|
44
44
|
try {
|
|
45
|
-
baseline = await
|
|
45
|
+
baseline = await withinIoDeadline(() => seat.store.readInstructionSource(sessionId));
|
|
46
46
|
}
|
|
47
47
|
catch (e) {
|
|
48
48
|
degraded("read", sessionId, e);
|
|
@@ -65,14 +65,14 @@ export async function announceInstructionSource(seat, sessionId, current) {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
try {
|
|
68
|
-
await
|
|
68
|
+
await withinIoDeadline(() => seat.store.recordInstructionSource(sessionId, baseline, current));
|
|
69
69
|
}
|
|
70
70
|
catch (e) {
|
|
71
71
|
degraded("write", sessionId, e);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
function degraded(op, sessionId, e) {
|
|
75
|
-
const why = e instanceof
|
|
75
|
+
const why = e instanceof IoDeadlineExceeded ? `timeout=${BASELINE_IO_DEADLINE_MS}ms` : `err=${e instanceof Error ? e.message : String(e)}`;
|
|
76
76
|
recordFailOpen("server.instruction-source.announce-degraded", `op=${op} session=${sessionId} ${why}`);
|
|
77
77
|
}
|
|
78
78
|
//# sourceMappingURL=instruction-source-notice.js.map
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
import type { McpServerSpec } from "@sema-agent/core";
|
|
37
37
|
import type { RequestMcpTransport } from "@sema-agent/settings-schema";
|
|
38
38
|
import type { McpDraft } from "./peer-seat.js";
|
|
39
|
+
/** 请求面 `transport.kind` 的种类词(契约包请求传输形的判别键):`GET /v1/capabilities.mcpTransportKinds` 的元素型,也是自铸登记表 `mcpTransportKind` 集的键型。 */
|
|
40
|
+
export type McpRequestTransportKind = RequestMcpTransport["kind"];
|
|
39
41
|
/** 请求面 stdio 传输的形(契约包):引擎的 stdio 传输去掉凭据引用位、加上 `env`(件)。 */
|
|
40
42
|
export type RequestStdioMcpTransport = Extract<RequestMcpTransport, {
|
|
41
43
|
kind: "stdio";
|
|
@@ -66,6 +68,8 @@ export declare const MCP_HTTP_TRANSPORT_DISPOSITION: {
|
|
|
66
68
|
readonly principalHeader: "engine";
|
|
67
69
|
readonly url: "engine";
|
|
68
70
|
};
|
|
71
|
+
/** 位的值:请求面读器的传输种类(书写序 = 表序,冻结;能力位每次响应取一份拷贝)。 */
|
|
72
|
+
export declare const MCP_REQUEST_TRANSPORT_KINDS: readonly McpRequestTransportKind[];
|
|
69
73
|
/**
|
|
70
74
|
* 请求面条目 → 草稿:传输按它那一臂的处置表拆(`env` / `headers` 进件,`assertionHeader` 两边都不进,其余进声明);条目层的键原样进声明。件缺席或空 ⇒ 草稿不带件(登记时什么都不留)。
|
|
71
75
|
* 纯函数:不登记、不读部署状态;被扣下的 `assertionHeader` 由调用方另说一行 info。
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { splitDeclaration } from "./plain-string-record.js";
|
|
2
2
|
export const MCP_STDIO_TRANSPORT_DISPOSITION = { args: "engine", command: "engine", env: "seat", kind: "engine" };
|
|
3
3
|
export const MCP_HTTP_TRANSPORT_DISPOSITION = { assertionHeader: "withheld", headers: "seat", kind: "engine", principalHeader: "engine", url: "engine" };
|
|
4
|
+
const MCP_REQUEST_TRANSPORT_ARMS = {
|
|
5
|
+
stdio: MCP_STDIO_TRANSPORT_DISPOSITION,
|
|
6
|
+
http: MCP_HTTP_TRANSPORT_DISPOSITION,
|
|
7
|
+
};
|
|
8
|
+
export const MCP_REQUEST_TRANSPORT_KINDS = Object.freeze(Object.keys(MCP_REQUEST_TRANSPORT_ARMS));
|
|
4
9
|
export function toMcpDraft(spec) {
|
|
5
10
|
const { transport, ...entry } = spec;
|
|
6
11
|
const split = transport.kind === "http" ? splitDeclaration(transport, MCP_HTTP_TRANSPORT_DISPOSITION) : splitDeclaration(transport, MCP_STDIO_TRANSPORT_DISPOSITION);
|
package/dist/memory-posture.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export type MemoryVectorRung = "native" | "portable" | "lexical";
|
|
|
29
29
|
export type MemoryDarkReason =
|
|
30
30
|
/** operator 显式关停(`MEMORY_ENGINE=off`)。 */
|
|
31
31
|
"engine_off"
|
|
32
|
-
/** 多租户 + file 底座:file 基座无租户隔离,fail-closed 不点亮(改 `MEMORY_ENGINE_BACKEND=pg|
|
|
32
|
+
/** 多租户 + file 底座:file 基座无租户隔离,fail-closed 不点亮(改 `MEMORY_ENGINE_BACKEND=pg|mysql`)。 */
|
|
33
33
|
| "multi_tenant_file_backend"
|
|
34
34
|
/** 平面分裂车道:模型的文件工具在沙箱 fs、引擎在 worker fs,harvest 看不到沙箱写入 ⇒ fail-closed。 */
|
|
35
35
|
| "remote_lane_split";
|
package/dist/memory-scope.d.ts
CHANGED
|
@@ -46,7 +46,7 @@ export declare function memoryLayersNamedBy(config: Pick<ServiceConfig, "require
|
|
|
46
46
|
* **读列表的第二层**,不是写家:写家是该任务的**项目层**,由 {@link memoryProjectKeyFor} 按任务根派生、经
|
|
47
47
|
* {@link memorySpecForRequest} 的 `projectKey` 位铸进 spec(读 = [项目, 本 scope],`writeScope` 显式 = 项目)。
|
|
48
48
|
* 本函数在授权期调(还不知道任务根),所以项目层不在这里。
|
|
49
|
-
* ② MULTI-TENANT + DB 后端(`requirePrincipal === true` ∧ `memoryEngineBackend` ∈ {`pg`, `
|
|
49
|
+
* ② MULTI-TENANT + DB 后端(`requirePrincipal === true` ∧ `memoryEngineBackend` ∈ {`pg`, `mysql`}):按**验过的**
|
|
50
50
|
* principal 铸 v2 typed 键 —— 无 projectId ⇒ `user:<tenant>`(core `formatUserScope`),有形状合法的
|
|
51
51
|
* projectId ⇒ `proj:<tenant>/<projectId>`(core `formatProjScope`);principal 缺席(匿名 run)⇒ `undefined`
|
|
52
52
|
* (无记忆)。scope 列是租户分区,授权就是「验过的 principal 进、一个租户的 scope 出」这一绑定;身份永远不来自
|
|
@@ -128,7 +128,7 @@ export declare function memoryEngineRemoteLanePosture(config: ServiceConfig): {
|
|
|
128
128
|
* N0 启动告警的**文案**(纯,可单测;boot/stores.ts 的装配点只负责喂「引擎这一腿真的接上了吗」并打日志)。
|
|
129
129
|
*
|
|
130
130
|
* 为什么要按引擎真身分腿(2026-08-12 复扫,已核真):{@link memoryEngineRemoteLanePosture} 的 `dark` 只对
|
|
131
|
-
* **file** 引擎腿有裁决权 —— 车道门的唯一消费者是 {@link memoryEngineBackendFor}。`MEMORY_ENGINE_BACKEND=pg|
|
|
131
|
+
* **file** 引擎腿有裁决权 —— 车道门的唯一消费者是 {@link memoryEngineBackendFor}。`MEMORY_ENGINE_BACKEND=pg|mysql`
|
|
132
132
|
* 的两条腿在 boot/stores.ts 里**先于**任何车道判断就被点亮(库是持久真身,与手的文件平面无关),于是同一次
|
|
133
133
|
* 启动会先打 `memory_engine_enabled {enabled:true, backend:"pg"}`,几行之后再打一句「memory dark
|
|
134
134
|
* (fail-closed) … Set MEMORY_ENGINE_REMOTE_LANE=allow」—— 两句直接对撞,而且把运维指向一个在该腿上**不接线**
|
|
@@ -172,13 +172,13 @@ engineWired: boolean): {
|
|
|
172
172
|
* (壳布局 `~/.sema/engine-data`)」时分家 ⇒ 记忆库每一次 Write 被硬拒(7.58.0 放行 ⇒ 回归);反向
|
|
173
173
|
* (`CONFIG_LOCAL_DIR=~/.sema` 当 dataRoot)则让配置根自身免检。两根本就是两件事:店根归誊本门,引擎根归本函数。
|
|
174
174
|
*
|
|
175
|
-
* 形与 boot/stores.ts 逐字同律:DB 记忆腿(pg/
|
|
175
|
+
* 形与 boot/stores.ts 逐字同律:DB 记忆腿(pg/mysql)的根 = `<localDataRoot ?? localRoot>/memory-work`
|
|
176
176
|
* (per-worker 物化区);file 腿 = boot file 后端的链 `resolveMemoryEngineRoot(memoryEngineDir)`。**不判引擎是否
|
|
177
177
|
* 启用**——纯路径推导,禁用态下豁免根仍以「若开引擎会写到哪」为根(boot 的 `engineDataRoot` 恒铸,经共享基座交每只 Runner)。
|
|
178
178
|
*/
|
|
179
179
|
export declare function memoryEngineRootFor(config: Pick<ServiceConfig, "memoryEngineBackend" | "memoryEngineDir" | "localDataRoot">, localRoot?: string): string;
|
|
180
180
|
/** file 后端的根链(`MEMORY_ENGINE_DIR ?? fallback ?? $AGENT_DATA_DIR ?? ~/.ai-agent`)—— 与后端选择器**无关**:
|
|
181
|
-
* `memoryEngineBackendFor` 恒造 file 后端(run-local 从 env 继承 `MEMORY_ENGINE_BACKEND=pg|
|
|
181
|
+
* `memoryEngineBackendFor` 恒造 file 后端(run-local 从 env 继承 `MEMORY_ENGINE_BACKEND=pg|mysql` 时仍是 file),
|
|
182
182
|
* 所以它的根必须走这条链而不是 `memoryEngineRootFor` 的分派(codex 修复验证轮 [medium]:分派会把 run-local 的
|
|
183
183
|
* 记忆搬到 `<localDataRoot>/memory-work`,存量记忆与控制面升级后隐形)。 */
|
|
184
184
|
export declare function fileMemoryEngineRoot(config: Pick<ServiceConfig, "memoryEngineDir">, fallbackRoot?: string): string;
|
package/dist/memory-scope.js
CHANGED
|
@@ -29,7 +29,7 @@ export function memoryLayersNamedBy(config) {
|
|
|
29
29
|
export { MEMORY_SCOPE_COLUMN_CHARS, assertMemoryScopeWidth } from "./plugins/memory-key-guards.js";
|
|
30
30
|
export function memoryScopeFor(config, principal, projectId) {
|
|
31
31
|
if (config.requirePrincipal === true) {
|
|
32
|
-
if (config.memoryEngineBackend === "pg" || config.memoryEngineBackend === "
|
|
32
|
+
if (config.memoryEngineBackend === "pg" || config.memoryEngineBackend === "mysql") {
|
|
33
33
|
if (!principal)
|
|
34
34
|
return undefined;
|
|
35
35
|
const scope = projectId ? formatProjScope(principal, projectId) : formatUserScope(principal);
|
|
@@ -20,7 +20,7 @@ export interface MemorySyncLog {
|
|
|
20
20
|
*/
|
|
21
21
|
export declare function memorySyncPeerOf(url: string): string;
|
|
22
22
|
/**
|
|
23
|
-
* 起服日志行 `memory_sync_enabled` 的字段(两处起服 —— `boot/stores.ts` 与 `run-local.ts` —— 同读这一只)。`peer`
|
|
23
|
+
* 起服日志行 `memory_sync_enabled` 的字段(两处起服 —— `boot/stores.ts` 与 `run-local.ts` —— 同读这一只)。`peer` 是这一腿的对端身份(游标绑的就是它;排障时与停驻行的轴对读):
|
|
24
24
|
* 它已过 {@link memorySyncPeerOf} 的两道判(带凭据的身份起不来);仍过展示铸点({@link buildDisplaySafeUrl}),日志里的 URL 值一律经它 —— 两只判官
|
|
25
25
|
* (引擎的出口脱敏与本仓的展示形状律)不是同一份实现,本仓的那一只若遮了引擎放行的一段,这一行带遮记号而不是逐字(响亮,不漏)。
|
|
26
26
|
*/
|
|
@@ -46,6 +46,16 @@ export declare function createMemorySyncTransport(opts: {
|
|
|
46
46
|
/** 一层的游标文件路径:引擎根 `memoryEngineDir`(= `RunnerDeps.memoryEngineDir` 的那一个值)下 `sync-cursors/<scope 摘要>.json`。一层一只,不随对端变、不随店变。
|
|
47
47
|
* 落在子目录里:与更早的根下一层文件名 `sync-cursor-<peer>.json` 的空间不相交。 */
|
|
48
48
|
export declare function memorySyncCursorPath(memoryEngineDir: string, scope: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* ⑦ 停驻层的出路(闭集;今天唯一成员)。`delete_cursor` = 删掉这一层的游标文件,下一轮从空基线重收敛。读面 `memorySync.parked[].wayOut` 与运维行同读这只词;
|
|
51
|
+
* 加一个词 ⇒ {@link wayOutSentence} 的穷尽 switch 编译红(句子先写好,词才上得了 wire)。
|
|
52
|
+
*/
|
|
53
|
+
export type MemorySyncWayOut = "delete_cursor";
|
|
54
|
+
/** ⑦ 一层停驻的读数(读面与运维行同读这一只记录;不带盘路径 —— 路径只进运维行)。 */
|
|
55
|
+
export interface MemorySyncParkedLayer {
|
|
56
|
+
readonly scope: string;
|
|
57
|
+
readonly wayOut: MemorySyncWayOut;
|
|
58
|
+
}
|
|
49
59
|
/** M31 一次的读数(测试按它断言)。`window: "closed"` ⇒ 一行都没读。 */
|
|
50
60
|
export interface SyncCursorRelocationM31Report {
|
|
51
61
|
readonly window: "open" | "closed";
|
|
@@ -56,7 +66,7 @@ export interface SyncCursorRelocationM31Report {
|
|
|
56
66
|
* ⑤ 一次性迁移 M31:把升级前的游标搬到各自那一层的路径(`rename`,字节不变;只搬不改,判归引擎)。`today` = UTC 日历日;窗关 ⇒ 不搬。
|
|
57
67
|
* · 上一版的分区目录 `sync-cursors/central/<scope 摘要>.json` ⇒ `sync-cursors/<同名>`(两种布局的文件名是同一只 scope 摘要);搬空的目录删掉;
|
|
58
68
|
* 名字不是摘要形的(写游标的 staging 残片)原地不动。
|
|
59
|
-
* · 更早的 `sync-cursor-central.json` ⇒ 它记的那一层的路径;读不成对象 / 没有 scope ⇒ 原地不动、一行 warn(
|
|
69
|
+
* · 更早的 `sync-cursor-central.json` ⇒ 它记的那一层的路径;读不成对象 / 没有 scope ⇒ 原地不动、一行 warn(没法说它是哪一层的基线;句子只点名删掉它,⑦ 一出路律)。
|
|
60
70
|
* · 目标路径上已有游标 ⇒ 不搬、不删,一行 warn(这一层用路径上的那一份;留下的那只没有读者)。
|
|
61
71
|
* 与同一数据根上并跑的另一个进程(server + run-local)同搬:源已被对方搬走(`ENOENT`)⇒ 跳过。残余(如实):先查目标再 `rename` 之间若有写者在目标路径上
|
|
62
72
|
* 落下游标,会被盖掉 —— 写者只有本模块的 runner,而 runner 写任何游标之前先跑完本步。别的读写错 ⇒ 抛(这一轮整轮失败、下一轮再搬)。
|
|
@@ -86,6 +96,8 @@ export interface MemorySyncRunner {
|
|
|
86
96
|
exclusive<T>(work: () => Promise<T>): Promise<T>;
|
|
87
97
|
/** 某一层的 cursor sidecar 路径(boot 日志/测试可见性)。 */
|
|
88
98
|
cursorPathFor(scope: string): string;
|
|
99
|
+
/** ⑦ 停驻的层(最近一轮被拒在游标上的那些;按轮序:同步层在前、项目层按键)。停驻事实的唯一读口 —— 读面与运维行读同一份。 */
|
|
100
|
+
parked(): readonly MemorySyncParkedLayer[];
|
|
89
101
|
/** 排队+在飞的轮数(测试的确定性等待面;0 = 完全空闲)。 */
|
|
90
102
|
pending(): number;
|
|
91
103
|
}
|
|
@@ -53,10 +53,54 @@ function scopeDigest(scope) {
|
|
|
53
53
|
export function memorySyncCursorPath(memoryEngineDir, scope) {
|
|
54
54
|
return join(memoryEngineDir, SYNC_CURSOR_DIR, `${scopeDigest(scope)}.json`);
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
const CURSOR_AXES = { scope: true, peer: true, store: true };
|
|
57
|
+
const CURSOR_FOUND = { absent: true, other: true };
|
|
58
|
+
function wayOutSentence(wayOut, cursorPath) {
|
|
59
|
+
switch (wayOut) {
|
|
60
|
+
case "delete_cursor":
|
|
61
|
+
return (`one way out: delete the cursor file ${cursorPath} — the next round re-converges from an empty baseline: it pulls what the center holds for this layer, ` +
|
|
62
|
+
"pushes what this store holds and pushes no deletes; entries changed on both sides are reported as conflicts, and an entry deleted on one side since the last round comes back from the other side");
|
|
63
|
+
default: {
|
|
64
|
+
const unhandled = wayOut;
|
|
65
|
+
throw new Error(`memory sync: no sentence for way out ${String(unhandled)}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function parkCauseSentence(cause) {
|
|
70
|
+
switch (cause.kind) {
|
|
71
|
+
case "cursor_refused":
|
|
72
|
+
return cause.detail === undefined
|
|
73
|
+
? "the sync engine refused its cursor"
|
|
74
|
+
: `the sync engine refused its cursor on the ${cause.detail.axis} axis (${cause.detail.found === "absent" ? `the cursor has no ${cause.detail.axis} binding` : `the cursor is bound to another ${cause.detail.axis}`})`;
|
|
75
|
+
case "cursor_unusable":
|
|
76
|
+
return `the cursor file cannot be read as a sync cursor (${cause.why})`;
|
|
77
|
+
default: {
|
|
78
|
+
const unhandled = cause;
|
|
79
|
+
throw new Error(`memory sync: unknown park cause ${JSON.stringify(unhandled)}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
class MemorySyncLayerParked extends Error {
|
|
84
|
+
scope;
|
|
85
|
+
cursorPath;
|
|
86
|
+
parkCause;
|
|
87
|
+
wayOut;
|
|
88
|
+
constructor(scope, cursorPath, parkCause, wayOut = "delete_cursor") {
|
|
89
|
+
super(`memory sync: layer ${JSON.stringify(scope)} parked — ${parkCauseSentence(parkCause)}; ${wayOutSentence(wayOut, cursorPath)}`);
|
|
90
|
+
this.scope = scope;
|
|
91
|
+
this.cursorPath = cursorPath;
|
|
92
|
+
this.parkCause = parkCause;
|
|
93
|
+
this.wayOut = wayOut;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function cursorRefusalOf(err) {
|
|
97
|
+
if (err === null || typeof err !== "object" || err.code !== "config.memory_sync_cursor_mismatch")
|
|
98
|
+
return undefined;
|
|
99
|
+
const d = err.detail;
|
|
100
|
+
const axis = d?.axis;
|
|
101
|
+
const found = d?.found;
|
|
102
|
+
const known = typeof axis === "string" && Object.hasOwn(CURSOR_AXES, axis) && typeof found === "string" && Object.hasOwn(CURSOR_FOUND, found);
|
|
103
|
+
return { detail: known ? { axis, found } : undefined };
|
|
60
104
|
}
|
|
61
105
|
function cursorOf(parsed) {
|
|
62
106
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
@@ -86,11 +130,11 @@ async function loadLayerBaseline(path, scope) {
|
|
|
86
130
|
parsed = JSON.parse(raw);
|
|
87
131
|
}
|
|
88
132
|
catch {
|
|
89
|
-
throw
|
|
133
|
+
throw new MemorySyncLayerParked(scope, path, { kind: "cursor_unusable", why: "not JSON" });
|
|
90
134
|
}
|
|
91
135
|
const cursor = cursorOf(parsed);
|
|
92
136
|
if (typeof cursor === "string")
|
|
93
|
-
throw
|
|
137
|
+
throw new MemorySyncLayerParked(scope, path, { kind: "cursor_unusable", why: cursor });
|
|
94
138
|
return { kind: "cursor", cursor };
|
|
95
139
|
}
|
|
96
140
|
async function saveCursor(path, cursor) {
|
|
@@ -190,7 +234,7 @@ export async function relocateSyncCursorsM31(memoryEngineDir, log, opts) {
|
|
|
190
234
|
const scope = scopeOfLegacyCursor(raw);
|
|
191
235
|
if (scope === undefined) {
|
|
192
236
|
left += 1;
|
|
193
|
-
log.warn("memory_sync_cursor_not_relocated", { path: legacyFile, note: "a cursor file written by an earlier version that is not a JSON object naming its scope, so no layer can take it as its baseline
|
|
237
|
+
log.warn("memory_sync_cursor_not_relocated", { path: legacyFile, note: "a cursor file written by an earlier version that is not a JSON object naming its scope, so no layer can take it as its baseline and no round reads it; delete it" });
|
|
194
238
|
}
|
|
195
239
|
else {
|
|
196
240
|
await move(legacyFile, memorySyncCursorPath(memoryEngineDir, scope));
|
|
@@ -229,15 +273,23 @@ export function createMemorySyncRunner(opts) {
|
|
|
229
273
|
let subRound = 0;
|
|
230
274
|
for (;;) {
|
|
231
275
|
subRound += 1;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
276
|
+
try {
|
|
277
|
+
result = await syncMemoryScope({
|
|
278
|
+
backend: storeOf(scope),
|
|
279
|
+
scope,
|
|
280
|
+
transport: opts.transport,
|
|
281
|
+
...(cursor !== undefined ? { cursor } : {}),
|
|
282
|
+
...(opts.now !== undefined ? { now: opts.now } : {}),
|
|
283
|
+
...(opts.maxPushEntries !== undefined ? { maxPushEntries: opts.maxPushEntries } : {}),
|
|
284
|
+
...(opts.maxPullEntries !== undefined ? { maxPullEntries: opts.maxPullEntries } : {}),
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
const refusal = cursorRefusalOf(err);
|
|
289
|
+
if (refusal !== undefined)
|
|
290
|
+
throw new MemorySyncLayerParked(scope, cursorPath, { kind: "cursor_refused", detail: refusal.detail });
|
|
291
|
+
throw err;
|
|
292
|
+
}
|
|
241
293
|
for (const w of result.warnings ?? [])
|
|
242
294
|
opts.log.warn("memory_sync_warning", { scope, subRound, warning: w });
|
|
243
295
|
if (!(result.pushTruncated === true || result.pullTruncated === true))
|
|
@@ -289,6 +341,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
289
341
|
return result;
|
|
290
342
|
};
|
|
291
343
|
let relocated = false;
|
|
344
|
+
let parkedLayers = [];
|
|
292
345
|
const round = async (reason) => {
|
|
293
346
|
if (!relocated) {
|
|
294
347
|
await relocateSyncCursorsM31(memoryEngineDir, opts.log, { today: new Date(now()).toISOString().slice(0, 10) });
|
|
@@ -296,15 +349,24 @@ export function createMemorySyncRunner(opts) {
|
|
|
296
349
|
}
|
|
297
350
|
const layers = [];
|
|
298
351
|
const failures = [];
|
|
352
|
+
const parkedNow = [];
|
|
299
353
|
for (const scope of layersOfRound()) {
|
|
300
354
|
try {
|
|
301
355
|
layers.push({ scope, result: await syncLayer(scope, reason) });
|
|
302
356
|
}
|
|
303
357
|
catch (err) {
|
|
304
358
|
failures.push({ scope, err });
|
|
305
|
-
|
|
359
|
+
if (err instanceof MemorySyncLayerParked) {
|
|
360
|
+
parkedNow.push({ scope, wayOut: err.wayOut });
|
|
361
|
+
const refused = err.parkCause.kind === "cursor_refused" ? err.parkCause.detail : undefined;
|
|
362
|
+
opts.log.warn("memory_sync_layer_parked", { reason, scope, cursorPath: err.cursorPath, ...(refused !== undefined ? { axis: refused.axis, found: refused.found } : {}), wayOut: err.wayOut, note: err.message });
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
opts.log.warn("memory_sync_layer_failed", { reason, scope, cursorPath: memorySyncCursorPath(memoryEngineDir, scope), err: String(err) });
|
|
366
|
+
}
|
|
306
367
|
}
|
|
307
368
|
}
|
|
369
|
+
parkedLayers = Object.freeze(parkedNow);
|
|
308
370
|
if (failures.length > 0) {
|
|
309
371
|
throw new Error(`memory sync round failed for ${failures.length} layer(s): ${failures.map((f) => `${f.scope}: ${f.err instanceof Error ? f.err.message : String(f.err)}`).join("; ")}`);
|
|
310
372
|
}
|
|
@@ -342,7 +404,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
342
404
|
tracked.add(scope);
|
|
343
405
|
queueRound("track");
|
|
344
406
|
};
|
|
345
|
-
return { syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(memoryEngineDir, scope), pending: () => pending };
|
|
407
|
+
return { syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(memoryEngineDir, scope), parked: () => parkedLayers, pending: () => pending };
|
|
346
408
|
}
|
|
347
409
|
export function startMemorySyncRunner(deps) {
|
|
348
410
|
const { sync } = deps;
|
|
@@ -41,7 +41,7 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
41
41
|
};
|
|
42
42
|
readonly "server.tool-end-media.live-backlog-degraded": {
|
|
43
43
|
readonly cls: "F";
|
|
44
|
-
readonly note: "core 7.50.0 媒体块提货(`trace/tool-output-media.ts` 的 `liveToolEndForBacklog`,三处活面写口共用的那一只判据 —— 同步流出口 `
|
|
44
|
+
readonly note: "core 7.50.0 媒体块提货(`trace/tool-output-media.ts` 的 `liveToolEndForBacklog`,三处活面写口共用的那一只判据 —— 同步流出口 `writeLiveFrame`、子代 tail 路由写环、tail 总线每订阅者队列):本帧媒体字节(`output` 媒体块的 `data` 与 `structured` 媒体卡的 `file.base64` 一起计)+ 这条连接 / 这个订阅者还没送出去的字节 > 上界(64 MiB)⇒ 本帧媒体降成持久面那只形(媒体块换描述符 `{ type, mimeType?, bytes, truncated: true }`,帧级 `truncated` + `totalChars`;媒体卡去掉 `file.base64`)。每降一帧记一次,只在这只函数里记(三处写口不各记)。放行的最坏后果 = 一个读得慢的观众在 SSE 上看不到这张图,只见「这里有一张多大的什么图、没送达」(账本面本就恒是描述符),不是门、不是凭据、不改任何裁决;反方向(不降)让一条停读的连接随帧数线性压住几百 MiB。帧上它与引擎自己的媒体预算降级同形不可分,所以计数是运维唯一能看出「多少观众丢了图」的位;计数持续非零 = 有消费端读流太慢(或网络瓶颈),去看那几条连接。detail = `backlog=<待发字节> media=<本帧媒体字节>`。";
|
|
45
45
|
};
|
|
46
46
|
readonly "server.memory-consolidation.operator-valve-layers-unfed": {
|
|
47
47
|
readonly cls: "P-DEBT";
|
|
@@ -171,6 +171,10 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
171
171
|
readonly cls: "F";
|
|
172
172
|
readonly note: "design/382 §3.5(S-44;core 7.55.0 #1468 P1 阶段二起读者是 core `readRuleOffers`,经 `approval-card.ts` 的 `readRuleOfferList`,两条腿同调):batch offer 的 `uncoveredDetail` 明细座不是「每个计数段一行良构行」(集外 reason / 非数组 / 行数与 count 座不等 —— core 读者判丢、读数带 `uncoveredDetailDropped`)或行数超本仓展示帽 MAX_UNCOVERED_DETAIL_ROWS ⇒ 只丢这一座、batch 本体照投。放行的最坏后果 = 卡上少几行「这段为什么还会问」的解释(count 座仍在,core 契约明写明细缺席不是断言);把整只 batch 判假才是错的方向(一只真可兑的合取批从卡上消失)。纯展示轴 ⇒ F 类;必须留痕,否则上游明细座漂形在遥测里零痕迹。detail = `index=<core 列表里的下标>` + 丢的缘由(不带段文本)。";
|
|
173
173
|
};
|
|
174
|
+
readonly "server.approval-batch.kindless-row": {
|
|
175
|
+
readonly cls: "F";
|
|
176
|
+
readonly note: "起服普查(`approval-batch-census.ts`;SQL 两方言同一只读口 `SqlCheckpointStore.scanKindlessBatchRows`):一行审批停驻行(`checkpoint` 表,所有状态)的规则提议里,有 batch offer 带着**无 `kind` 的存量成员** —— 引擎读者(`readRuleOffers`)为这个旧形留着一条存量读臂(无 `kind`、四个命令文本键齐的成员读成 `command`,并给那只 offer 打 `kindlessForm` 记号),本仓用 `countKindlessBatchOffers` 数这个记号。载体两个:`rule_suggestions` 列、blob 里的 `pendingAction.ruleOffers`(detail 的 `carriers=` 点名是哪个)。每个带旧形成员的行记一次(逐次计数 + 探针行 + 每进程一次 warn),起服另出一行 info `approval_batch_kindless_rows { count }`。放行的最坏后果 = 无新增放行:这是读侧存量兼容臂被数的位,读法本来就是『旧形读成 command』,普查不改任何一行的读法、不翻行、不删臂;留痕的唯一目的是让『还有几行靠这条臂』可证 —— 计数为零(整个部署的每个副本起服都是零)才能由后续一版删那条存量臂;非零 = 还有行靠它,不可删,detail 的 `row=` 是行指纹(不是 token)。F 类(读侧兼容、纯展示轴:这条臂读出的是「不再询问」候选的卡面,不是门)。逐页读、不是原子快照(页间别的副本的写入可以让一行被漏数或多数一行);滚动升级窗里停在旧版本的副本之后写的旧形行,下次起服才数得到。后台跑、不挡起服就绪。";
|
|
177
|
+
};
|
|
174
178
|
readonly "server.checkpoint.inbox-risk-axes-damaged": {
|
|
175
179
|
readonly cls: "F";
|
|
176
180
|
readonly note: "core 7.55.0 `summarizeCheckpoint` 的第二参(`plugins/checkpoint-store-sql.ts` 的 `listByScope`:supervisor inbox / 调度视图的按 scope 待决投影):一行审批门的风险描述符读穿了一格坏轴 —— `axes` 不是普通对象,或某个标记(`egress` / `irreversible` / `shell`)在场却不是布尔(手改 / 第三方写者;引擎铸的行没有这一形)⇒ 行照投,`severity` 取有效标记给出的下界,证不出就缺席;core 交一句话给店的座,本座逐行计一次。放行的最坏后果 = 这一行在按 severity 倒排的 inbox 里排位偏低或无档(分诊材料,不是门;resume 的判据不读它)。不放行的代价是一行坏描述符打掉整个 scope 的 inbox(修前:非对象描述符抛 TypeError 出投影)。修前不传座 ⇒ 降档是静默的。detail = core 的句子(按 checkpoint id 与会话 JSON 点名、按形说成员,不带令牌、不带值);每次列 inbox 都会再说一次,直到那一行被修或被决。LOCAL 车道的同一句经 backend 的 `file_store_corrupt_read` 座(core File 店自带),不走本 tag。";
|
|
@@ -193,7 +197,7 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
193
197
|
};
|
|
194
198
|
readonly "server.approvals.park-row-blob-unreadable": {
|
|
195
199
|
readonly cls: "F";
|
|
196
|
-
readonly note: "S-700(`plugins/checkpoint-store-sql.ts` 的 `readParkRowActionCell`):运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint` 列里寄存行的 **`pendingAction` 子树**(NP-3 / 7.100.0 起引擎内只取这一棵、整只快照不出库;只为 park 行事实 `requiresRealApproval` / `ruleOffersAbsence` / `mandated`(S-663 二段,core #1079)/ `mandate`(S-721)/ `origin`(S-755)而读 —— 事实位的真源是 `approval-card.ts` 的 `ParkRowFacts`)**在场却不是一只对象**(JSON `null` 字面 / 串 / 数 —— 手改 / 迁移脚本改过)。子树**缺席**(SQL NULL:成员不在,或整只文档不是对象 —— 后者 `get()` 照旧拒;MariaDB 文本承载上的非 JSON 字节同样答 SQL NULL)⇒ 事实缺席、不计数。放行的最坏后果 = **这一行**的 park 行事实(`ParkRowFacts` 各位)缺席(展示/分诊材料;core 定性 display metadata,resume 的带子读 gate 自己的位),队列其余部分照常 —— 而这一行本来就 resume 不了(`get()` 对同一格拒)。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 blob 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),与同一 `rows.map` 里 `rule_suggestions` / `risk_descriptor` 两格同族同判。故 F 类;tag 与那两格**分开计**(哪一格在坏要读得出来)。计数非零 = 这一行的 decide 同样读不动(`get()` 对同一格拒),去按 session 查那一行。 (`member=gate` 那第二座随 core 7.55.0 #1468 P1 阶段二退役:队列行的卡与到期分法改读 `approval_card` / `gate_kind` 两列 —— 写口与 M29 回填同一只派生落下的格 —— 本读面不再读 blob 的 `gate` 子树。)S-2076 起同 tag 另有两座同判据的臂(`SqlCheckpointStore.readParkedSkillCardFacts
|
|
200
|
+
readonly note: "S-700(`plugins/checkpoint-store-sql.ts` 的 `readParkRowActionCell`):运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint` 列里寄存行的 **`pendingAction` 子树**(NP-3 / 7.100.0 起引擎内只取这一棵、整只快照不出库;只为 park 行事实 `requiresRealApproval` / `ruleOffersAbsence` / `mandated`(S-663 二段,core #1079)/ `mandate`(S-721)/ `origin`(S-755)而读 —— 事实位的真源是 `approval-card.ts` 的 `ParkRowFacts`)**在场却不是一只对象**(JSON `null` 字面 / 串 / 数 —— 手改 / 迁移脚本改过)。子树**缺席**(SQL NULL:成员不在,或整只文档不是对象 —— 后者 `get()` 照旧拒;MariaDB 文本承载上的非 JSON 字节同样答 SQL NULL)⇒ 事实缺席、不计数。放行的最坏后果 = **这一行**的 park 行事实(`ParkRowFacts` 各位)缺席(展示/分诊材料;core 定性 display metadata,resume 的带子读 gate 自己的位),队列其余部分照常 —— 而这一行本来就 resume 不了(`get()` 对同一格拒)。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 blob 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),与同一 `rows.map` 里 `rule_suggestions` / `risk_descriptor` 两格同族同判。故 F 类;tag 与那两格**分开计**(哪一格在坏要读得出来)。计数非零 = 这一行的 decide 同样读不动(`get()` 对同一格拒),去按 session 查那一行。 (`member=gate` 那第二座随 core 7.55.0 #1468 P1 阶段二退役:队列行的卡与到期分法改读 `approval_card` / `gate_kind` 两列 —— 写口与 M29 回填同一只派生落下的格 —— 本读面不再读 blob 的 `gate` 子树。)S-2076 起同 tag 另有两座同判据的臂(`SqlCheckpointStore.readParkedSkillCardFacts`:对行上可能带技能卡两位之一的行,整只快照读回的体坏、或体是对象但 core 的摘要读不动它 ⇒ 只丢**这一行**的两位〔`boundGrants` / `classifierNoRuling`〕,授予绑定那一臂〔`boundGrantDigest` / `boundGrantAbsence`〕读自子树、照在(S-1612),队列照常;`not-text` 同样原样抛)—— 计数非零的处置同上:去按 session 查那一行。";
|
|
197
201
|
};
|
|
198
202
|
readonly "server.approvals.durable-row-checkpoint-id-missing": {
|
|
199
203
|
readonly cls: "F";
|
|
@@ -287,6 +291,14 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
287
291
|
readonly cls: "F";
|
|
288
292
|
readonly note: "会话行的位置与项目那一格(`session_meta.workspace` / local `session-workspaces.json`;唯一写者 `session-workspace.ts` 的腿记录座,唯一列属主 `plugins/session-workspace-cell.ts`;detail 的 `op` 点名哪一步):`op=write` 一次写失败(库抖 / 盘满 / 锁等待超时)⇒ 行停在上一帧,下一帧照写;`op=read` 一格读不出(被带外改坏)⇒ 读作没有记录,下一帧整格重写;`op=frame` 一帧投不成存储形(引擎不会铸出这种帧)⇒ 这一帧不记。这一格是宿主对**引擎判过的答案**的记录,不是判据 —— 引擎每条腿都重判项目(有记号就判记号,声明不等响亮拒)。放行的最坏后果 = 这只会话的下一条不带 `projectId` 的提交续声明的是旧的项目或不声明(没有记号的检出里这只会话少一次续声明);不放行的代价是把一条在跑的腿、或这只会话往后的每一次提交,为一份记录摔掉。计数非零 = 这台部署的会话店写不动 / 有人改过库。";
|
|
289
293
|
};
|
|
294
|
+
readonly "server.local-session-row.created-at-absent": {
|
|
295
|
+
readonly cls: "F";
|
|
296
|
+
readonly note: "local 会话店(`plugins/local-session-store.ts` 的 `rowCreatedAtOf`,行上建档时刻的唯一读法 —— 列表行 `firstActivityAt`、会话视图的 `createdAt`、交给引擎的会话元数据同读它):会话元数据边档的那一格没有建档时刻(补铸的格:盘上有存档而边档里没有它的格;或一次性迁移 M33 没走过的数据根 —— 跳版升上来的,或窗关之后才出现的这一形)⇒ 读作纪元(1970-01-01T00:00:00.000Z),与修前「存档头没有建档时刻」那一降级同值;不读存档头(那是同一事实的第二个读者)。放行的最坏后果 = 这只会话在按建档时刻排的列表里落到最后、视图上的建档时刻是纪元 —— 装饰性读数,不开任何门、不进任何判据。**计数单位 = 一次读到缺席**(同一只会话每次被列出 / 打开再数一次;累计计数是本进程的观察次数,现存多少读边档),探针行带 `session=<id>`。";
|
|
297
|
+
};
|
|
298
|
+
readonly "server.session-sync.dangling-anchor-export": {
|
|
299
|
+
readonly cls: "F";
|
|
300
|
+
readonly note: "会话同步的 manifest 导出(`session-sync.ts` 的 `exportSessionManifest`,`GET /v1/sessions/:id/sync/manifest` 与对等同步的一切 manifest 读者共用):源店里存着一条续跑锚,它指向的条目不在这份 manifest 自己的 `entryIds` 里(悬空锚 —— 一个更早的版本在导入口收下过指向声明集外的锚;现行导入口已不再写出这种锚)⇒ 这一条不进导出体,店里的行不动。判的是入口门用的同一只谓词(`session-sync-kernel.ts` 的 `anchorEntryDeclared`)。放行的最坏后果 = 对端少收一条本来就解不到条目的锚(续跑到它只会落到「没有这个条目」);不丢的代价是这份 manifest 原样推回时被导入口点名拒、这只会话从此推不出去。**计数单位 = 一条被丢掉的锚的一次导出**(累计计数是本进程自起动以来的观察次数;同一条存量悬空锚行留着、没有单条删除口,每次导出再数一次),探针行带 `sessionId` / `eventId` / `entryId`(JSON 引号包住、各截到 96 字,存量里的 id 是任意串);计数非零 = 本进程导出时碰到过存量悬空锚,现存多少读锚表,不读计数。";
|
|
301
|
+
};
|
|
290
302
|
readonly "server.engine-notice.wire-sink-threw": {
|
|
291
303
|
readonly cls: "F";
|
|
292
304
|
readonly note: "#310:`engine_notice` 分流器把一条白名单通告投给某条 run 腿的登记口时,那只口抛了(live 口写向已断/已撕裂的 SSE socket,或 durable 口的账本写同步抛)。放行的最坏后果 = **这一条通告的这一个终点**缺席:①日志终点在分流之前已经逐字打过(事实一条不丢);②两个终点注册成两只独立 sink ⇒ live 抛不牵连 durable(断连后仍看得见的那半保住);③同会话其余口照投。故 F 类。不放行的代价是把异常回抛给 core 的 `deliverEngineNotice` —— 它会吞掉,于是同一次失败**既没有留痕也没有第二只口**,正是本 tag 要根除的形。留痕是承重的:静默吞掉之后「wire 腿为什么总有几条通告不到」在遥测里与「core 本来就没发」同形。";
|
|
@@ -557,10 +569,26 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
557
569
|
readonly cls: "F";
|
|
558
570
|
readonly note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备 / 提交脚本删一只私有暂存目录(`.<名>.stage-<十六进制>/`,生来 0700;只删这次写放进去的 `data` 与这只空目录,从不递归删)没删掉、在 stdout 写一条记录点名 —— 五处:`mv` 已把暂存件换到目标上之后删当前那只;跟一跳到别的目录重新暂存成功之后删原来那只;重新暂存却没用上(收紧不成 / 拷不成)的那一只;就地拷入 / 追加时删暂存名失败、或写完关掉 fd 后删私有目录失败(有的文件系统为仍打开着的文件留隐藏项:NFS / libfuse / SMB);准备时收不成私有 / 里面已有 `data` 的那一只。目录里多出了不是这次写放进去的东西(同一身份或 root 的别的进程往里写过 / 收紧之前别人放进来的)、目录被换成了符号链接、或文件系统拒删。写照它自己的结局答(提交已成 ⇒ 成功:答失败会让调用方以为目标没变),残留只在这里留痕。放行的最坏后果 = 目标(或链接所指)目录里多一只隐藏目录(空的,或装着别人放进去的东西;重新暂存没用上的那只可能装着这次写的半份新内容),只有写入方进得去;不是门、不是凭据、不碰目标内容。计数非零 = 那台目标机上有东西在往别人的暂存目录里写,或删不动,去看那只目录。detail = 残留目录的路径(一次写最多两只,逐只计)。";
|
|
559
571
|
};
|
|
572
|
+
readonly "server.resume-anchor.capture-failed": {
|
|
573
|
+
readonly cls: "F";
|
|
574
|
+
readonly note: "回退锚(`resumeAt` 的提示词 / 助手文本手柄)写入失败 —— 三条腿同一只写法 `runs.ts` 的 `settleResumeAnchor`:流式腿(`POST /v1/tasks/stream`,done 帧之前 / 断连收尾时 await)、后台腿(`POST /v1/runs`)、续跑腿。锚店写抛(库抖 / 表缺 / 盘满),或单行店操作期限(`BASELINE_IO_DEADLINE_MS`,2 s;与项目指令基线读写同一只数、同一只 `withinIoDeadline`)内没落定(锚店挂住:锚店的写口自己不带期限)⇒ 这一次提示词(或这一轮助手文本)此刻没有回退手柄,run 照常收尾、终帧照发、不改任何终局;到期只放手不取消,那次写照常在飞、迟到落定的锚照样可用(迟到的失败被吞,不重复计数)。放行的最坏后果 = 用户拿这次 run 的 taskId 去 `resumeAt` 得 404 `resume_at.unknown_event`、会话视图里那条用户消息行不带 `taskId` 手柄(用户面的读数只有这一处真源,本臂不另铸第二份);不是门、不是凭据。不放行(抛)的代价是让一次锚写失败把整条已经跑完的 run 判成失败。计数持续非零 = 锚店写不进(去看库)。计数里 `reason=timeout` 占多数 = 锚店慢到秒级(去看库的锁 / 连接池)。detail = `leg=<stream|bg|resume> kind=<prompt|turn> reason=error error=<写口的错误句>` 或 `… reason=timeout boundMs=<期限>`。";
|
|
575
|
+
};
|
|
560
576
|
readonly "server.remote-write.stage-seal-fallback": {
|
|
561
577
|
readonly cls: "F";
|
|
562
578
|
readonly note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备脚本把刚建的私有暂存目录收成 0700 并读回核实(`chmod 700` 退出 0 ∧ `stat` 读回的模式低三位恰是 700 ∧ 属主 = 本身份),任一步不成 —— `chmod` 被文件系统 / 内核拒;退出 0 却没生效(不认权限位的文件系统);父目录带缺省 ACL 而掩码收不下来;目标机上没有 `stat` —— 准备退出 4、这次写**不暂存**,两只文件面、五条腿一律改为直接写目标(与「目标目录不收新名字」同一条回落;能追加写的腿直接追加,e2b / adb 的追加 = 读旧 + 拼 + 整份写目标 —— adb 的这一形就是 7.138.x 的追加,push 没有追加写)。修前这一步是 `chmod 700 … || :`:加固失败照样放行上传,字节落在一只别的身份可能读得到的目录里(P 轴 fail-open),那一臂已删,字节从不进一只没核成私有的目录。放行的最坏后果 = 这一次写失去「整旧或整新」(非原子:中途断 / 取消 ⇒ 目标可能只写了一半,整份写目标的 e2b / adb 上可能没了或只剩半份;写目标那一步因取消中断答 `unknown`,那一步开始之前取消答 `aborted`、目标未碰,那一步不收取消(ssh 的 sftp)照样写完);ssh / local-docker / k8s 写进原 inode、权限位不变,e2b 的 `files.write` 与 adb 的 push 整份写目标、inode / 权限位由对端定(adbd 不保,与 7.138.x 同形);不是门、不是凭据。Android 共享存储(`/sdcard` 一类:权限位由挂载派生、不认 chmod)上 adb 的写按 sdcardfs / MediaProvider FUSE 的权限语义**推断**每一次都走这里(未上真机,真机读数候 test;结构性,运维修不掉;工作区放在 `/data/local/tmp` 一类认权限位的目录就不走)。计数非零 = 这台目标机的这个目录上私有目录收不成私有(文件系统不认权限位 / 缺省 ACL 掩码收不下来 / 缺 `stat`),写在走非原子的退路,去看那个目录。detail = `<目标路径>: <准备脚本写的原因句>`。";
|
|
563
579
|
};
|
|
580
|
+
readonly "server.park.held-card-void-failed": {
|
|
581
|
+
readonly cls: "F";
|
|
582
|
+
readonly note: "停驻没落定就以失败收场的腿(`runs.ts` 的 `voidHeldParkCard`;两处:后台腿 catch 的失败臂 `leg=bg`、续跑腿 catch 放认领的取消 / 普通异常两臂 `leg=resume`):手里的结果是宿主层停驻(引擎交回之前已把卡落进 checkpoint 店,卡在本 run 的会话上),而停驻行 / 用量行 / 结果到手之后的账本写抛 ⇒ 腿以 `failed` 收场、放认领;放认领之前先按卡自己的 scope 店级 `expire` 作废那张卡(动词 = `card-void.ts` 的结算作废,与验证器自己的停驻同一只)。作废没做成 —— 店读 / 作废抛错(`store_error`)、卡的会话与本 run 的会话对不上(`card_mismatch`,只作废那一张,对不上就不动)、本部署没接 checkpoint 店(`no_checkpoint_store`)—— 照样以 `failed` 收场、放认领,每次记一次。放行的最坏后果 = 那张卡回到修前的形:留在待决面上,兑现它的 run 已经 `failed`、认领已放,决定口照样受理、续跑在没有认领的会话上驱动(`resume-legs.ts` 认领缺席成因 ②),由审批兜底收割收走;前提是停驻簿记写失败,并且这张卡没作废成(三个 reason 任一:checkpoint 店这一笔也失败 / 卡与本 run 的会话对不上 / 没接店 —— 后两者不需要第二次写失败)。不是门、不是凭据、不改 run 的判词。计数非零 = 有一次停驻没落定的失败收场留下了一张卡:按 reason 看 —— `store_error` = checkpoint 店在同一拍也出错,`card_mismatch` = 结果里的卡与本 run 的会话对不上,`no_checkpoint_store` = 这台部署没接 checkpoint 店;去看那只会话。detail = `reason=<词> leg=<bg|resume> [err=<原句>]`(不含凭据)。";
|
|
583
|
+
};
|
|
584
|
+
readonly "server.verifier-pause.card-void-failed": {
|
|
585
|
+
readonly cls: "F";
|
|
586
|
+
readonly note: "验证门结算(`verifier-pause-card.ts` 的 `voidVerifierPauseCard`;三处结算点:bg 组合腿、同步提交腿、续跑 verify 臂):验证器自己的停驻卡(结果上 `verification.verifierPause`)是本服务兑现不了的卡(它在验证器现铸的会话上,`/decide` 读不到续跑输入),结算时按卡自己的 scope 走店级 `expire` 当场作废;作废没做成 —— 店读 / 作废抛错(`store_error`)、卡的会话与停驻报的验证器会话不一致(`card_mismatch`,只作废那一张,对不上就不动)、本部署没接 checkpoint 店却收到了带凭据的验证器停驻(`no_checkpoint_store`)。每次记一次。放行的最坏后果 = 那张卡回到修前的形:留在待决面上点不动(`/decide` 答 409),由审批兜底收割收走;不是门、不是凭据(凭据由出口封口摘,与本臂无关)、不改 run 的判词。计数非零 = 这台部署的 checkpoint 店在出错,或引擎交回的停驻与店里的卡对不上(去看那只会话)。detail = `reason=<词> [err=<原句>]`(不含凭据)。引擎给出「作废验证器卡并释放其会话」的动词后本臂改调它,本 tag 随臂删或改写。";
|
|
587
|
+
};
|
|
588
|
+
readonly "server.run-permission-mode.write-back-unpersisted": {
|
|
589
|
+
readonly cls: "F";
|
|
590
|
+
readonly note: "在飞 run 的切档口(`http/routes/run-permission-mode.ts`,`POST /v1/runs/:id/permission-mode`):引擎已经收下新档(本腿下一次工具调用按它仲裁),把准入的词写回这条 run 存储体(run 模式词的唯一来源,续跑腿按它重建 spec)没有落定 —— 这一刻这条 run 已不持有会话认领或已不在跑(`claim_released`:切档与停驻 / 终局同时到;认领栅栏拒写,不会拿旧 run 的体盖掉同会话新 run 的)、会话没有存储体行(`context_absent`)、店写抛(`store_failed`:库抖 / 一行装不下 / 封签失败)。每次记一次 + 一行 warn,回执 `persisted:false` + `persistMiss` 照实告诉壳。放行的最坏后果 = 下一条腿(停驻后续跑 / 重启后续跑)回到切档前的词:切松了 ⇒ 续跑回到更严的档(多问);切严了 ⇒ 续跑回到切档前更松的档 —— 那一档是这条 run 提交时受理过的,受理判官当时判过,不越过任何部署拒;不是门、不是凭据。计数非零 = 切档常与停驻相撞或 checkpoint 店在出错;壳据 `persisted` 在续跑后重切。detail = `miss=<词> mode=<词>`。";
|
|
591
|
+
};
|
|
564
592
|
};
|
|
565
593
|
/** 词表键推导的闭集类型——未登记的 tag 传不进 {@link recordFailOpen}(编译期拒)。 */
|
|
566
594
|
export type FailOpenTag = keyof typeof FAIL_OPEN_TAGS;
|
|
@@ -22,7 +22,7 @@ export const FAIL_OPEN_TAGS = {
|
|
|
22
22
|
},
|
|
23
23
|
"server.tool-end-media.live-backlog-degraded": {
|
|
24
24
|
cls: "F",
|
|
25
|
-
note: "core 7.50.0 媒体块提货(`trace/tool-output-media.ts` 的 `liveToolEndForBacklog`,三处活面写口共用的那一只判据 —— 同步流出口 `
|
|
25
|
+
note: "core 7.50.0 媒体块提货(`trace/tool-output-media.ts` 的 `liveToolEndForBacklog`,三处活面写口共用的那一只判据 —— 同步流出口 `writeLiveFrame`、子代 tail 路由写环、tail 总线每订阅者队列):本帧媒体字节(`output` 媒体块的 `data` 与 `structured` 媒体卡的 `file.base64` 一起计)+ 这条连接 / 这个订阅者还没送出去的字节 > 上界(64 MiB)⇒ 本帧媒体降成持久面那只形(媒体块换描述符 `{ type, mimeType?, bytes, truncated: true }`,帧级 `truncated` + `totalChars`;媒体卡去掉 `file.base64`)。每降一帧记一次,只在这只函数里记(三处写口不各记)。放行的最坏后果 = 一个读得慢的观众在 SSE 上看不到这张图,只见「这里有一张多大的什么图、没送达」(账本面本就恒是描述符),不是门、不是凭据、不改任何裁决;反方向(不降)让一条停读的连接随帧数线性压住几百 MiB。帧上它与引擎自己的媒体预算降级同形不可分,所以计数是运维唯一能看出「多少观众丢了图」的位;计数持续非零 = 有消费端读流太慢(或网络瓶颈),去看那几条连接。detail = `backlog=<待发字节> media=<本帧媒体字节>`。",
|
|
26
26
|
},
|
|
27
27
|
"server.memory-consolidation.operator-valve-layers-unfed": {
|
|
28
28
|
cls: "P-DEBT",
|
|
@@ -152,6 +152,10 @@ export const FAIL_OPEN_TAGS = {
|
|
|
152
152
|
cls: "F",
|
|
153
153
|
note: "design/382 §3.5(S-44;core 7.55.0 #1468 P1 阶段二起读者是 core `readRuleOffers`,经 `approval-card.ts` 的 `readRuleOfferList`,两条腿同调):batch offer 的 `uncoveredDetail` 明细座不是「每个计数段一行良构行」(集外 reason / 非数组 / 行数与 count 座不等 —— core 读者判丢、读数带 `uncoveredDetailDropped`)或行数超本仓展示帽 MAX_UNCOVERED_DETAIL_ROWS ⇒ 只丢这一座、batch 本体照投。放行的最坏后果 = 卡上少几行「这段为什么还会问」的解释(count 座仍在,core 契约明写明细缺席不是断言);把整只 batch 判假才是错的方向(一只真可兑的合取批从卡上消失)。纯展示轴 ⇒ F 类;必须留痕,否则上游明细座漂形在遥测里零痕迹。detail = `index=<core 列表里的下标>` + 丢的缘由(不带段文本)。",
|
|
154
154
|
},
|
|
155
|
+
"server.approval-batch.kindless-row": {
|
|
156
|
+
cls: "F",
|
|
157
|
+
note: "起服普查(`approval-batch-census.ts`;SQL 两方言同一只读口 `SqlCheckpointStore.scanKindlessBatchRows`):一行审批停驻行(`checkpoint` 表,所有状态)的规则提议里,有 batch offer 带着**无 `kind` 的存量成员** —— 引擎读者(`readRuleOffers`)为这个旧形留着一条存量读臂(无 `kind`、四个命令文本键齐的成员读成 `command`,并给那只 offer 打 `kindlessForm` 记号),本仓用 `countKindlessBatchOffers` 数这个记号。载体两个:`rule_suggestions` 列、blob 里的 `pendingAction.ruleOffers`(detail 的 `carriers=` 点名是哪个)。每个带旧形成员的行记一次(逐次计数 + 探针行 + 每进程一次 warn),起服另出一行 info `approval_batch_kindless_rows { count }`。放行的最坏后果 = 无新增放行:这是读侧存量兼容臂被数的位,读法本来就是『旧形读成 command』,普查不改任何一行的读法、不翻行、不删臂;留痕的唯一目的是让『还有几行靠这条臂』可证 —— 计数为零(整个部署的每个副本起服都是零)才能由后续一版删那条存量臂;非零 = 还有行靠它,不可删,detail 的 `row=` 是行指纹(不是 token)。F 类(读侧兼容、纯展示轴:这条臂读出的是「不再询问」候选的卡面,不是门)。逐页读、不是原子快照(页间别的副本的写入可以让一行被漏数或多数一行);滚动升级窗里停在旧版本的副本之后写的旧形行,下次起服才数得到。后台跑、不挡起服就绪。",
|
|
158
|
+
},
|
|
155
159
|
"server.checkpoint.inbox-risk-axes-damaged": {
|
|
156
160
|
cls: "F",
|
|
157
161
|
note: "core 7.55.0 `summarizeCheckpoint` 的第二参(`plugins/checkpoint-store-sql.ts` 的 `listByScope`:supervisor inbox / 调度视图的按 scope 待决投影):一行审批门的风险描述符读穿了一格坏轴 —— `axes` 不是普通对象,或某个标记(`egress` / `irreversible` / `shell`)在场却不是布尔(手改 / 第三方写者;引擎铸的行没有这一形)⇒ 行照投,`severity` 取有效标记给出的下界,证不出就缺席;core 交一句话给店的座,本座逐行计一次。放行的最坏后果 = 这一行在按 severity 倒排的 inbox 里排位偏低或无档(分诊材料,不是门;resume 的判据不读它)。不放行的代价是一行坏描述符打掉整个 scope 的 inbox(修前:非对象描述符抛 TypeError 出投影)。修前不传座 ⇒ 降档是静默的。detail = core 的句子(按 checkpoint id 与会话 JSON 点名、按形说成员,不带令牌、不带值);每次列 inbox 都会再说一次,直到那一行被修或被决。LOCAL 车道的同一句经 backend 的 `file_store_corrupt_read` 座(core File 店自带),不走本 tag。",
|
|
@@ -174,7 +178,7 @@ export const FAIL_OPEN_TAGS = {
|
|
|
174
178
|
},
|
|
175
179
|
"server.approvals.park-row-blob-unreadable": {
|
|
176
180
|
cls: "F",
|
|
177
|
-
note: "S-700(`plugins/checkpoint-store-sql.ts` 的 `readParkRowActionCell`):运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint` 列里寄存行的 **`pendingAction` 子树**(NP-3 / 7.100.0 起引擎内只取这一棵、整只快照不出库;只为 park 行事实 `requiresRealApproval` / `ruleOffersAbsence` / `mandated`(S-663 二段,core #1079)/ `mandate`(S-721)/ `origin`(S-755)而读 —— 事实位的真源是 `approval-card.ts` 的 `ParkRowFacts`)**在场却不是一只对象**(JSON `null` 字面 / 串 / 数 —— 手改 / 迁移脚本改过)。子树**缺席**(SQL NULL:成员不在,或整只文档不是对象 —— 后者 `get()` 照旧拒;MariaDB 文本承载上的非 JSON 字节同样答 SQL NULL)⇒ 事实缺席、不计数。放行的最坏后果 = **这一行**的 park 行事实(`ParkRowFacts` 各位)缺席(展示/分诊材料;core 定性 display metadata,resume 的带子读 gate 自己的位),队列其余部分照常 —— 而这一行本来就 resume 不了(`get()` 对同一格拒)。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 blob 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),与同一 `rows.map` 里 `rule_suggestions` / `risk_descriptor` 两格同族同判。故 F 类;tag 与那两格**分开计**(哪一格在坏要读得出来)。计数非零 = 这一行的 decide 同样读不动(`get()` 对同一格拒),去按 session 查那一行。 (`member=gate` 那第二座随 core 7.55.0 #1468 P1 阶段二退役:队列行的卡与到期分法改读 `approval_card` / `gate_kind` 两列 —— 写口与 M29 回填同一只派生落下的格 —— 本读面不再读 blob 的 `gate` 子树。)S-2076 起同 tag 另有两座同判据的臂(`SqlCheckpointStore.readParkedSkillCardFacts
|
|
181
|
+
note: "S-700(`plugins/checkpoint-store-sql.ts` 的 `readParkRowActionCell`):运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint` 列里寄存行的 **`pendingAction` 子树**(NP-3 / 7.100.0 起引擎内只取这一棵、整只快照不出库;只为 park 行事实 `requiresRealApproval` / `ruleOffersAbsence` / `mandated`(S-663 二段,core #1079)/ `mandate`(S-721)/ `origin`(S-755)而读 —— 事实位的真源是 `approval-card.ts` 的 `ParkRowFacts`)**在场却不是一只对象**(JSON `null` 字面 / 串 / 数 —— 手改 / 迁移脚本改过)。子树**缺席**(SQL NULL:成员不在,或整只文档不是对象 —— 后者 `get()` 照旧拒;MariaDB 文本承载上的非 JSON 字节同样答 SQL NULL)⇒ 事实缺席、不计数。放行的最坏后果 = **这一行**的 park 行事实(`ParkRowFacts` 各位)缺席(展示/分诊材料;core 定性 display metadata,resume 的带子读 gate 自己的位),队列其余部分照常 —— 而这一行本来就 resume 不了(`get()` 对同一格拒)。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 blob 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),与同一 `rows.map` 里 `rule_suggestions` / `risk_descriptor` 两格同族同判。故 F 类;tag 与那两格**分开计**(哪一格在坏要读得出来)。计数非零 = 这一行的 decide 同样读不动(`get()` 对同一格拒),去按 session 查那一行。 (`member=gate` 那第二座随 core 7.55.0 #1468 P1 阶段二退役:队列行的卡与到期分法改读 `approval_card` / `gate_kind` 两列 —— 写口与 M29 回填同一只派生落下的格 —— 本读面不再读 blob 的 `gate` 子树。)S-2076 起同 tag 另有两座同判据的臂(`SqlCheckpointStore.readParkedSkillCardFacts`:对行上可能带技能卡两位之一的行,整只快照读回的体坏、或体是对象但 core 的摘要读不动它 ⇒ 只丢**这一行**的两位〔`boundGrants` / `classifierNoRuling`〕,授予绑定那一臂〔`boundGrantDigest` / `boundGrantAbsence`〕读自子树、照在(S-1612),队列照常;`not-text` 同样原样抛)—— 计数非零的处置同上:去按 session 查那一行。",
|
|
178
182
|
},
|
|
179
183
|
"server.approvals.durable-row-checkpoint-id-missing": {
|
|
180
184
|
cls: "F",
|
|
@@ -268,6 +272,14 @@ export const FAIL_OPEN_TAGS = {
|
|
|
268
272
|
cls: "F",
|
|
269
273
|
note: "会话行的位置与项目那一格(`session_meta.workspace` / local `session-workspaces.json`;唯一写者 `session-workspace.ts` 的腿记录座,唯一列属主 `plugins/session-workspace-cell.ts`;detail 的 `op` 点名哪一步):`op=write` 一次写失败(库抖 / 盘满 / 锁等待超时)⇒ 行停在上一帧,下一帧照写;`op=read` 一格读不出(被带外改坏)⇒ 读作没有记录,下一帧整格重写;`op=frame` 一帧投不成存储形(引擎不会铸出这种帧)⇒ 这一帧不记。这一格是宿主对**引擎判过的答案**的记录,不是判据 —— 引擎每条腿都重判项目(有记号就判记号,声明不等响亮拒)。放行的最坏后果 = 这只会话的下一条不带 `projectId` 的提交续声明的是旧的项目或不声明(没有记号的检出里这只会话少一次续声明);不放行的代价是把一条在跑的腿、或这只会话往后的每一次提交,为一份记录摔掉。计数非零 = 这台部署的会话店写不动 / 有人改过库。",
|
|
270
274
|
},
|
|
275
|
+
"server.local-session-row.created-at-absent": {
|
|
276
|
+
cls: "F",
|
|
277
|
+
note: "local 会话店(`plugins/local-session-store.ts` 的 `rowCreatedAtOf`,行上建档时刻的唯一读法 —— 列表行 `firstActivityAt`、会话视图的 `createdAt`、交给引擎的会话元数据同读它):会话元数据边档的那一格没有建档时刻(补铸的格:盘上有存档而边档里没有它的格;或一次性迁移 M33 没走过的数据根 —— 跳版升上来的,或窗关之后才出现的这一形)⇒ 读作纪元(1970-01-01T00:00:00.000Z),与修前「存档头没有建档时刻」那一降级同值;不读存档头(那是同一事实的第二个读者)。放行的最坏后果 = 这只会话在按建档时刻排的列表里落到最后、视图上的建档时刻是纪元 —— 装饰性读数,不开任何门、不进任何判据。**计数单位 = 一次读到缺席**(同一只会话每次被列出 / 打开再数一次;累计计数是本进程的观察次数,现存多少读边档),探针行带 `session=<id>`。",
|
|
278
|
+
},
|
|
279
|
+
"server.session-sync.dangling-anchor-export": {
|
|
280
|
+
cls: "F",
|
|
281
|
+
note: "会话同步的 manifest 导出(`session-sync.ts` 的 `exportSessionManifest`,`GET /v1/sessions/:id/sync/manifest` 与对等同步的一切 manifest 读者共用):源店里存着一条续跑锚,它指向的条目不在这份 manifest 自己的 `entryIds` 里(悬空锚 —— 一个更早的版本在导入口收下过指向声明集外的锚;现行导入口已不再写出这种锚)⇒ 这一条不进导出体,店里的行不动。判的是入口门用的同一只谓词(`session-sync-kernel.ts` 的 `anchorEntryDeclared`)。放行的最坏后果 = 对端少收一条本来就解不到条目的锚(续跑到它只会落到「没有这个条目」);不丢的代价是这份 manifest 原样推回时被导入口点名拒、这只会话从此推不出去。**计数单位 = 一条被丢掉的锚的一次导出**(累计计数是本进程自起动以来的观察次数;同一条存量悬空锚行留着、没有单条删除口,每次导出再数一次),探针行带 `sessionId` / `eventId` / `entryId`(JSON 引号包住、各截到 96 字,存量里的 id 是任意串);计数非零 = 本进程导出时碰到过存量悬空锚,现存多少读锚表,不读计数。",
|
|
282
|
+
},
|
|
271
283
|
"server.engine-notice.wire-sink-threw": {
|
|
272
284
|
cls: "F",
|
|
273
285
|
note: "#310:`engine_notice` 分流器把一条白名单通告投给某条 run 腿的登记口时,那只口抛了(live 口写向已断/已撕裂的 SSE socket,或 durable 口的账本写同步抛)。放行的最坏后果 = **这一条通告的这一个终点**缺席:①日志终点在分流之前已经逐字打过(事实一条不丢);②两个终点注册成两只独立 sink ⇒ live 抛不牵连 durable(断连后仍看得见的那半保住);③同会话其余口照投。故 F 类。不放行的代价是把异常回抛给 core 的 `deliverEngineNotice` —— 它会吞掉,于是同一次失败**既没有留痕也没有第二只口**,正是本 tag 要根除的形。留痕是承重的:静默吞掉之后「wire 腿为什么总有几条通告不到」在遥测里与「core 本来就没发」同形。",
|
|
@@ -517,10 +529,26 @@ export const FAIL_OPEN_TAGS = {
|
|
|
517
529
|
cls: "F",
|
|
518
530
|
note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备 / 提交脚本删一只私有暂存目录(`.<名>.stage-<十六进制>/`,生来 0700;只删这次写放进去的 `data` 与这只空目录,从不递归删)没删掉、在 stdout 写一条记录点名 —— 五处:`mv` 已把暂存件换到目标上之后删当前那只;跟一跳到别的目录重新暂存成功之后删原来那只;重新暂存却没用上(收紧不成 / 拷不成)的那一只;就地拷入 / 追加时删暂存名失败、或写完关掉 fd 后删私有目录失败(有的文件系统为仍打开着的文件留隐藏项:NFS / libfuse / SMB);准备时收不成私有 / 里面已有 `data` 的那一只。目录里多出了不是这次写放进去的东西(同一身份或 root 的别的进程往里写过 / 收紧之前别人放进来的)、目录被换成了符号链接、或文件系统拒删。写照它自己的结局答(提交已成 ⇒ 成功:答失败会让调用方以为目标没变),残留只在这里留痕。放行的最坏后果 = 目标(或链接所指)目录里多一只隐藏目录(空的,或装着别人放进去的东西;重新暂存没用上的那只可能装着这次写的半份新内容),只有写入方进得去;不是门、不是凭据、不碰目标内容。计数非零 = 那台目标机上有东西在往别人的暂存目录里写,或删不动,去看那只目录。detail = 残留目录的路径(一次写最多两只,逐只计)。",
|
|
519
531
|
},
|
|
532
|
+
"server.resume-anchor.capture-failed": {
|
|
533
|
+
cls: "F",
|
|
534
|
+
note: "回退锚(`resumeAt` 的提示词 / 助手文本手柄)写入失败 —— 三条腿同一只写法 `runs.ts` 的 `settleResumeAnchor`:流式腿(`POST /v1/tasks/stream`,done 帧之前 / 断连收尾时 await)、后台腿(`POST /v1/runs`)、续跑腿。锚店写抛(库抖 / 表缺 / 盘满),或单行店操作期限(`BASELINE_IO_DEADLINE_MS`,2 s;与项目指令基线读写同一只数、同一只 `withinIoDeadline`)内没落定(锚店挂住:锚店的写口自己不带期限)⇒ 这一次提示词(或这一轮助手文本)此刻没有回退手柄,run 照常收尾、终帧照发、不改任何终局;到期只放手不取消,那次写照常在飞、迟到落定的锚照样可用(迟到的失败被吞,不重复计数)。放行的最坏后果 = 用户拿这次 run 的 taskId 去 `resumeAt` 得 404 `resume_at.unknown_event`、会话视图里那条用户消息行不带 `taskId` 手柄(用户面的读数只有这一处真源,本臂不另铸第二份);不是门、不是凭据。不放行(抛)的代价是让一次锚写失败把整条已经跑完的 run 判成失败。计数持续非零 = 锚店写不进(去看库)。计数里 `reason=timeout` 占多数 = 锚店慢到秒级(去看库的锁 / 连接池)。detail = `leg=<stream|bg|resume> kind=<prompt|turn> reason=error error=<写口的错误句>` 或 `… reason=timeout boundMs=<期限>`。",
|
|
535
|
+
},
|
|
520
536
|
"server.remote-write.stage-seal-fallback": {
|
|
521
537
|
cls: "F",
|
|
522
538
|
note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备脚本把刚建的私有暂存目录收成 0700 并读回核实(`chmod 700` 退出 0 ∧ `stat` 读回的模式低三位恰是 700 ∧ 属主 = 本身份),任一步不成 —— `chmod` 被文件系统 / 内核拒;退出 0 却没生效(不认权限位的文件系统);父目录带缺省 ACL 而掩码收不下来;目标机上没有 `stat` —— 准备退出 4、这次写**不暂存**,两只文件面、五条腿一律改为直接写目标(与「目标目录不收新名字」同一条回落;能追加写的腿直接追加,e2b / adb 的追加 = 读旧 + 拼 + 整份写目标 —— adb 的这一形就是 7.138.x 的追加,push 没有追加写)。修前这一步是 `chmod 700 … || :`:加固失败照样放行上传,字节落在一只别的身份可能读得到的目录里(P 轴 fail-open),那一臂已删,字节从不进一只没核成私有的目录。放行的最坏后果 = 这一次写失去「整旧或整新」(非原子:中途断 / 取消 ⇒ 目标可能只写了一半,整份写目标的 e2b / adb 上可能没了或只剩半份;写目标那一步因取消中断答 `unknown`,那一步开始之前取消答 `aborted`、目标未碰,那一步不收取消(ssh 的 sftp)照样写完);ssh / local-docker / k8s 写进原 inode、权限位不变,e2b 的 `files.write` 与 adb 的 push 整份写目标、inode / 权限位由对端定(adbd 不保,与 7.138.x 同形);不是门、不是凭据。Android 共享存储(`/sdcard` 一类:权限位由挂载派生、不认 chmod)上 adb 的写按 sdcardfs / MediaProvider FUSE 的权限语义**推断**每一次都走这里(未上真机,真机读数候 test;结构性,运维修不掉;工作区放在 `/data/local/tmp` 一类认权限位的目录就不走)。计数非零 = 这台目标机的这个目录上私有目录收不成私有(文件系统不认权限位 / 缺省 ACL 掩码收不下来 / 缺 `stat`),写在走非原子的退路,去看那个目录。detail = `<目标路径>: <准备脚本写的原因句>`。",
|
|
523
539
|
},
|
|
540
|
+
"server.park.held-card-void-failed": {
|
|
541
|
+
cls: "F",
|
|
542
|
+
note: "停驻没落定就以失败收场的腿(`runs.ts` 的 `voidHeldParkCard`;两处:后台腿 catch 的失败臂 `leg=bg`、续跑腿 catch 放认领的取消 / 普通异常两臂 `leg=resume`):手里的结果是宿主层停驻(引擎交回之前已把卡落进 checkpoint 店,卡在本 run 的会话上),而停驻行 / 用量行 / 结果到手之后的账本写抛 ⇒ 腿以 `failed` 收场、放认领;放认领之前先按卡自己的 scope 店级 `expire` 作废那张卡(动词 = `card-void.ts` 的结算作废,与验证器自己的停驻同一只)。作废没做成 —— 店读 / 作废抛错(`store_error`)、卡的会话与本 run 的会话对不上(`card_mismatch`,只作废那一张,对不上就不动)、本部署没接 checkpoint 店(`no_checkpoint_store`)—— 照样以 `failed` 收场、放认领,每次记一次。放行的最坏后果 = 那张卡回到修前的形:留在待决面上,兑现它的 run 已经 `failed`、认领已放,决定口照样受理、续跑在没有认领的会话上驱动(`resume-legs.ts` 认领缺席成因 ②),由审批兜底收割收走;前提是停驻簿记写失败,并且这张卡没作废成(三个 reason 任一:checkpoint 店这一笔也失败 / 卡与本 run 的会话对不上 / 没接店 —— 后两者不需要第二次写失败)。不是门、不是凭据、不改 run 的判词。计数非零 = 有一次停驻没落定的失败收场留下了一张卡:按 reason 看 —— `store_error` = checkpoint 店在同一拍也出错,`card_mismatch` = 结果里的卡与本 run 的会话对不上,`no_checkpoint_store` = 这台部署没接 checkpoint 店;去看那只会话。detail = `reason=<词> leg=<bg|resume> [err=<原句>]`(不含凭据)。",
|
|
543
|
+
},
|
|
544
|
+
"server.verifier-pause.card-void-failed": {
|
|
545
|
+
cls: "F",
|
|
546
|
+
note: "验证门结算(`verifier-pause-card.ts` 的 `voidVerifierPauseCard`;三处结算点:bg 组合腿、同步提交腿、续跑 verify 臂):验证器自己的停驻卡(结果上 `verification.verifierPause`)是本服务兑现不了的卡(它在验证器现铸的会话上,`/decide` 读不到续跑输入),结算时按卡自己的 scope 走店级 `expire` 当场作废;作废没做成 —— 店读 / 作废抛错(`store_error`)、卡的会话与停驻报的验证器会话不一致(`card_mismatch`,只作废那一张,对不上就不动)、本部署没接 checkpoint 店却收到了带凭据的验证器停驻(`no_checkpoint_store`)。每次记一次。放行的最坏后果 = 那张卡回到修前的形:留在待决面上点不动(`/decide` 答 409),由审批兜底收割收走;不是门、不是凭据(凭据由出口封口摘,与本臂无关)、不改 run 的判词。计数非零 = 这台部署的 checkpoint 店在出错,或引擎交回的停驻与店里的卡对不上(去看那只会话)。detail = `reason=<词> [err=<原句>]`(不含凭据)。引擎给出「作废验证器卡并释放其会话」的动词后本臂改调它,本 tag 随臂删或改写。",
|
|
547
|
+
},
|
|
548
|
+
"server.run-permission-mode.write-back-unpersisted": {
|
|
549
|
+
cls: "F",
|
|
550
|
+
note: "在飞 run 的切档口(`http/routes/run-permission-mode.ts`,`POST /v1/runs/:id/permission-mode`):引擎已经收下新档(本腿下一次工具调用按它仲裁),把准入的词写回这条 run 存储体(run 模式词的唯一来源,续跑腿按它重建 spec)没有落定 —— 这一刻这条 run 已不持有会话认领或已不在跑(`claim_released`:切档与停驻 / 终局同时到;认领栅栏拒写,不会拿旧 run 的体盖掉同会话新 run 的)、会话没有存储体行(`context_absent`)、店写抛(`store_failed`:库抖 / 一行装不下 / 封签失败)。每次记一次 + 一行 warn,回执 `persisted:false` + `persistMiss` 照实告诉壳。放行的最坏后果 = 下一条腿(停驻后续跑 / 重启后续跑)回到切档前的词:切松了 ⇒ 续跑回到更严的档(多问);切严了 ⇒ 续跑回到切档前更松的档 —— 那一档是这条 run 提交时受理过的,受理判官当时判过,不越过任何部署拒;不是门、不是凭据。计数非零 = 切档常与停驻相撞或 checkpoint 店在出错;壳据 `persisted` 在续跑后重切。detail = `miss=<词> mode=<词>`。",
|
|
551
|
+
},
|
|
524
552
|
};
|
|
525
553
|
export function failOpenTagForDroppedFrame(frameType) {
|
|
526
554
|
switch (frameType) {
|
|
@@ -259,7 +259,6 @@ export function createMetrics() {
|
|
|
259
259
|
m.counter("remote_env_suspend_total", "E2B remote-env suspendVM outcomes (v1.5, provider/result)");
|
|
260
260
|
m.counter("remote_env_resume_total", "E2B remote-env resumeVM outcomes (v1.5, provider/result)");
|
|
261
261
|
m.counter("remote_env_exec_stream_read_timeout_total", "E2B execStream idle read-timeouts (#1128, provider)");
|
|
262
|
-
m.counter("resume_anchor_capture_failed", "E18 resume-at per-turn anchor captures that failed (best-effort, run unaffected)");
|
|
263
262
|
m.counter("brain_retry_total", "Brain-layer status events (S4), by phase (rate_limited/retrying/reconnecting/circuit_open/waiting_first_token/recovered/gave_up)");
|
|
264
263
|
m.gauge("store_backend_degraded", "1 when the auto DB probe failed at boot and this replica degraded to in-memory (S5)");
|
|
265
264
|
m.counter("runs_reaped_total", "Run rows flipped by the background reapers (S7), by kind (stale/suspended/expired_checkpoint)");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type PersistedTaskResult, type RunRecord } from "../plugins/store-contracts.js";
|
|
2
|
+
import { type StoredResultReadSeat } from "../terminal.js";
|
|
2
3
|
/** 一次终局写口交给观测面的原料(store 侧装配,**未脱敏** —— 脱敏在本模块内做,见 {@link notifyRunTerminal})。 */
|
|
3
4
|
export interface RunTerminalRecord {
|
|
4
5
|
/** 这条 run 的账本 id = `GET /v1/runs/:id` 的 id(本仓 run 行主键名为 taskId,同一个值)。 */
|
|
@@ -205,8 +206,11 @@ export declare function redactTerminalResult<T extends {
|
|
|
205
206
|
* 所以调用点已封过口的腿零行为变化。
|
|
206
207
|
*
|
|
207
208
|
* `null` 原样(= 这条腿没有结果可落:引擎没给终局就收流 / 断连竞态 park / 重开 re-park)。
|
|
209
|
+
*
|
|
210
|
+
* `readSeat`:读面借这同一只组合读存量行时(`GET /v1/runs/:id` 的非终态臂)给出读座 —— 只决定折码运维行是「一件新的失败」还是「同一行的再观看」
|
|
211
|
+
* (见 `sealResultForExit`);店的写口不给。
|
|
208
212
|
*/
|
|
209
|
-
export declare function sealRunRowResult(result: PersistedTaskResult | null): PersistedTaskResult | null;
|
|
213
|
+
export declare function sealRunRowResult(result: PersistedTaskResult | null, readSeat?: StoredResultReadSeat): PersistedTaskResult | null;
|
|
210
214
|
/** [ref](第五轮合并重扫 wf_f00c00bc 两根 [high]):同一个 `TaskResult` 除 run 行外还被逐字追加进 run_events 的 `done` 行
|
|
211
215
|
* (bg/verify/cascade/stream/resume 五腿),`failed` 行带原文 `errorMessage`,`GET /v1/runs/:id/events` 原样回放。
|
|
212
216
|
* 三孪生 `appendEvent` 入口 + 事件读面(旧行纵深)同调本函数:只认 `done.result`(整块走 `redactTerminalResult`)与
|
|
@@ -262,7 +266,7 @@ export declare function memoRedactedRead<T>(seat: string, raw: T, redact: (v: T)
|
|
|
262
266
|
* `memoRedactTerminalResult` 会对它 O(1) 直返明文,读面纵深对这一叶恒不生效(这正是 [ref] 立案时
|
|
263
267
|
* `editedFiles` 那一格的病)。读数:8MB result 的一次摘要 ~10ms 量级,远小于一次全扫;干净行仍只摘要
|
|
264
268
|
* 一次即进 clean 集,脏行超过 {@link READ_MEMO_DIRTY_MAX_ENTRY_BYTES} 时退化为每次重扫(既有形)。 */
|
|
265
|
-
export declare function memoRedactTerminalResult<T extends Parameters<typeof redactTerminalResult>[0]>(seat:
|
|
269
|
+
export declare function memoRedactTerminalResult<T extends Parameters<typeof redactTerminalResult>[0]>(seat: StoredResultReadSeat, raw: T): T;
|
|
266
270
|
/**
|
|
267
271
|
* 🔴 **账本行的读面口 —— 全部耐久读腿共用这一只**([ref],7.105.0)。
|
|
268
272
|
*
|