@sema-agent/server 7.69.0 → 7.71.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/README.md +4 -1
- package/README.zh-CN.md +4 -1
- package/USAGE.md +109 -10
- package/dist/approval-ask-machine.d.ts +1 -1
- package/dist/approval-card.d.ts +3 -2
- package/dist/approval-content-kind.d.ts +13 -7
- package/dist/approval-content-kind.js +3 -1
- package/dist/approval-reconciler.d.ts +0 -54
- package/dist/approval-reconciler.js +2 -2
- package/dist/approval.js +3 -2
- package/dist/bench/s1/live-deps.js +2 -0
- package/dist/boot/execution-env.js +12 -2
- package/dist/boot/memory-consolidation.d.ts +2 -1
- package/dist/boot/parked-revive-gate.d.ts +2 -0
- package/dist/boot/parked-revive-gate.js +15 -10
- package/dist/boot/resolve-spec.js +8 -3
- package/dist/boot/runner-deps.d.ts +50 -46
- package/dist/boot/runner-deps.js +12 -10
- package/dist/brain.d.ts +3 -1
- package/dist/brain.js +2 -1
- package/dist/budget.js +3 -2
- package/dist/config-catalog.d.ts +31 -2
- package/dist/config-catalog.js +44 -24
- package/dist/config-types.d.ts +31 -1
- package/dist/config-types.js +1 -0
- package/dist/config.js +15 -3
- package/dist/deployment-governance.d.ts +6 -3
- package/dist/deployment-governance.js +2 -1
- package/dist/execution-lane-caps.d.ts +51 -37
- package/dist/execution-lane-caps.js +23 -0
- package/dist/fleet/fleet-bus.d.ts +22 -1
- package/dist/fleet/fleet-bus.js +2 -1
- package/dist/http/routes/approvals-assistant.js +3 -12
- package/dist/http/routes/diagnostics.js +4 -0
- package/dist/http/routes/runs.js +2 -3
- package/dist/http/routes/tasks.js +22 -18
- package/dist/http/server.js +21 -7
- package/dist/index.d.ts +1 -1
- package/dist/leader/wire.js +4 -3
- package/dist/main.js +6 -4
- package/dist/memory-operator-faces.d.ts +1 -1
- package/dist/memory-scope.d.ts +24 -0
- package/dist/memory-scope.js +12 -3
- package/dist/observability/metrics.d.ts +4 -6
- package/dist/observability/metrics.js +5 -1
- package/dist/observability/permission-rule-events.d.ts +20 -0
- package/dist/observability/permission-rule-events.js +28 -0
- package/dist/observability/run-terminal-log.d.ts +13 -22
- package/dist/observability/run-terminal-log.js +187 -47
- package/dist/observability/tool-trace.d.ts +12 -0
- package/dist/observability/tool-trace.js +5 -1
- package/dist/orchestration/workflow-agent-session-index.d.ts +85 -2
- package/dist/orchestration/workflow-agent-session-index.js +38 -5
- package/dist/parked-decide.d.ts +40 -1
- package/dist/parked-decide.js +19 -2
- package/dist/plugins/checkpoint-store-sql.js +5 -5
- package/dist/plugins/local-checkpoint-store.js +3 -3
- package/dist/plugins/remote-env-adb.d.ts +11 -0
- package/dist/plugins/remote-env-adb.js +2 -0
- package/dist/plugins/remote-env-device.d.ts +9 -0
- package/dist/plugins/remote-env-device.js +3 -0
- package/dist/plugins/remote-env-e2b.d.ts +11 -0
- package/dist/plugins/remote-env-e2b.js +2 -0
- package/dist/plugins/remote-env-host.d.ts +11 -0
- package/dist/plugins/remote-env-host.js +2 -0
- package/dist/plugins/remote-env-k8s.d.ts +11 -0
- package/dist/plugins/remote-env-k8s.js +2 -0
- package/dist/plugins/remote-env-local-docker.d.ts +11 -0
- package/dist/plugins/remote-env-local-docker.js +2 -0
- package/dist/plugins/remote-env-ssh.d.ts +11 -0
- package/dist/plugins/remote-env-ssh.js +2 -0
- package/dist/plugins/store-backend.d.ts +36 -13
- package/dist/plugins/store-backend.js +12 -3
- package/dist/plugins/store-contracts.d.ts +54 -18
- package/dist/plugins/store-contracts.js +17 -30
- package/dist/plugins/workflow-journal-store-sql.d.ts +1 -1
- package/dist/plugins/workflow-journal-store-sql.js +3 -10
- package/dist/plugins/workflow-run-store-sql.d.ts +31 -9
- package/dist/plugins/workflow-run-store-sql.js +28 -1
- package/dist/posture-source.d.ts +23 -3
- package/dist/posture-source.js +1 -1
- package/dist/read-face-posture.d.ts +8 -1
- package/dist/read-face-posture.js +1 -0
- package/dist/run-cancel-context.d.ts +50 -1
- package/dist/run-cancel-context.js +22 -0
- package/dist/run-local.d.ts +14 -6
- package/dist/run-local.js +4 -1
- package/dist/runs.d.ts +0 -51
- package/dist/runs.js +40 -25
- package/dist/task-a2a.js +2 -2
- package/dist/task-mcp.js +2 -2
- package/dist/task-settings.d.ts +21 -8
- package/dist/task-settings.js +2 -2
- package/dist/tool-approval.d.ts +14 -8
- package/dist/tool-approval.js +3 -3
- package/dist/trace/core-keyset-guard.d.ts +135 -11
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +1 -0
- package/dist/trace/ledger-events.d.ts +22 -2
- package/dist/trace/ledger-events.js +6 -1
- package/dist/trace/ledger-sink.d.ts +10 -8
- package/dist/trace/ledger-sink.js +10 -2
- package/dist/trace/project.d.ts +24 -6
- package/dist/trace/project.js +5 -3
- package/dist/trace/redact.d.ts +28 -0
- package/dist/trace/redact.js +58 -30
- package/package.json +3 -3
|
@@ -71,7 +71,8 @@ export type ServiceFileHistoryStore = FileHistoryStore & {
|
|
|
71
71
|
/** SVC-2 (core CORE-7) WorkflowJournalStore — core's interface PLUS the service `deleteByRun` (GC extra). tidb/pg =
|
|
72
72
|
* durable SQL twins (cross-replica resume journal); local = the service {@link FileWorkflowJournalStore}
|
|
73
73
|
* (single-box but RESTART-durable crash-safe JSONL, replacing core's InMemory). The shared equivalence suite keeps
|
|
74
|
-
* all three byte-matched (scope guard / idempotent-per-ordinal / oversize
|
|
74
|
+
* all three byte-matched (scope guard / idempotent-per-ordinal / oversize **refusal** —— S-194 / core 7.12.0
|
|
75
|
+
* [ref] 起两臂一律抛 `workflow.journal_oversize`,判长口径归 core `assertJournalEntryFits` 单一属主). */
|
|
75
76
|
export type ServiceWorkflowJournalStore = WorkflowJournalStore & {
|
|
76
77
|
/** codex R1-H2(journal 读面界):分页+单行字节门的投影读——SQL 端 LIMIT/OFFSET 定行数、LENGTH 门
|
|
77
78
|
* 定单行(超 maxResultBytes 的行 resultJson=null 只回 resultBytes,读面标 truncated)。HTTP 读面
|
|
@@ -115,6 +116,22 @@ export type ToolResultStoreFull = ToolResultStore & {
|
|
|
115
116
|
export type BreakerStateStore = TiDBBreakerState | PgBreakerState;
|
|
116
117
|
export type CostQuotaStore = TiDBCostQuota | PgCostQuota;
|
|
117
118
|
export type RateLimiterStore = TiDBRateLimiter | PgRateLimiter;
|
|
119
|
+
/**
|
|
120
|
+
* S-215 —— capture opt-out **记录载体的按平面取用座**。形与 core 的
|
|
121
|
+
* `RunnerDeps.memoryCaptureRecordStore`(`(plane: { controlDir }) => SessionCaptureRecordStore`)同构,
|
|
122
|
+
* 多一个**不指名平面**的缺省臂:`src()` = 「本部署记忆引擎自己那一只平面」。
|
|
123
|
+
*
|
|
124
|
+
* 🔴 为什么是工厂而不是一只实例(S-215 codex r1 [high] 验真后改形):core 的记忆平面**不止一个**
|
|
125
|
+
* (`prepare-memory.js:162` 的 personal 平面自带 `derivePersonalControlDir(engineRoot)`,与 project 平面的
|
|
126
|
+
* controlDir 不是同一只)。返回单只实例 ⇒ server 把所有平面折到一只目录上,于是升级前落在 personal
|
|
127
|
+
* 控制面的旧 opt-out 记录在升级后读作**缺席**=采集恢复(隐私面的静默 fail-open)。按平面取用后,
|
|
128
|
+
* file 载体与 core 的缺省**逐字节同址**,唯一的变化是这个席位现在**被声明了**(core 的能力信号要的就是它)。
|
|
129
|
+
* SQL 载体**刻意忽略** plane:它按 session_id 键控、跨平面全局 —— opt-out 是**会话**事实而不是平面事实
|
|
130
|
+
* (逐字理由见 boot/runner-deps.ts 该席的注)。
|
|
131
|
+
*/
|
|
132
|
+
export type SessionCaptureRecordSource = (plane?: {
|
|
133
|
+
controlDir: string;
|
|
134
|
+
}) => SessionCaptureRecordStore;
|
|
118
135
|
export interface StoreBackend {
|
|
119
136
|
/** Protocol-truth kind (clay 2026-07-06 wording): "mysql" = any MySQL-protocol server (MySQL / TiDB /
|
|
120
137
|
* MariaDB) behind the one mysql2 pool — the startup summary prints durable(mysql) for all of them. */
|
|
@@ -185,19 +202,25 @@ export interface StoreBackend {
|
|
|
185
202
|
* 「没有行 ⇒ 代码缺省 allow」—— 那是**放宽面**上的静默降级(permissionRule 那条禁令的同一理由)。
|
|
186
203
|
* File 形候真实的 local+governed 部署需求出现再落(候需求档)。 */
|
|
187
204
|
memoryOptOutGrant(): MemoryOptOutGrantStore | undefined;
|
|
188
|
-
/** [ref]②([ref] §2.1b,core 7.0.2 [ref] 件2):会话 capture opt-out
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
205
|
+
/** [ref]②([ref] §2.1b,core 7.0.2 [ref] 件2)/ S-215:会话 capture opt-out **记录**的载体 ——
|
|
206
|
+
* core `RunnerDeps.memoryCaptureRecordStore` / `MemoryEngineOptions.captureRecordStore` 的持久层。
|
|
207
|
+
* 与 §3.1 的**授权表**不是一个轴:那张管「谁**可以** opt-out」(operator 写的 verdict),这张管
|
|
208
|
+
* 「谁**已经** opt-out」(用户写的一次性单向记录)。
|
|
209
|
+
*
|
|
210
|
+
* 返回的是**按平面取用的源**({@link SessionCaptureRecordSource}),不是一只实例 —— core 的记忆平面
|
|
211
|
+
* 不止一只,折平会挪动旧记录的地址(理由全文见该类型的头注)。
|
|
212
|
+
* 载体按车道分形,但**三条车道都供**(S-215 起):SQL 两支 = 双方言 twin(`session_capture_optout`,
|
|
213
|
+
* Promise 形三腿,池即依托 ⇒ 无条件供,`plane` 刻意忽略);`local` = core 的控制面文件三腿
|
|
214
|
+
* `fileSessionCaptureRecordStore(controlDir)` 按平面现造,依托是**记忆引擎的控制面目录** ⇒ 记忆面暗的
|
|
215
|
+
* 部署上该平面不存在,于是这一支**随引擎接线门缺席**(唯一的 `undefined` 成因,逐字理由见 LocalBackend 的实装注)。
|
|
193
216
|
*
|
|
194
|
-
* 🔴
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
sessionCaptureRecords():
|
|
217
|
+
* 🔴 修前 local 恒 `undefined` 且注称「缺席是一个真答案」—— 那句话在**事实层面**成立(单机的正确载体
|
|
218
|
+
* 确实就是文件三腿)、在**接线层面**是假的:core 的 `captureCarrierUnsupported`
|
|
219
|
+
* (core 7.13.0 `prepare-task.js:497`)判的是「这个席位在不在」而不是「文件形能不能用」,叠上
|
|
220
|
+
* `isRemoteExecutionEnv` 的接口形鸭子判(`remote-env.js:21`)把本仓 host lane 一并算作 remote,
|
|
221
|
+
* 于是单机默认部署上 `memoryCapture:"off"` 两条 ingress 结构性不可达(B-085)。
|
|
222
|
+
* 刻意仍不给 local 一个**内存** twin:内存形会在重启时把一条隐私记录静默丢掉(比文件三腿严格更差)。 */
|
|
223
|
+
sessionCaptureRecords(): SessionCaptureRecordSource | undefined;
|
|
201
224
|
/** S-1 legacy-epoch discriminant (core `RunnerDeps.legacyRewindBoundaryProbe`): does the RETIRED whole-tree
|
|
202
225
|
* snapshot epoch hold a boundary for (sessionId, entryId)? Reads the legacy `snapshot_manifest` table.
|
|
203
226
|
* SQL backends only (local never had a whole-tree epoch table). Diagnostic input on the refusal path —
|
|
@@ -2,6 +2,7 @@ import { homedir } from "node:os";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { AdoptionError, FileStorageBackend, FileSessionRepo } from "@sema-agent/core";
|
|
4
4
|
import { FileWorkflowJournalStore } from "@sema-agent/core";
|
|
5
|
+
import { fileSessionCaptureRecordStore } from "@sema-agent/core";
|
|
5
6
|
import { TiDBOutcomeLedger, PgOutcomeLedger } from "./outcome-ledger-sql.js";
|
|
6
7
|
import { FileOutcomeSink } from "./file-outcome-sink.js";
|
|
7
8
|
import { TiDBWorkflowRunStore, TiDBWorkflowCompletionInbox, TiDBWorkflowNotifyJournalStore, TiDBWorkflowAgentSessionIndex, PgWorkflowRunStore, PgWorkflowCompletionInbox, PgWorkflowNotifyJournalStore, PgWorkflowAgentSessionIndex } from "./workflow-run-store-sql.js";
|
|
@@ -20,6 +21,7 @@ import { TiDBSessionPolicyStore, PgSessionPolicyStore } from "./session-policy-s
|
|
|
20
21
|
import { TiDBWorkflowJournalStore, PgWorkflowJournalStore } from "./workflow-journal-store-sql.js";
|
|
21
22
|
import { TiDBFileHistoryStore, PgFileHistoryStore, FILE_HISTORY_BLOB_TABLE, FILE_HISTORY_BLOB_KEY_PREFIX } from "./file-history-store-sql.js";
|
|
22
23
|
import { MinioBlobBackend, SqlBlobBackend } from "./blob-backend.js";
|
|
24
|
+
import { fileMemoryControlDirFor } from "../memory-scope.js";
|
|
23
25
|
import { createTidbPool, ensureSchema as ensureTidbSchema } from "./tidb-pool.js";
|
|
24
26
|
import { createPgPool, ensurePgSchema, pgPoolOptions } from "./pg-pool.js";
|
|
25
27
|
import { TiDBRunStore, PgRunStore } from "./run-store-sql.js";
|
|
@@ -136,7 +138,8 @@ class TiDBBackend {
|
|
|
136
138
|
sessionPolicy() { return new TiDBSessionPolicyStore(this.pool); }
|
|
137
139
|
fileHistory() { return new TiDBFileHistoryStore(this.pool, historyBlobBackend(this.config, "tidb", this.pool), this.config.fileHistory); }
|
|
138
140
|
memoryOptOutGrant() { return new TiDBMemoryOptOutGrantStore(this.pool); }
|
|
139
|
-
sessionCaptureRecords() { return new TiDBSessionCaptureRecordStore(this.pool); }
|
|
141
|
+
sessionCaptureRecords() { return () => (this.captureRecordsInst ??= new TiDBSessionCaptureRecordStore(this.pool)); }
|
|
142
|
+
captureRecordsInst;
|
|
140
143
|
legacyRewindBoundaryProbe(query) { return legacyProbeImpl("tidb", this.pool, query); }
|
|
141
144
|
legacySnapshotBlobSweep() { return legacySweepImpl(this.config, "tidb", this.pool); }
|
|
142
145
|
legacySnapshotPurge(sessionId) { return legacyPurgeImpl("tidb", this.pool, sessionId); }
|
|
@@ -183,7 +186,8 @@ class PgBackend {
|
|
|
183
186
|
sessionPolicy() { return new PgSessionPolicyStore(this.pool); }
|
|
184
187
|
fileHistory() { return new PgFileHistoryStore(this.pool, historyBlobBackend(this.config, "pg", this.pool), this.config.fileHistory); }
|
|
185
188
|
memoryOptOutGrant() { return new PgMemoryOptOutGrantStore(this.pool); }
|
|
186
|
-
sessionCaptureRecords() { return new PgSessionCaptureRecordStore(this.pool); }
|
|
189
|
+
sessionCaptureRecords() { return () => (this.captureRecordsInst ??= new PgSessionCaptureRecordStore(this.pool)); }
|
|
190
|
+
captureRecordsInst;
|
|
187
191
|
legacyRewindBoundaryProbe(query) { return legacyProbeImpl("pg", this.pool, query); }
|
|
188
192
|
legacySnapshotBlobSweep() { return legacySweepImpl(this.config, "pg", this.pool); }
|
|
189
193
|
legacySnapshotPurge(sessionId) { return legacyPurgeImpl("pg", this.pool, sessionId); }
|
|
@@ -259,6 +263,10 @@ class LocalBackend {
|
|
|
259
263
|
this.sendFileLedgerStore = new FileSendFileLedger(this.fileBackend.root);
|
|
260
264
|
this.workflowJournalStore = new FileWorkflowJournalStore(this.fileBackend.root);
|
|
261
265
|
this.outcomeSinkInst = new FileOutcomeSink(this.fileBackend.root);
|
|
266
|
+
const ownControlDir = fileMemoryControlDirFor(config);
|
|
267
|
+
this.captureRecordsSource = ownControlDir === undefined
|
|
268
|
+
? undefined
|
|
269
|
+
: (plane) => fileSessionCaptureRecordStore(plane?.controlDir ?? ownControlDir);
|
|
262
270
|
}
|
|
263
271
|
async ensureSchema() { }
|
|
264
272
|
async close() {
|
|
@@ -304,7 +312,8 @@ class LocalBackend {
|
|
|
304
312
|
sessionPolicy() { return this.fileBackend.sessionPolicyStore; }
|
|
305
313
|
fileHistory() { return this.fileBackend.fileHistoryStore; }
|
|
306
314
|
memoryOptOutGrant() { return undefined; }
|
|
307
|
-
sessionCaptureRecords() { return
|
|
315
|
+
sessionCaptureRecords() { return this.captureRecordsSource; }
|
|
316
|
+
captureRecordsSource;
|
|
308
317
|
workflowJournal() { return this.workflowJournalStore; }
|
|
309
318
|
outcomeSink() { return this.outcomeSinkInst; }
|
|
310
319
|
session() { return this.sessionStore; }
|
|
@@ -108,27 +108,65 @@ export interface RunRecord {
|
|
|
108
108
|
updatedAt: string;
|
|
109
109
|
}
|
|
110
110
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
111
|
+
* `RunRecord["status"]`(= `"running" | TaskStatus`)的**唯一分类表** —— 词表的属主 = 判据的属主,
|
|
112
|
+
* 与类型定义同住一个文件。四个类:
|
|
113
|
+
* · `live` —— 还在跑;
|
|
114
|
+
* · `succeeded` —— 成功终局;
|
|
115
|
+
* · `unsuccessful` —— **非成功**终局。两个词:`failed`(限额/供应商/中止/无效输出)与 `blocked`
|
|
116
|
+
* —— 后者是 core `TerminalCause` 的 `{kind:"blocked", reason}`,d.ts 逐字为「the agent could not
|
|
117
|
+
* finish and said why」= **agent 自报的终态**(说完就结束,不等人、不留 checkpoint)。它**不是**
|
|
118
|
+
* 治理判决:门拒绝那条腿在 core 里走 `paused`/`failed`,永远不铸这个词;
|
|
119
|
+
* · `parked` —— 停在门上还能再动(`suspended` 审批/资源门、`needs_review` 评审门)。
|
|
113
120
|
*
|
|
114
|
-
* 🔴
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* · **删词** ⇒ 集合里留一个死字面量,没有任何东西会说话。
|
|
120
|
-
* 穷举 `switch` 把两个方向都变成**编译期**问题:core 加成员 ⇒ `default` 臂收不敛到 `never` ⇒ tsc 红,
|
|
121
|
-
* 加成员的人被迫当场回答「这个新态算不算终局」;删成员 ⇒ 对应 `case` 变成不可达字面量、同样红。
|
|
122
|
-
* 这条判据不许再被手抄成第二份 —— 两个消费点(`approval-reconciler.ts` 判据 2、`http/routes/runs.ts`
|
|
123
|
-
* 的取消后复读)都从这里取。
|
|
121
|
+
* 🔴 为什么是**一张表**而不是三张 switch(S-213③,2026-09-11):`blocked` 这个词此前在本仓有**两个
|
|
122
|
+
* 独立写者**且答案互斥 —— `observability/run-terminal-log.ts` 判它是「治理判决,不是失败 ⇒ 不打行」,
|
|
123
|
+
* `fleet/fleet-bus.ts` 判它是「非成功终局(agent 放弃)⇒ failed 桶、行离场」。两处各自穷举、各自正确
|
|
124
|
+
* 地编译,而一条 `report_blocked` 的 run 在面板上离场、在引擎日志里一个字都没有。三张手抄的 switch
|
|
125
|
+
* 就是三个写者;收成一张表之后,词的语义只有这一处能改。
|
|
124
126
|
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
+
* 🔴 为什么这**不是**当年被否掉的 `ReadonlySet<string>`([ref] 二轮扫描 finding):那份集合的门对象是
|
|
128
|
+
* `string`,对词表两个方向都无钉 —— 增词静默判「非终局」、删词留一个死字面量没人说话。本表两个方向
|
|
129
|
+
* 都是**编译期**问题:`satisfies Readonly<Record<RunRecord["status"], RunStatusClass>>` 要求每个词都有
|
|
130
|
+
* 一行(core 加成员 ⇒ 缺行 ⇒ tsc 红,加成员的人被迫当场回答「这个新态算哪一类」),而多出一行
|
|
131
|
+
* core 已经没有的词同样红(超出键)。终局词表真变过(core 5.8.0 把 `"timeout"` 从 `TaskStatus` 删了)。
|
|
132
|
+
*
|
|
133
|
+
* 判据不许再被手抄成第二份:消费点(`approval-reconciler.ts` 判据 2、`http/routes/runs.ts` 的取消后
|
|
134
|
+
* 复读、`observability/run-terminal-log.ts` 的级别判、`fleet/fleet-bus.ts` 的 failed 桶编译钉)都从这里取。
|
|
135
|
+
*/
|
|
136
|
+
export type RunStatusClass = "live" | "succeeded" | "unsuccessful" | "parked";
|
|
137
|
+
export declare const RUN_STATUS_CLASS: {
|
|
138
|
+
readonly running: "live";
|
|
139
|
+
readonly completed: "succeeded";
|
|
140
|
+
readonly failed: "unsuccessful";
|
|
141
|
+
readonly blocked: "unsuccessful";
|
|
142
|
+
readonly suspended: "parked";
|
|
143
|
+
readonly needs_review: "parked";
|
|
144
|
+
};
|
|
145
|
+
/** 表判为「非成功终局」的词——**从表派生**,不是手抄的第二份清单。今天 = `"failed" | "blocked"`。
|
|
146
|
+
* 消费点(`fleet/fleet-bus.ts` 的 failed 桶)拿它做编译期同源钉:表翻面 ⇒ 那道钉当场红。 */
|
|
147
|
+
export type UnsuccessfulRunStatus = {
|
|
148
|
+
[K in RunRecord["status"]]: (typeof RUN_STATUS_CLASS)[K] extends "unsuccessful" ? K : never;
|
|
149
|
+
}[RunRecord["status"]];
|
|
150
|
+
/**
|
|
151
|
+
* 「这一行已经走到**终局**了吗」——终局 = 成功 or 非成功(completed / failed / blocked);
|
|
152
|
+
* 非终局 = running / suspended / needs_review(后两者都还能再动)。
|
|
153
|
+
*
|
|
154
|
+
* 未知词判**非终局**:判成「终局」的代价不可逆 —— `approval-reconciler` 判据 2 会把一张还活着的 ask
|
|
155
|
+
* 永久打成 DENIED/routing_failure,取消路由也会把这一行当终局上报。
|
|
127
156
|
*/
|
|
128
157
|
export declare function isTerminalRunStatus(status: RunRecord["status"]): boolean;
|
|
129
158
|
/**
|
|
130
|
-
*
|
|
131
|
-
*
|
|
159
|
+
* 「这一行的终局是**非成功**的吗」(S-213③ 的共享判据)——`failed` 与 `blocked` 两词。
|
|
160
|
+
*
|
|
161
|
+
* 🔴 它回答的问题与 {@link isTerminalRunStatus} **不同**:后者只问「还会不会再动」,本判据问「这次任务
|
|
162
|
+
* 到底成没成」。凡是「这条 run 该不该在失败面上出声/离场」的判断都读这一条,不许再手抄
|
|
163
|
+
* `=== "failed" || === "blocked"`(那正是 `blocked` 长出两个互斥读法的成因)。
|
|
164
|
+
*
|
|
165
|
+
* 未知词判**不是**非成功:对读不懂的词保持沉默,好过按 error 级刷屏。
|
|
166
|
+
*/
|
|
167
|
+
export declare function isUnsuccessfulRunStatus(status: RunRecord["status"]): boolean;
|
|
168
|
+
/**
|
|
169
|
+
* 「这一行**停在门上**(park)了吗」(扫描P2,2026-08-12)。
|
|
132
170
|
*
|
|
133
171
|
* 🔴 为什么必须有名字:park 是**两个词**,不是一个 —— `suspended`(tool_approval / policy_ask / human /
|
|
134
172
|
* resource_limit 门)与 `needs_review`(`plan_review` / dry-run 拦截门,写点 = 各 store 的
|
|
@@ -139,8 +177,6 @@ export declare function isTerminalRunStatus(status: RunRecord["status"]): boolea
|
|
|
139
177
|
*
|
|
140
178
|
* 未知词的方向与 {@link isTerminalRunStatus} 同向保守:判**不是** park。理由是消费点会据此做**不可逆**
|
|
141
179
|
* 动作(cancel 的恢复把手会 CAS-expire 一张 checkpoint);对一个本进程读不懂的状态,少动一次远好过错杀。
|
|
142
|
-
* 真正的执法点是下面那条 `never`:core 的 `TaskStatus` 一加成员就 tsc 红,加成员的人必须当场回答
|
|
143
|
-
* 「这个新态算不算 park」。
|
|
144
180
|
*/
|
|
145
181
|
export declare function isParkedRunStatus(status: RunRecord["status"]): boolean;
|
|
146
182
|
/** One row of the GET /v1/sessions list (CC /resume picker): a DISTINCT session (sessionId) aggregated from its
|
|
@@ -1,37 +1,24 @@
|
|
|
1
1
|
import { parseJsonOr, toIso as iso, normalizeIsoOrNull as isoOrNull } from "./sql-row-helpers.js";
|
|
2
|
+
export const RUN_STATUS_CLASS = {
|
|
3
|
+
running: "live",
|
|
4
|
+
completed: "succeeded",
|
|
5
|
+
failed: "unsuccessful",
|
|
6
|
+
blocked: "unsuccessful",
|
|
7
|
+
suspended: "parked",
|
|
8
|
+
needs_review: "parked",
|
|
9
|
+
};
|
|
10
|
+
function runStatusClassOf(status) {
|
|
11
|
+
return RUN_STATUS_CLASS[status];
|
|
12
|
+
}
|
|
2
13
|
export function isTerminalRunStatus(status) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
case "running":
|
|
9
|
-
case "suspended":
|
|
10
|
-
case "needs_review":
|
|
11
|
-
return false;
|
|
12
|
-
default: {
|
|
13
|
-
const unhandled = status;
|
|
14
|
-
void unhandled;
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
14
|
+
const cls = runStatusClassOf(status);
|
|
15
|
+
return cls === "succeeded" || cls === "unsuccessful";
|
|
16
|
+
}
|
|
17
|
+
export function isUnsuccessfulRunStatus(status) {
|
|
18
|
+
return runStatusClassOf(status) === "unsuccessful";
|
|
18
19
|
}
|
|
19
20
|
export function isParkedRunStatus(status) {
|
|
20
|
-
|
|
21
|
-
case "suspended":
|
|
22
|
-
case "needs_review":
|
|
23
|
-
return true;
|
|
24
|
-
case "running":
|
|
25
|
-
case "completed":
|
|
26
|
-
case "failed":
|
|
27
|
-
case "blocked":
|
|
28
|
-
return false;
|
|
29
|
-
default: {
|
|
30
|
-
const unhandled = status;
|
|
31
|
-
void unhandled;
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
21
|
+
return runStatusClassOf(status) === "parked";
|
|
35
22
|
}
|
|
36
23
|
export function mapBakeRow(r) {
|
|
37
24
|
return {
|
|
@@ -57,7 +57,7 @@ export declare class SqlWorkflowJournalStore implements WorkflowJournalStore {
|
|
|
57
57
|
/** Record one agent's result. Idempotent per (run_id, ordinal): the PK upsert OVERWRITES (last-write-wins), so a
|
|
58
58
|
* resumed run re-appending its replayed prefix verbatim leaves no duplicate. callKeyOrdinal is the SOLE ordinal
|
|
59
59
|
* source (core's parser — a malformed key collapses to 0, matching InMemory). Throws on a REAL failure
|
|
60
|
-
* (LOAD-BEARING)
|
|
60
|
+
* (LOAD-BEARING) — an oversize entry included, on BOTH arms (S-194 / core 7.12.0 [ref]; see below). */
|
|
61
61
|
append(runId: string, scope: string, entry: WorkflowJournalEntry): Promise<void>;
|
|
62
62
|
/** Entries for `runId` IF its recorded scope === `scope`, ASCENDING by ordinal; otherwise EMPTY. CORE-9 audit
|
|
63
63
|
* BLOCKER: scope in the WHERE (twin of WorkflowRunStore) — the LLM controls `resumeFromRunId`, so a cross-scope
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { callKeyOrdinal } from "@sema-agent/core";
|
|
2
|
-
import {
|
|
3
|
-
import { journalEntryOfRow,
|
|
2
|
+
import { assertJournalEntryFits } from "@sema-agent/core";
|
|
3
|
+
import { journalEntryOfRow, journalRowPayloadOf } from "../orchestration/workflow-journal-entry.js";
|
|
4
4
|
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
5
5
|
const DEFAULT_RESUME_CLAIM_TTL_MS = 60 * 60 * 1000;
|
|
6
6
|
export class SqlWorkflowJournalStore {
|
|
@@ -14,15 +14,8 @@ export class SqlWorkflowJournalStore {
|
|
|
14
14
|
return this.db.dialect === "tidb" ? tidb : pg;
|
|
15
15
|
}
|
|
16
16
|
async append(runId, scope, entry) {
|
|
17
|
+
assertJournalEntryFits(entry);
|
|
17
18
|
const serialized = journalRowPayloadOf(entry);
|
|
18
|
-
if (oversizeJournalResult(serialized)) {
|
|
19
|
-
if (journalEntryPayload(entry).parked) {
|
|
20
|
-
throw new Error(`workflow journal: PARKED entry ${entry.callKey} is ${serialized.length} bytes, over the journal result cap — refusing to skip it. ` +
|
|
21
|
-
"Skipping a parked entry would make a resume start a SECOND live agent beside the session already pinned by the pending checkpoint (duplicating pre-approval side effects); " +
|
|
22
|
-
"a completed result may be skipped (resume just re-runs that agent), a park may not.");
|
|
23
|
-
}
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
19
|
await this.db.query(this.q("INSERT INTO workflow_journal (run_id, ordinal, scope, call_key, result, created_at_ms) VALUES (?,?,?,?,?,?) " +
|
|
27
20
|
"ON DUPLICATE KEY UPDATE call_key = VALUES(call_key), result = VALUES(result)", "INSERT INTO workflow_journal (run_id, ordinal, scope, call_key, result, created_at_ms) VALUES ($1,$2,$3,$4,$5,$6) " +
|
|
28
21
|
"ON CONFLICT (run_id, ordinal) DO UPDATE SET call_key = EXCLUDED.call_key, result = EXCLUDED.result"), [runId, callKeyOrdinal(entry.callKey), scope, entry.callKey, serialized, Date.now()]);
|
|
@@ -59,7 +59,7 @@ import type { Pool as PgPool } from "pg";
|
|
|
59
59
|
import { summarizeWorkflowRun, type WorkflowRunStore } from "@sema-agent/core";
|
|
60
60
|
import { type WorkflowCompletionInbox, type WorkflowCompletionInboxEntry } from "../orchestration/workflow-completion-inbox.js";
|
|
61
61
|
import type { WorkflowNotifyJournalStore, WorkflowNotifyJournalEntry } from "../orchestration/workflow-notify-journal.js";
|
|
62
|
-
import type
|
|
62
|
+
import { type WorkflowAgentSessionIndex, type WorkflowAgentSessionRow } from "../orchestration/workflow-agent-session-index.js";
|
|
63
63
|
import { type SqlDriver } from "./sql-driver.js";
|
|
64
64
|
type WorkflowRun = Parameters<WorkflowRunStore["put"]>[1];
|
|
65
65
|
/** Row cap guard: a run blob beyond this is NOT stored on update (the prior revision stays) — TiDB's
|
|
@@ -68,19 +68,41 @@ type WorkflowRun = Parameters<WorkflowRunStore["put"]>[1];
|
|
|
68
68
|
export declare const MAX_RUN_BLOB_BYTES: number;
|
|
69
69
|
declare const onWarnType: (msg: string, meta: Record<string, unknown>) => void;
|
|
70
70
|
export type InboxWarn = typeof onWarnType;
|
|
71
|
-
/** Review fix (1.108, lens④ HIGH): an oversize blob must NOT fail the update outright — core's persist chain
|
|
72
|
-
* treats `false` as a CAS loss and does NOT advance its tracked rev, so once a run outgrows the cap EVERY later
|
|
73
|
-
* write (including the TERMINAL one) keeps failing → the durable row is stuck `running` forever and the 24h
|
|
74
|
-
* orphan sweep finalizes a genuinely-completed run as `abandoned`. Instead degrade the PAYLOAD, not the write:
|
|
75
|
-
* step 1 truncate the two unbounded strings (result/error); step 2 drop the heavy arrays (phases/agents/groups)
|
|
76
|
-
* with an explicit marker. Returns null only when even the skeleton is oversize (physics — caller keeps the
|
|
77
|
-
* prior revision, matching the old behavior for that corner). Dialect-neutral (pure JS, no SQL) — shared
|
|
78
|
-
* verbatim by both twins. */
|
|
79
71
|
/** S-107:workflow_run 行的 `error`(脚本抛出的 `err.message`,core 源头不脱)在**两条写腿**(put / update 常态 + oversize 降级)
|
|
80
72
|
* 同一口脱;`result` 由 core 完成时源头脱。幂等,不抛(超大 ⇒ 占位)。 */
|
|
81
73
|
export declare function withRedactedError<T extends {
|
|
82
74
|
error?: string | undefined;
|
|
83
75
|
}>(run: T): T;
|
|
76
|
+
/** Review fix (1.108, lens④ HIGH): an oversize blob must NOT fail the update outright — core's persist chain
|
|
77
|
+
* treats `false` as a CAS loss and does NOT advance its tracked rev, so once a run outgrows the cap EVERY later
|
|
78
|
+
* write (including the TERMINAL one) keeps failing → the durable row is stuck `running` forever and the 24h
|
|
79
|
+
* orphan sweep finalizes a genuinely-completed run as `abandoned`. Instead degrade the PAYLOAD, not the write.
|
|
80
|
+
*
|
|
81
|
+
* 🔴 **S-192(7.71.0):瘦身只裁展示载荷,parked 行的恢复身份是最后才放弃的东西。**
|
|
82
|
+
* 病(修前逐字):step2 写作 `agents: []` ⇒ 一只 parked 的 workflow 子代的 `callKey` / `sessionId` /
|
|
83
|
+
* `parkedCheckpointToken` 随整段消失。server 侧复核「这条 park 仍在此 token 下」的依据**只有这一行**(join 索引
|
|
84
|
+
* 刻意不存它——它是能力;引擎自己的赎回键另住 resume journal),于是 `/decide` 的 workflow 车道在 `run.agents`
|
|
85
|
+
* 里找不到那一行 ⇒ 落 legacy 腿拿一个**错因**的 409 `conflict.resume_context_unavailable`,人的批准无处兑付;
|
|
86
|
+
* 同时 core resume 路径上那道「blob 里 parked 而 journal 缺条目 ⇒ 拒 resume」的防线(`prior.agents`)也随之失明。
|
|
87
|
+
*
|
|
88
|
+
* 阶梯 = 一张**按价值排序的放弃清单**,一级一级试到装得下为止(**判据只有一条:恢复身份最后放弃**):
|
|
89
|
+
* step 1 —— 两个无界字符串(`result` / `error`)截断带标记;
|
|
90
|
+
* step 2 —— `phases` / `groups` 整段丢,`agents` 收敛成**恢复身份行**
|
|
91
|
+
* ({@link buildParkedRecoveryIdentities};非 parked 的行不带恢复身份 ⇒ 整行丢,与修前同);
|
|
92
|
+
* step 3 —— 顶层**观测载荷**(codex r1-[high],红先复现后修):`effectiveArgs` 与 `resultFull` 整段丢、
|
|
93
|
+
* `name` / `description` 先脱后切。core 对 `effectiveArgs` 的成文口径逐字是 OBSERVATION-ONLY
|
|
94
|
+
* (never a gate input,never re-read by the engine;resume 的身份由 journal 的 callKey 携带,
|
|
95
|
+
* 不是这个字段),`resultFull` 同类(取回面),两者都**无界**。修前这一级不存在,于是阶梯
|
|
96
|
+
* 一边原样留着它们、一边把唯一那条 parked 行删掉盖标记 —— 自己违反了自己那条判据,
|
|
97
|
+
* 一条本来赎得回的审批被判死(生产上界 4 MiB 上真复现);
|
|
98
|
+
* step 4 —— 恢复身份行上的**展示名**(`label`)—— 它是这条行上唯一可弃的展示物,身份四位恒在
|
|
99
|
+
* (codex r2-[high],红先复现后修:修前阶梯一边留着每行 200 字符的 label、一边把**全部**身份
|
|
100
|
+
* 删掉盖标记 —— 同一条判据第二次被自己违反;6000 条 CJK label 的 parked 行在生产上界上
|
|
101
|
+
* 实测 `agents:[]` + `parks:6000`,而清空 label 后 6000 条身份**全都装得下**);
|
|
102
|
+
* step 5 —— 连只剩身份的行都装不下 ⇒ 丢掉它们并落**显式不可恢复标记**({@link PARK_IDENTITY_LOSS_KEY}),
|
|
103
|
+
* `/decide` 与 SLA sweep 读到它就**响亮拒**,而不是让一条赎回不了的 park 看起来像普通 miss。
|
|
104
|
+
* Returns null only when even the marked skeleton is oversize (physics — caller keeps the prior revision,
|
|
105
|
+
* matching the old behavior for that corner). Dialect-neutral (pure JS, no SQL) — shared verbatim by both twins. */
|
|
84
106
|
export declare function slimOversizeRun(run: WorkflowRun & {
|
|
85
107
|
id: string;
|
|
86
108
|
scope: string;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { redactErrorMessage, redactHead } from "../observability/run-terminal-log.js";
|
|
2
2
|
import { summarizeWorkflowRun, isTerminalWorkflowStatus, WorkflowRunStoreError } from "@sema-agent/core";
|
|
3
3
|
import { foldKeyFamily, MAX_PENDING_PER_SESSION, PURGE_FENCE_MS, SERVED_FENCE_MS, } from "../orchestration/workflow-completion-inbox.js";
|
|
4
|
+
import { buildParkedRecoveryIdentities, buildParkIdentityLoss, PARK_IDENTITY_LOSS_KEY } from "../orchestration/workflow-agent-session-index.js";
|
|
4
5
|
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
5
6
|
import { isDupKeyError } from "./sql-errors.js";
|
|
6
7
|
export const MAX_RUN_BLOB_BYTES = 4 * 1024 * 1024;
|
|
8
|
+
const SLIM_HEADER_MAX = 200;
|
|
7
9
|
const onWarnType = (msg, meta) => void [msg, meta];
|
|
8
10
|
export function withRedactedError(run) {
|
|
9
11
|
if (typeof run.error !== "string")
|
|
@@ -22,8 +24,33 @@ export function slimOversizeRun(run, maxBytes) {
|
|
|
22
24
|
let blob = JSON.stringify(step1);
|
|
23
25
|
if (Buffer.byteLength(blob) <= maxBytes)
|
|
24
26
|
return blob;
|
|
25
|
-
const
|
|
27
|
+
const identities = buildParkedRecoveryIdentities(run.agents);
|
|
28
|
+
const shortError = step1.error ? String(step1.error).slice(0, 1000) + MARK : MARK;
|
|
29
|
+
const step2 = { ...step1, phases: [], agents: identities, groups: [], error: shortError };
|
|
26
30
|
blob = JSON.stringify(step2);
|
|
31
|
+
if (Buffer.byteLength(blob) <= maxBytes)
|
|
32
|
+
return blob;
|
|
33
|
+
const step3 = { ...step2 };
|
|
34
|
+
delete step3.effectiveArgs;
|
|
35
|
+
delete step3.resultFull;
|
|
36
|
+
if (typeof step3.name === "string")
|
|
37
|
+
step3.name = redactHead(step3.name, SLIM_HEADER_MAX);
|
|
38
|
+
if (typeof step3.description === "string")
|
|
39
|
+
step3.description = redactHead(step3.description, SLIM_HEADER_MAX);
|
|
40
|
+
blob = JSON.stringify(step3);
|
|
41
|
+
if (Buffer.byteLength(blob) <= maxBytes)
|
|
42
|
+
return blob;
|
|
43
|
+
const bareIdentities = buildParkedRecoveryIdentities(run.agents, 0);
|
|
44
|
+
const step4 = { ...step3, agents: bareIdentities };
|
|
45
|
+
blob = JSON.stringify(step4);
|
|
46
|
+
if (Buffer.byteLength(blob) <= maxBytes)
|
|
47
|
+
return blob;
|
|
48
|
+
const step5 = {
|
|
49
|
+
...step4,
|
|
50
|
+
agents: [],
|
|
51
|
+
...(identities.length > 0 ? { [PARK_IDENTITY_LOSS_KEY]: buildParkIdentityLoss(identities.length) } : {}),
|
|
52
|
+
};
|
|
53
|
+
blob = JSON.stringify(step5);
|
|
27
54
|
return Buffer.byteLength(blob) <= maxBytes ? blob : null;
|
|
28
55
|
}
|
|
29
56
|
export class SqlWorkflowRunStore {
|
package/dist/posture-source.d.ts
CHANGED
|
@@ -13,8 +13,16 @@
|
|
|
13
13
|
* `env 显式` > `center 发布` > `posture 派生` > `引擎/服务端默认`。
|
|
14
14
|
*
|
|
15
15
|
* ## 定界
|
|
16
|
-
*
|
|
17
|
-
* ({@link PostureKnobFacts.postureMeaning})
|
|
16
|
+
* 本模块只说**来源**。值本身、以及**两条键专属臂**的句子——「这台机器凭什么派生出这个值」
|
|
17
|
+
* ({@link PostureKnobFacts.postureMeaning})与「组织发布的值在这台机器上怎么生效」
|
|
18
|
+
* ({@link PostureKnobFacts.centerMeaning})——由每个键自己给。那两句话是键专属的事实,共享它们只会
|
|
19
|
+
* 得到一句谁都不认的空话,或者更坏:一句**对某些键是假的**话。
|
|
20
|
+
*
|
|
21
|
+
* 🔴 `centerMeaning` 是 G-7(test [ref])的修复面,成因是一次真的过期断言:这一臂原本硬编码
|
|
22
|
+
* 「applied from the config-center (**restart-to-apply**)」,而 S-174(core 7.10.0 [ref])之后
|
|
23
|
+
* `readFace.face` 是**热换席**(`Runner.swapDeps({readFace})`,下一拍 refresh 当场换进全部 Runner,
|
|
24
|
+
* `read-face` 重启片也不再为它签理由)——运维照那句话去滚动重启一片 fleet,重启的是一个已经生效的变更。
|
|
25
|
+
* 生效时机是**键**的事实,不是四词表的属性,所以它跟 `postureMeaning` 一样归键。
|
|
18
26
|
*/
|
|
19
27
|
/** 旋钮取值来源的闭集词表(顺序 = 优先序)。 */
|
|
20
28
|
export declare const POSTURE_SOURCE_WORDS: readonly ["env", "center", "posture", "engine-default"];
|
|
@@ -25,13 +33,25 @@ export interface PostureKnobFacts {
|
|
|
25
33
|
/** 这台机器上占住这个键的 env 变量名(指路句要逐字点它,运维才知道去改哪个)。 */
|
|
26
34
|
readonly knob: string;
|
|
27
35
|
readonly source: PostureSource;
|
|
36
|
+
/**
|
|
37
|
+
* `center` 那一臂**专属**的一句话:组织发布的这个值在这台机器上**怎么生效**(热换席?等重启?
|
|
38
|
+
* 还是这个键根本没有 center 腿?),以及怎么把这个键取回来。只在 `source === "center"` 时被读到。
|
|
39
|
+
*
|
|
40
|
+
* 🔴 为什么归键而不是共享(G-7 的成因):生效时机是键的事实。同一句「restart-to-apply」对
|
|
41
|
+
* `readFace.face` 是假的(S-174 热换席),对三个 S-178 旋钮则连触发条件都没有(`config.ts` 的
|
|
42
|
+
* `postureSourceOfKnob` / `postureSourceOfNumEnv` 只产 env/posture/engine-default,center 写不到
|
|
43
|
+
* 它们)—— 一句共享的硬编码必然对其中一类说谎。
|
|
44
|
+
*/
|
|
45
|
+
readonly centerMeaning: string;
|
|
28
46
|
/**
|
|
29
47
|
* `posture` 那一臂**专属**的一句话:这台机器为什么派生出了这个值,以及怎么把它钉回去。
|
|
30
48
|
* 只在 `source === "posture"` 时被读到 —— 别的臂的句子是通用的(见 {@link postureSourceNote})。
|
|
31
49
|
*/
|
|
32
50
|
readonly postureMeaning: string;
|
|
33
51
|
}
|
|
34
|
-
/** 运维指路句 —— **穷举 switch,无缺省臂**:四词闭集加一个成员这里就编译红。
|
|
52
|
+
/** 运维指路句 —— **穷举 switch,无缺省臂**:四词闭集加一个成员这里就编译红。
|
|
53
|
+
* 两条**通用**臂(`env` / `engine-default`)的句子在这里写死并点名旋钮;两条**键专属**臂
|
|
54
|
+
* (`center` / `posture`)原样取键给的那句话(理由见 {@link PostureKnobFacts} 的两个字段注)。 */
|
|
35
55
|
export declare function postureSourceNote(facts: PostureKnobFacts): string;
|
|
36
56
|
/**
|
|
37
57
|
* 这个值是**运维显式表态**的吗?
|
package/dist/posture-source.js
CHANGED
|
@@ -4,7 +4,7 @@ export function postureSourceNote(facts) {
|
|
|
4
4
|
case "env":
|
|
5
5
|
return `pinned by this machine's ${facts.knob} env var (deployment sovereignty — it wins over any published value for this key)`;
|
|
6
6
|
case "center":
|
|
7
|
-
return
|
|
7
|
+
return facts.centerMeaning;
|
|
8
8
|
case "posture":
|
|
9
9
|
return facts.postureMeaning;
|
|
10
10
|
case "engine-default":
|
|
@@ -46,7 +46,14 @@ export interface ReadFacePostureFacts {
|
|
|
46
46
|
*/
|
|
47
47
|
export declare function readFaceForPosture(singleUserTurnkey: boolean): ReadFacePostureFacts;
|
|
48
48
|
/** 本档的运维指路句 —— 骨架由共享件 {@link postureSourceNote} 给(四词穷举在那里,加词即编译红);
|
|
49
|
-
*
|
|
49
|
+
* 本函数提供两条**键专属**臂(`center` / `posture`)的事实句。
|
|
50
|
+
*
|
|
51
|
+
* 🔴 `center` 那句话的事实来源是 S-174(core 7.10.0 [ref]):`RunnerDeps.readFace` 进了 core 的可换席
|
|
52
|
+
* 闭集 `SWAPPABLE_DEP_SEATS` ⇒ 组织发布/撤回一个档位,**下一拍 refresh 当场换进全部 Runner**,
|
|
53
|
+
* `config-center/restart-signal.ts` 的 `read-face` 片也不再为它签重启理由(判据同源 =
|
|
54
|
+
* `CENTER_READ_FACE_LIVE_KEYS`,由 core 的 `isSwappableDepSeat` 派生)。本句此前逐字写着
|
|
55
|
+
* 「(restart-to-apply)」—— G-7 抓获的那条过期断言,方向恰好是「让运维为一个已生效的变更重启 fleet」。
|
|
56
|
+
* ⚠️ 只说 `face` 这一席:三个 deny 键仍是 boot-baked,那半场的话不在这条读面上(它们没有来源位)。 */
|
|
50
57
|
export declare function readFaceSourceNote(source: ReadFaceSource): string;
|
|
51
58
|
/** operator 诊断面的投影(`GET /v1/diagnostics/wiring`)。`face:null` = 本部署没钉档,引擎默认接管。 */
|
|
52
59
|
export declare function buildReadFaceWiring(facts: ReadFacePostureFacts): {
|
|
@@ -7,6 +7,7 @@ export function readFaceSourceNote(source) {
|
|
|
7
7
|
return postureSourceNote({
|
|
8
8
|
knob: "READ_FACE",
|
|
9
9
|
source,
|
|
10
|
+
centerMeaning: "published by the config-center and applied HOT — the containment FACE is a swappable engine seat (core 7.10.0), so the next refresh (or POST /v1/admin/config/refresh) swaps it into every Runner with NO restart; set READ_FACE on this machine to take the key back",
|
|
10
11
|
postureMeaning: "derived from the single-user turnkey posture (REQUIRE_PRINCIPAL is not set): reads outside the workspace roots are allowed by default — set READ_FACE=roots to pin the containing posture back",
|
|
11
12
|
});
|
|
12
13
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RunCancelContext } from "./plugins/store-contracts.js";
|
|
1
|
+
import type { PersistedTaskResult, RunCancelContext } from "./plugins/store-contracts.js";
|
|
2
2
|
/**
|
|
3
3
|
* @param taskId 这条 run 的账本 id(turn 活性登记的键)。
|
|
4
4
|
* @param startedAtMs 这条 run 起跑的墙钟时刻(epoch ms):取消腿手上有行就用行的 `createdAt`,
|
|
@@ -10,4 +10,53 @@ import type { RunCancelContext } from "./plugins/store-contracts.js";
|
|
|
10
10
|
* 断言「我看过现场,什么都没有」,而事实是「我什么都没看见」)。
|
|
11
11
|
*/
|
|
12
12
|
export declare function buildCancelContext(taskId: string, startedAtMs: number, nowMs?: number): RunCancelContext | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* 🔴 **用户取消的机器码 —— 全仓单源**(合并重扫 [5])。
|
|
15
|
+
*
|
|
16
|
+
* run 行的 `errorCode` 列、因由臂上的 `terminal.code`、`tasks_total{status}` 的 metrics 标签、fleet 行的
|
|
17
|
+
* 终态词,是**同一个词**;同一个词就该同一个来源。此前仓里是「一个词两只常量 + 十余处字面量」
|
|
18
|
+
* (`approval-reconciler.ts` 另有一份 `CANCELLED_ERROR_CODE`),改它的值会让 recordDone / fleet 标签 /
|
|
19
|
+
* reconciler 的 VOID 判据 / cancel 端点的幂等判据静默分叉。
|
|
20
|
+
*
|
|
21
|
+
* 机器门(新写者当场红):`test/cancel-terminal-single-owner.test.ts` 的 G2/G3。
|
|
22
|
+
*/
|
|
23
|
+
export declare const CANCELLED_CODE = "cancelled";
|
|
24
|
+
/**
|
|
25
|
+
* S-104 —— **取消终局的唯一铸点**。三条用户取消腿(sync stream 的断连支与 cancel-verb 支、durable bg 腿)
|
|
26
|
+
* 都从这里出结果,于是「一次取消在账本/wire 上长什么样」只有一份口径。
|
|
27
|
+
*
|
|
28
|
+
* ## 两种到达形,一条规则
|
|
29
|
+
* `engineResult` 在场与否 = 「引擎有没有给出终局」,不是两条规则:
|
|
30
|
+
* · **在场**(现行常态):**跑起来之后**的 `signal.aborted` 被 core 当成流内事件优雅收尾 —— 流照常
|
|
31
|
+
* 发 `done`,`terminal` 是一条**不带码**的 `failed`。这里在**它**之上补标,不另造一份结果(另造会把
|
|
32
|
+
* 引擎已经算出的 `stats` / `result` / `editedFiles` 全丢掉)。
|
|
33
|
+
* · **缺席**:根本没读到 done —— 客户端断连后迭代器早收,或**开跑之前**就已经立起的旗
|
|
34
|
+
* (亲核 core 7.11.2 `dist/core/runner/runtask.js:623-626`:`runLocked` 入口对已 aborted 的 signal
|
|
35
|
+
* 抛 `AbortError`)⇒ 合成一条诚实的取消终局。
|
|
36
|
+
*
|
|
37
|
+
* ## 「只补空座」纪律(与 core 的 `amendTerminal` 同规),而且**补码与补快照同进同退**
|
|
38
|
+
* · 引擎给的终局**不是无码的 failed**(它完成了 / 阻塞了 / 自己带了码)⇒ **整只原样返回**。
|
|
39
|
+
* 这一条是本车自查轮逮住的自造缺陷:首版只让**补码**受这条纪律管、快照却无条件挂,于是「取消动词
|
|
40
|
+
* 到了、但活其实已经干完了」会产出一条 `completed` 却带着取消便签的终局 —— 契约 §5.3 逐字写着
|
|
41
|
+
* 「只在用户取消形上铸」,两个方向的读者都会读错。一次与 abort 赛跑的真失败(`brain.network`)同理:
|
|
42
|
+
* 贴成 `cancelled` 会把故障统计洗白。
|
|
43
|
+
* · `cancelContext`:已经有快照就不重铸(重铸会把「上一条引擎事件多久以前」重新计时)。
|
|
44
|
+
*
|
|
45
|
+
* ## 为什么不判「这条 failed 是不是 abort 形」
|
|
46
|
+
* 判据在**调用点**:调用点问的是「本腿这次是不是被取消动词打中了」(`cancelCtrl.signal.aborted` /
|
|
47
|
+
* `cancelledViaVerb`),那是 server 自己的事实。让本函数去嗅 `terminal.message` 的字面就是再造事实
|
|
48
|
+
* —— [ref] 二那次事故的同一个形。
|
|
49
|
+
*/
|
|
50
|
+
export declare function mintCancelledResult(args: {
|
|
51
|
+
/** 这条 run 的账本 id(活性登记的键;账本不在场时给空串,快照按「证不出」处理)。 */
|
|
52
|
+
taskId: string;
|
|
53
|
+
/** 会话 id(合成形要写进结果;引擎给了结果时不用)。 */
|
|
54
|
+
sessionId: string;
|
|
55
|
+
/** 本腿起跑的墙钟时刻(epoch ms)。语义与缺席纪律见 {@link buildCancelContext}。 */
|
|
56
|
+
startedAtMs: number;
|
|
57
|
+
/** 合成形的失败文案(引擎给了终局时不用)。 */
|
|
58
|
+
message: string;
|
|
59
|
+
/** 引擎给出的终局(在场 ⇒ 在它之上补标)。 */
|
|
60
|
+
engineResult?: PersistedTaskResult;
|
|
61
|
+
}): PersistedTaskResult;
|
|
13
62
|
//# sourceMappingURL=run-cancel-context.d.ts.map
|
|
@@ -12,4 +12,26 @@ export function buildCancelContext(taskId, startedAtMs, nowMs = Date.now()) {
|
|
|
12
12
|
...(elapsedMs !== undefined ? { elapsedMs } : {}),
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
+
export const CANCELLED_CODE = "cancelled";
|
|
16
|
+
export function mintCancelledResult(args) {
|
|
17
|
+
const ctx = args.taskId === "" ? undefined : buildCancelContext(args.taskId, args.startedAtMs);
|
|
18
|
+
const engine = args.engineResult;
|
|
19
|
+
if (engine !== undefined) {
|
|
20
|
+
if (engine.terminal.kind !== "failed" || engine.terminal.code !== undefined)
|
|
21
|
+
return engine;
|
|
22
|
+
return {
|
|
23
|
+
...engine,
|
|
24
|
+
terminal: { ...engine.terminal, code: CANCELLED_CODE },
|
|
25
|
+
...(engine.cancelContext === undefined && ctx !== undefined ? { cancelContext: ctx } : {}),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
taskId: args.taskId,
|
|
30
|
+
sessionId: args.sessionId,
|
|
31
|
+
terminal: { kind: "failed", code: CANCELLED_CODE, message: args.message },
|
|
32
|
+
result: "",
|
|
33
|
+
stats: { turns: 0, tokens: 0 },
|
|
34
|
+
...(ctx ? { cancelContext: ctx } : {}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
15
37
|
//# sourceMappingURL=run-cancel-context.js.map
|