@sema-agent/server 7.110.0 → 7.111.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 +8 -0
- package/dist/approval-ask-audit-store.d.ts +38 -25
- package/dist/approval-ask-audit-store.js +26 -59
- package/dist/bench/s1/run-firm.d.ts +11 -0
- package/dist/bench/s1/run-firm.js +47 -32
- package/dist/boot/coordinators.js +1 -1
- package/dist/boot/session-faces.d.ts +4 -0
- package/dist/boot/session-faces.js +3 -1
- package/dist/boot/stage-07-capability-layer.d.ts +1 -0
- package/dist/boot/stage-07-capability-layer.js +4 -3
- package/dist/boot/stage-08-reapers.d.ts +1 -0
- package/dist/boot/stage-09-leader.d.ts +1 -0
- package/dist/boot/stage-09-leader.js +2 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -0
- package/dist/boot/workflow-orchestration.js +1 -1
- package/dist/http/resume-legs.js +6 -3
- package/dist/http/routes/runs.js +14 -1
- package/dist/http/routes/tasks.js +8 -6
- package/dist/http/server.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/instruction-selection.d.ts +34 -0
- package/dist/instruction-selection.js +23 -0
- package/dist/observability/corrupt-read-seat.d.ts +18 -0
- package/dist/observability/corrupt-read-seat.js +18 -0
- package/dist/observability/fail-open.d.ts +15 -3
- package/dist/observability/fail-open.js +15 -3
- package/dist/orchestration/workflow-agent-session-index.d.ts +12 -23
- package/dist/orchestration/workflow-agent-session-index.js +11 -50
- package/dist/orchestration/workflow-notify-journal.d.ts +11 -8
- package/dist/orchestration/workflow-notify-journal.js +20 -36
- package/dist/plugins/file-outcome-sink.d.ts +4 -1
- package/dist/plugins/file-outcome-sink.js +9 -3
- package/dist/plugins/file-run-store.js +3 -3
- package/dist/plugins/retention-store-sql.d.ts +1 -1
- package/dist/plugins/retention-store-sql.js +2 -0
- package/dist/plugins/store-backend.js +1 -0
- package/dist/plugins/store-contracts.d.ts +19 -2
- package/dist/plugins/store-contracts.js +9 -0
- package/dist/plugins/user-input-ledger-file.d.ts +4 -2
- package/dist/plugins/user-input-ledger-file.js +11 -1
- package/dist/plugins/user-input-ledger-sql.d.ts +14 -4
- package/dist/plugins/user-input-ledger-sql.js +46 -18
- package/dist/project-memory.d.ts +10 -2
- package/dist/project-memory.js +5 -2
- package/dist/runs.js +5 -3
- package/dist/trace/ledger-sink.d.ts +4 -0
- package/dist/trace/ledger-sink.js +1 -1
- package/dist/trace/project.d.ts +11 -3
- package/dist/trace/project.js +6 -5
- package/dist/trace/self-minted-wire.d.ts +26 -0
- package/dist/trace/self-minted-wire.js +9 -0
- package/dist/trace/wire-projection-faces.d.ts +3 -3
- package/dist/trace/wire-projection-faces.js +4 -2
- package/dist/user-input-ledger.d.ts +101 -48
- package/dist/user-input-ledger.js +52 -35
- package/package.json +2 -2
package/MIGRATION.md
CHANGED
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
> ⚠️ 完整清单在仓库根 `CHANGELOG.md`——它**不随 npm tarball 出包**(本文件随包)。看完整迁移窗的
|
|
8
8
|
> 权威姿势是源码 tag diff:`git diff v<旧>..v<新>`(每版都推 `v<版本>` tag);npm 包页也镜像 CHANGELOG。
|
|
9
9
|
|
|
10
|
+
## 7.111.0(车TA,S-961 / S-972)—— `wiring_manifest` 帧 +`instructions` 段(additive,零动作)
|
|
11
|
+
|
|
12
|
+
- **wire(additive,零动作)**:`wiring_manifest` 帧(四条腿:流式 SSE / 账本行 / 非流式 `wiringManifest` 回执 / 续跑腿)多一段本服务自铸的 `instructions: { selected: string[] }` = 这条腿选中的项目指令文件(相对 cwd 的链成员);`[]` = 没有,缺席 = 无陈述(契约附录 G.21)。按封闭 schema 校验该帧的消费方加这一可选键;不要它的部署 `PROJECT_MEMORY_ENABLED=false`(连同项目记忆注入一起关)。`task.user_steer_undrained{cause:"crashed"}` 的 `message` 改句序(行动句在前、< 400 字符),`detail` 不变 —— 按文本匹配的脚本改读 `detail.cause`(S-972)。
|
|
13
|
+
- **会话删除 / 托管留存级联接上用户输入受理账本(车TC,S-965;零 DDL、零动作)**:`DELETE /v1/sessions/:id` 与 SQL 后端的托管留存清扫自本版起连带删掉该会话的受理行(SQL `user_input_acceptance` 按 `session_id` 删;local `ledger.jsonl` 追加一行会话级墓碑 `{"kind":"erased","sessionId":…,"at":…}`);`DELETE` 回执形不变。**回滚**(local 车道):≤7.110.x 不认 `erased` 行 —— 启动 warn `user_input_ledger_corrupt_read`,被删会话的行在旧进程里复现;要干净回滚,停服后删 `<localDataRoot>/user-input-ledger/ledger.jsonl`(代价 = 丢掉还没告知的崩溃丢失提醒)。SQL 车道回滚无影响。**嵌入方(类型面硬破坏)**:`UserInputLedgerStore`(包根导出型)新增必选 `deleteBySession(sessionId): Promise<number>`;自建 `StoreBackend.userInputLedger()` 的嵌入方 tsc 红即通知。
|
|
14
|
+
- **同步子代的 steer 入用户输入受理账本(车TF,S-966;零 DDL、零动作)**:`POST /v1/runs/:id/subagents/:target/steer` 自本版起与 run 自己的 steer 同一本账(先账后引擎;行记在回执上的那条 run 名下)——进程在子代收场前崩了 ⇒ 同会话下一条 run 首拍的 `task.user_steer_undrained{cause:"crashed"}` 把它并进那条 run 的条数;账本写不进 ⇒ 该路由回 **503 `steering.ledger_unavailable`**(此前 200);200 回执 additive 多一个 `messageId`(按封闭 schema 校验回执的消费方加这一可选键)。workflow agent 的 steer 不变(不入账,崩溃丢失仍零告知,另票)。**回滚**:无数据动作(行形不变;≤7.110.x 读得懂这些行,照常收敛 / 告知)。
|
|
15
|
+
- **用户输入受理账本收敛判词合一 / SQL 坏行逐行跳过(车TD,S-969;零 DDL、零动作)**:local 车道 boot 收敛自本版起读 run 行 —— 前世进程没结清、而承载它的 run 已正常收场 / 已挂起的受理补结清、不再告知(7.110.x 一律按崩溃告知);SQL 表里读不出的行逐行跳过 + 计数 `server.user-input-ledger.corrupt-record-skipped`,其余照常(7.110.x 整页读失败)。**嵌入方(类型面硬破坏)**:`UserInputLedgerStore.listOpen` 返回 `Promise<OpenScanPage>`(`{ open, next? }`,`next` = 这一页原始末行的键、缺席 = 扫到底;包根新导出 `OpenScanPage`)—— 自建店改交页对象、读不出的行自己跳过并照原始行给游标;`WriterGoneEvidence` 改为 `{ kind, runs }`(两臂都带 run 店读口)。tsc 红即通知。**回滚**:回到 7.110.x 无数据动作(账本行形不变)。
|
|
16
|
+
- **`@sema-agent/core` 7.38.0 → 7.38.2(车TG;两只 patch,core 硬破坏 0、wire 键 / 码 / 闭集零变化,零动作)**:① 内建读拒表的家锚定行(`~/.sema/settings.json` 等)另落**账户家**(core 7.38.1 #1244,S-941)—— 引擎进程的 `HOME` 被改指时,账户真实家下的同名文件由「读得到」变为 `read_path_denied`;真要读的部署用 `READ_DENY_BUILTIN_EXCLUDE` 删那一行。S-941 时期按 DEPLOY-PREREQS 加的 `READ_DENY_PATTERNS` 不锚家同名行可撤(不撤无害)。账户查询无答的主机上这些行在任何位置判(项目自己的 `.sema/settings.json` 也拒,一次 `console.warn`,下一条 run 重问)。② local 车道 File 店族的 JSONL 末段整条记录缺换行 ⇒ 认下并在下次打开补换行(core 7.38.2 #1278,S-975);末段非对象 ⇒ 截 + 多一条 `corrupt-record-skipped` 计数。**回滚**到 ≤7.110.x:回退版本的 core 会把「只缺换行」的末行截掉(只在崩溃留下的那一形上;盘上格式不变,零数据动作)。
|
|
17
|
+
|
|
10
18
|
## 7.110.0(车ST,S-955;小车 SV,S-956;车SX,S-958 / S-959;小车 SA,S-962 / S-963;主会话 S-964;车SW,S-928)—— `@sema-agent/core` 7.38.0(minor,core 硬破坏 0):通告码受众 / 铸者改读 core 表 · `terminal.failed.detail` 过境 · hook 载荷 `permission_mode` = 腿实时模式 · 类对判据走 core 屏 · run-local 无 TTY 拒记部署政策名下 · 复合体读不出词的 PreToolUse 结果交引擎屏 · 🔴 `MEMORY_SYNC_SCOPE` 缺省不再从非 v2 `MEMORY_SCOPE` 派生(拒启) · 崩溃丢掉的 200-applied steer 下一条 run 告知(用户输入受理账本)
|
|
11
19
|
|
|
12
20
|
- **🔴 记忆同步 env(S-964,配置面硬破坏)**:单用户 file 引擎上设了 `MEMORY_SYNC_URL` / `MEMORY_SYNC_TOKEN`、**没设** `MEMORY_SYNC_SCOPE`、且 `MEMORY_SCOPE` 不是 typed 键(`user:` / `org:` / `proj:` / `userproj:`;含缺省 `local`)的部署**升级后拒启**。此前这一形派生同步层 `user:<MEMORY_SCOPE>` —— 一个本部署从不写的层(写家是裸 `MEMORY_SCOPE`,core 同步按同一个名字取条目)⇒ 同步腿自 142-S2.5-W1 起每轮推 0 条。出路二选一(错误句原文点名):`MEMORY_SYNC_SCOPE=<要复制的层>`,或把 `MEMORY_SCOPE` 设成中心期待的 typed 键(如 `user:<tenant>`,缺省同步层随之同名)。显式 `MEMORY_SYNC_SCOPE` 的部署零变化。
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* `server/designs/2026-08-31-s38-local-stream-approval-v1.md`,v1 三臂 + v1.1 §6 两件增益)——
|
|
4
4
|
* local 车道(`DB_BACKEND=local`)的 **File ask 审计店**。SINGLE-FILE 店([ref] A12 单文件店纪律,
|
|
5
5
|
* 形照 `plugins/memory-optout-grant-store-sql.ts`:一个实现文件、数据形/偏离/坏值姿态全部成文在头注;
|
|
6
|
-
*
|
|
7
|
-
* 重放容忍撕尾行)。
|
|
6
|
+
* 崩溃安全机制 = append-only JSONL、fsync 逐行、重放容忍撕尾行 —— 追加与重放都走本仓 JSONL 店族的**共用口**(S-967,见下「追加 / 重放口」段))。
|
|
8
7
|
*
|
|
9
8
|
* ## 它治的病([ref] / [ref]① 车定界钉;⚠️ **病因描述是 [ref] 当时的事实,S-384 起已变**)
|
|
10
9
|
* 当时:local 车道 ask 账进程内易失(`InMemoryApprovalAskStore`),活卡窗内崩溃 = **零痕迹**:重启后
|
|
@@ -135,11 +134,24 @@
|
|
|
135
134
|
* ## `crashed_before_park` 与 FAIL-OPEN census 的关系(稿未闭环②,施工时裁定,成文于此)
|
|
136
135
|
* **不入词表**:census 词表登记的是「走了兜底而外面看不出」的**臂**;`crashed_before_park` 是一条
|
|
137
136
|
* **终态成因注**(收敛器 fail-closed 地把孤儿收成 DENIED 同码并在读面显形),它的存在恰恰是响亮化
|
|
138
|
-
*
|
|
139
|
-
* [ref] 律走 `recordFailOpen("server.approval-ask-audit.append-failed")`(census
|
|
140
|
-
*
|
|
137
|
+
* 本身,不是静默降级。本店的静默降级臂有两条:① 运行期 append 失败(审批可用性不押在审计盘上),已按
|
|
138
|
+
* [ref] 律走 `recordFailOpen("server.approval-ask-audit.append-failed")`(census 第 53 行);② boot 重放跳过中间坏行
|
|
139
|
+
* (S-967 起由重放属主逐条披露 + `server.approval-ask-audit.corrupt-record-skipped`,census 第 106 行;此前只计 `corruptLines`)。
|
|
140
|
+
* boot 期(mkdir/重放的读错)失败则**拒启**(与 File 店族同律:`FileStorageBackend` ctor 同 root 同姿态)——
|
|
141
141
|
* 一台「审计写不进却继续批」的机器 = [ref] 换个皮回来。
|
|
142
142
|
*
|
|
143
|
+
* ## 追加 / 重放口(S-967:一只追加日志实现,不留两份)
|
|
144
|
+
* 追加 = core `AppendLog`(惰性打开,首次 append 才铸文件;一行 = 单次 `writeSync` 的 `JSON.stringify(rec) + "\n"`、`fsync=true` 逐行;文件 0o600、目录 0o700;
|
|
145
|
+
* 末段无换行的字节在打开与每次追加前由它按 core 的同一只判词修 —— 解析成对象 = 丢了换行的整条记录,**补换行、字节保留**;不解析 = 崩溃半写(撕尾),
|
|
146
|
+
* **截断**回最后一个换行;解析成非对象 = 截断并披露(core 7.38.2 #1278))。重放 = `replayJsonlSkipping`(登记名 `approval-ask-audit`,fail-open + F 类 tag
|
|
147
|
+
* `server.approval-ask-audit.corrupt-record-skipped`:中间坏行逐条披露 + 计数;撕尾不计,无换行的完整末行照常进重放)。本店只剩两样自己的东西:**形判据**({@link LEDGER_SHAPE},逐 `kind` 的最小必填键
|
|
148
|
+
* + 词表外成因行「计坏行、结清照认」)与**失败方向**(运行期 append 失败 ⇒ `recordFailOpen("server.approval-ask-audit.append-failed")`,审批照常)。
|
|
149
|
+
* **一行「在账」的判据 = 它的字节解析成一只对象**(与共用口同一条,core 7.38.2 #1278:读面、打开修尾、追加门修尾三站问同一只判词;换行只是边界的拼法,
|
|
150
|
+
* 不是记录存在的条件 —— 没有一只 JSON 对象的真前缀能被解析,所以解析得出的末行就是整条记录;失败的 append 由 core 截回它开写处,不留自己的字节)。
|
|
151
|
+
* 修前本店手写了第二份追加 / 修尾(私有的脏尾标 + 写字节注入席),撕尾用**孤立换行隔离**保留残字节而不是截断;与共用口的形差(撕尾隔离 ⇄ 截断、撕尾计数、
|
|
152
|
+
* 字节全落却报错后多落的孤立换行、空白行计数、坏行披露)逐条记在 CHANGELOG 7.111.0 S-967 段,读数钉 = `test/approval-ask-audit.test.ts` 的「S-967」两组;
|
|
153
|
+
* 「完整 JSON 却缺换行的末行」一条(R2)在 core 7.38.2 下与修前同形(认下 + 补换行,C2 正向钉),不再是形差。
|
|
154
|
+
*
|
|
143
155
|
* ## 装配(为什么不在 `plugins/store-backend.ts`)
|
|
144
156
|
* 本店是协调器侧的审计 sink,不是跨后端 store 契约的一员(SQL 车道有真 ask 店,永不装本店)——
|
|
145
157
|
* 装配点在 `boot/coordinators.ts`(判据:`toolApprovalEnabled` ∧ `backend.kind === "local"` ∧
|
|
@@ -152,10 +164,14 @@
|
|
|
152
164
|
* journal 同款成文)。读面列表按收敛时刻新前旧后、上限 {@link CRASH_CONVERGED_LIST_MAX}(有界 wire)。
|
|
153
165
|
*/
|
|
154
166
|
import type { HostDecision } from "./host-decision.js";
|
|
167
|
+
import { AppendLog } from "@sema-agent/core";
|
|
168
|
+
import type { Logger } from "./observability/logger.js";
|
|
155
169
|
/** 数据子目录名(挂在 local 数据根下,与 `workflow-notify/` 同层同形)。 */
|
|
156
170
|
export declare const APPROVAL_ASK_AUDIT_DIR = "approval-ask-audit";
|
|
157
171
|
/** 崩溃收敛行的读面上限(新前旧后截断;有界 wire —— 读面不许随月份线性长)。 */
|
|
158
172
|
export declare const CRASH_CONVERGED_LIST_MAX = 200;
|
|
173
|
+
/** 追加口的窄形(core `AppendLog` 的 `append` / `close`)—— 店的测试注入席({@link FileApprovalAskAuditStore} 构造器 `internals.appendLog`)按它造替身。 */
|
|
174
|
+
export type AuditAppendPort = Pick<AppendLog, "append" | "close">;
|
|
159
175
|
/** 铸造点入参(协调器 askBroadcast 的 register-before-emit 之后挂线)。 */
|
|
160
176
|
export interface AuditAskInput {
|
|
161
177
|
/** wire 身份 = 呈卡帧上那把 `approvalId`(respond/queue 同源可达;不是本地 pending 键)。 */
|
|
@@ -302,14 +318,10 @@ export type LateSettleUnresolved = "ambiguous_anchor";
|
|
|
302
318
|
export declare class FileApprovalAskAuditStore implements ApprovalAskAuditSink, ApprovalAskAuditReadFace {
|
|
303
319
|
/** 账本绝对路径(测试/取证按文件字节对账 —— 崩溃语义的真源是文件,不是内存投影)。 */
|
|
304
320
|
readonly ledgerPath: string;
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
* 失败 ⇒ 脏标保持,本条记录走留痕臂 —— 绝不往可能的残尾上直接续字节。 */
|
|
310
|
-
private tailDirty;
|
|
311
|
-
/** 写字节口(测试注入席:半写故障的确定性构造;生产恒缺省 = `fs.writeSync`)。 */
|
|
312
|
-
private readonly writeBytes;
|
|
321
|
+
/** 追加口(core `AppendLog`,惰性打开 —— 首次 append 才铸文件;`close()` 后下一次 append 重开)。撕尾修复 / 短写补齐 / 逐行 fsync 都在它里面,本店不再自写一份。 */
|
|
322
|
+
private log;
|
|
323
|
+
/** 追加口的铸造座(测试注入席:单条写失败 / 半写的确定性构造;生产恒缺省 = `new AppendLog(path)`)。 */
|
|
324
|
+
private readonly openLog;
|
|
313
325
|
/** boot 重放快照(冻结于构造时刻 = 前世账本;本进程新写的行不进它 —— 收敛只对前世行)。 */
|
|
314
326
|
private readonly replayed;
|
|
315
327
|
/** 前世账本里的腿闭事件(taskId + 行序;decided 臂按「决议行之后」判)。 */
|
|
@@ -339,24 +351,25 @@ export declare class FileApprovalAskAuditStore implements ApprovalAskAuditSink,
|
|
|
339
351
|
/** [ref] 反查索引 ③:approvalId → 铸造坐标(命中后回填 late 行的 `taskId`)。 */
|
|
340
352
|
private readonly askTaskId;
|
|
341
353
|
/**
|
|
342
|
-
* boot 期 fail-stop
|
|
354
|
+
* boot 期 fail-stop:建目录 / 重放的**读错**(非 ENOENT)⇒ 抛 ⇒ **拒启**(File 店族同律)。root = local 数据根
|
|
343
355
|
* (`config.localDataRoot`;与 `LocalBackend` 同源,见头注装配段)。
|
|
344
|
-
* 0o700 目录 + 0o600 账本:行携租户身份(owner/sessionId),不得组/他人可读
|
|
345
|
-
* (
|
|
356
|
+
* 0o700 目录 + 0o600 账本:行携租户身份(owner/sessionId),不得组/他人可读(目录由 core `ensureDir`、账本由 `AppendLog` 铸)。
|
|
357
|
+
* 追加口惰性打开(S-967,与 `FileUserInputLedgerStore` 同形):末段的修(撕尾截断 / 完整末行补换行)随首次 append 的打开发生 —— 重放读数不依赖它
|
|
358
|
+
* (撕尾不进重放、完整末行进重放,修前修后读数同);
|
|
359
|
+
* 目录已在而盘后来满 / 变只读 ⇒ boot 照常,append 走 `recordFailOpen`。
|
|
360
|
+
* @param internals.appendLog 测试注入席(见 {@link AuditAppendPort})。
|
|
361
|
+
* @param internals.warn 腐读披露座(缺席 ⇒ 重放属主退回 `console.warn`,不退回沉默)。
|
|
346
362
|
*/
|
|
347
363
|
constructor(root: string, internals?: {
|
|
348
|
-
|
|
364
|
+
appendLog?: (path: string) => AuditAppendPort;
|
|
365
|
+
warn?: Logger["warn"];
|
|
349
366
|
});
|
|
350
|
-
/**
|
|
351
|
-
* 这里不持 logger 席,与 journal 同姿态;坏行数暴露给收敛日志)。 */
|
|
367
|
+
/** 重放的坏值计数(`convergeCrashedAsks().corruptLines`):重放属主跳过的坏行(撕尾不计)+ 成因词表外的行(行在、结清照认,坏值照样计数)。 */
|
|
352
368
|
private corruptLines;
|
|
353
|
-
/**
|
|
354
|
-
* ⚠️ 判据是**换行缺席**不是「解析失败」:崩在「JSON 全落、换行未落」的字节窗时撕尾能 parse 成功,
|
|
355
|
-
* 但不修尾照样会与下一条记录并行。 */
|
|
356
|
-
private tornTail;
|
|
369
|
+
/** 重放前世账本(共用口 `replayJsonlSkipping`):撕尾 = 未落完的写,静默跳过;坏行跳过 + 逐条披露;坏行数另暴露给收敛日志。 */
|
|
357
370
|
private replay;
|
|
358
371
|
private group;
|
|
359
|
-
/** append + fsync(逐行落盘:进程猝死不丢已记的行 ——
|
|
372
|
+
/** append + fsync(逐行落盘:进程猝死不丢已记的行 —— 本店的全部价值就在崩溃时刻;写口 = 共用的 `AppendLog`:单次 `writeSync` 一行、末段在打开与每次追加前修(撕尾截、完整末行补换行)、失败的写截回开写处)。
|
|
360
373
|
* 运行期失败**响亮不阻断**:审批可用性不押在审计盘上(盘满/只读时人还得能批),但丢行必须在遥测
|
|
361
374
|
* 显形([ref] 三件套,census 登记行同批)。
|
|
362
375
|
* 🔴 返回**这条记录是否真的落盘并 fsync 成功**([ref] codex R1-F2 采):迟到结算的 late 判据只认
|
|
@@ -432,7 +445,7 @@ export declare class FileApprovalAskAuditStore implements ApprovalAskAuditSink,
|
|
|
432
445
|
* `owner === scope` 的行(null-owner 行对 principal 不可见,fail-closed 宁少列不超范围;`"__none__"`
|
|
433
446
|
* 未鉴权哨兵自然恒空)。 */
|
|
434
447
|
listCrashConverged(scope: string | undefined): CrashConvergedRow[];
|
|
435
|
-
/**
|
|
448
|
+
/** 收追加口(优雅关闭腿;SIGKILL 下不跑也不丢 —— 每行已 fsync)。core `AppendLog.close()` 自己吞 `closeSync` 的错、不抛(全函数),本店不再包 catch。 */
|
|
436
449
|
close(): void;
|
|
437
450
|
}
|
|
438
451
|
//# sourceMappingURL=approval-ask-audit-store.d.ts.map
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, writeSync } from "node:fs";
|
|
3
2
|
import { join } from "node:path";
|
|
3
|
+
import { AppendLog, ensureDir } from "@sema-agent/core";
|
|
4
4
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
5
|
+
import { replayJsonlSkipping } from "./observability/corrupt-read-seat.js";
|
|
5
6
|
export const APPROVAL_ASK_AUDIT_DIR = "approval-ask-audit";
|
|
6
7
|
export const CRASH_CONVERGED_LIST_MAX = 200;
|
|
7
8
|
export const AUDIT_SHUTDOWN_CAUSE_WORDS = { shutdown_before_park: true };
|
|
@@ -160,11 +161,20 @@ function readLedgerRecord(u, noteOutOfSetSeat) {
|
|
|
160
161
|
return undefined;
|
|
161
162
|
}
|
|
162
163
|
}
|
|
164
|
+
const LEDGER_SHAPE = {
|
|
165
|
+
what: "approval-ask audit record of an unknown kind or with a missing / out-of-vocabulary member",
|
|
166
|
+
read(record) {
|
|
167
|
+
let outOfSetCause = false;
|
|
168
|
+
const rec = readLedgerRecord(record, () => {
|
|
169
|
+
outOfSetCause = true;
|
|
170
|
+
});
|
|
171
|
+
return rec === undefined ? undefined : { rec, outOfSetCause };
|
|
172
|
+
},
|
|
173
|
+
};
|
|
163
174
|
export class FileApprovalAskAuditStore {
|
|
164
175
|
ledgerPath;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
writeBytes;
|
|
176
|
+
log;
|
|
177
|
+
openLog;
|
|
168
178
|
replayed = new Map();
|
|
169
179
|
replayedLegClosed = [];
|
|
170
180
|
replayedShutdownMarks = [];
|
|
@@ -177,44 +187,21 @@ export class FileApprovalAskAuditStore {
|
|
|
177
187
|
parkExpired = new Set();
|
|
178
188
|
askTaskId = new Map();
|
|
179
189
|
constructor(root, internals) {
|
|
180
|
-
this.
|
|
190
|
+
this.openLog = internals?.appendLog ?? ((p) => new AppendLog(p));
|
|
181
191
|
const dir = join(root, APPROVAL_ASK_AUDIT_DIR);
|
|
182
|
-
|
|
192
|
+
ensureDir(dir);
|
|
183
193
|
this.ledgerPath = join(dir, "ledger.jsonl");
|
|
184
|
-
this.replay();
|
|
185
|
-
if (this.tornTail) {
|
|
186
|
-
this.fd = openSync(this.ledgerPath, "a", 0o600);
|
|
187
|
-
writeSync(this.fd, "\n");
|
|
188
|
-
fsyncSync(this.fd);
|
|
189
|
-
}
|
|
194
|
+
this.replay(internals?.warn);
|
|
190
195
|
}
|
|
191
196
|
corruptLines = 0;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return;
|
|
196
|
-
const text = readFileSync(this.ledgerPath, "utf8");
|
|
197
|
-
this.tornTail = text.length > 0 && !text.endsWith("\n");
|
|
197
|
+
replay(warn) {
|
|
198
|
+
const { records, skipped } = replayJsonlSkipping(this.ledgerPath, "approval-ask-audit", LEDGER_SHAPE, warn);
|
|
199
|
+
this.corruptLines = skipped;
|
|
198
200
|
let line = 0;
|
|
199
|
-
for (const
|
|
200
|
-
if (raw.trim() === "")
|
|
201
|
-
continue;
|
|
201
|
+
for (const { rec, outOfSetCause } of records) {
|
|
202
202
|
line += 1;
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
parsed = JSON.parse(raw);
|
|
206
|
-
}
|
|
207
|
-
catch {
|
|
208
|
-
this.corruptLines += 1;
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
const rec = readLedgerRecord(parsed, () => {
|
|
203
|
+
if (outOfSetCause)
|
|
212
204
|
this.corruptLines += 1;
|
|
213
|
-
});
|
|
214
|
-
if (rec === undefined) {
|
|
215
|
-
this.corruptLines += 1;
|
|
216
|
-
continue;
|
|
217
|
-
}
|
|
218
205
|
switch (rec.kind) {
|
|
219
206
|
case "ask": {
|
|
220
207
|
const g = this.group(rec.approvalId, line);
|
|
@@ -265,25 +252,11 @@ export class FileApprovalAskAuditStore {
|
|
|
265
252
|
}
|
|
266
253
|
append(rec) {
|
|
267
254
|
try {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if (this.tailDirty) {
|
|
271
|
-
const nl = Buffer.from("\n", "utf8");
|
|
272
|
-
let noff = 0;
|
|
273
|
-
while (noff < nl.length)
|
|
274
|
-
noff += this.writeBytes(this.fd, nl, noff, nl.length - noff);
|
|
275
|
-
fsyncSync(this.fd);
|
|
276
|
-
this.tailDirty = false;
|
|
277
|
-
}
|
|
278
|
-
const buf = Buffer.from(`${JSON.stringify(rec)}\n`, "utf8");
|
|
279
|
-
let off = 0;
|
|
280
|
-
while (off < buf.length)
|
|
281
|
-
off += this.writeBytes(this.fd, buf, off, buf.length - off);
|
|
282
|
-
fsyncSync(this.fd);
|
|
255
|
+
this.log ??= this.openLog(this.ledgerPath);
|
|
256
|
+
this.log.append(rec, true);
|
|
283
257
|
return true;
|
|
284
258
|
}
|
|
285
259
|
catch (err) {
|
|
286
|
-
this.tailDirty = true;
|
|
287
260
|
recordFailOpen("server.approval-ask-audit.append-failed", `kind=${rec.kind} err=${err instanceof Error ? err.message : String(err)}`);
|
|
288
261
|
return false;
|
|
289
262
|
}
|
|
@@ -440,14 +413,8 @@ export class FileApprovalAskAuditStore {
|
|
|
440
413
|
return rows;
|
|
441
414
|
}
|
|
442
415
|
close() {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
closeSync(this.fd);
|
|
446
|
-
}
|
|
447
|
-
catch {
|
|
448
|
-
}
|
|
449
|
-
this.fd = undefined;
|
|
450
|
-
}
|
|
416
|
+
this.log?.close();
|
|
417
|
+
this.log = undefined;
|
|
451
418
|
}
|
|
452
419
|
}
|
|
453
420
|
//# sourceMappingURL=approval-ask-audit-store.js.map
|
|
@@ -15,6 +15,17 @@
|
|
|
15
15
|
*
|
|
16
16
|
* 🔴 ENV GATING: the live path requires E2B_API_KEY + DEEPSEEK_API_KEY; absent → the driver refuses the live path
|
|
17
17
|
* with a clear message (never a silent fake run). The deterministic --dry-run never needs keys.
|
|
18
|
+
*
|
|
19
|
+
* 🔴 LEDGER I/O (S-977): the WRITE side is the repo's shared append port — core `AppendLog` (lazy open, one `writeSync` loop per
|
|
20
|
+
* row, a failed write rolled back, a torn tail repaired on open AND before every append) — so a crash that tore the last
|
|
21
|
+
* row no longer glues the resume's first row onto the broken suffix (which lost that cell's row ⇒ one cell re-run at real
|
|
22
|
+
* E2B / DeepSeek cost). No fsync (a crash loses at most the tail row ⇒ that cell stays re-runnable — the same posture as before).
|
|
23
|
+
* The READ side ({@link readLedger}) stays PRIVATE — **registered exception, "bench CLI"**: this is a single-process offline
|
|
24
|
+
* experiment driver, not a serving store; its ledger path comes from `--ledger`, its readers are this driver and its tests, and a
|
|
25
|
+
* skipped (unparseable) ledger row costs one re-run of that cell — never a gate, never a tenant fact. The serving-layer replay owner
|
|
26
|
+
* (`replayJsonlSkipping`, `observability/corrupt-read-seat.ts`) is a closed registry of census-registered seats (each one a row in
|
|
27
|
+
* `docs/FAIL-OPEN-CENSUS.md` with an F tag in the serving telemetry); a bench seat there would register a non-serving face in the
|
|
28
|
+
* serving census. Direction note: a parseable row with an unknown arm tag still THROWS ({@link armUpper}) — loud, not skipped.
|
|
18
29
|
*/
|
|
19
30
|
import { Runner } from "@sema-agent/core";
|
|
20
31
|
import { type Arm } from "./arms.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { Runner, InMemoryCheckpointStore } from "@sema-agent/core";
|
|
3
|
+
import { AppendLog, Runner, InMemoryCheckpointStore } from "@sema-agent/core";
|
|
4
4
|
import { runArm } from "./arms.js";
|
|
5
5
|
import { FIRM_TRAPS, ALL_TRAPS } from "./tasks.js";
|
|
6
6
|
import { makeBenchClock } from "./runner-ctx.js";
|
|
@@ -71,9 +71,18 @@ function armUpper(tag) {
|
|
|
71
71
|
throw new Error(`s1 run-firm: unknown arm tag ${JSON.stringify(tag)} in ledger row (expected solo|sup|team)`);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
function createLedgerWriter(ledgerPath) {
|
|
75
|
+
let log;
|
|
76
|
+
return {
|
|
77
|
+
append(rec) {
|
|
78
|
+
log ??= new AppendLog(ledgerPath);
|
|
79
|
+
log.append(rec, false);
|
|
80
|
+
},
|
|
81
|
+
close() {
|
|
82
|
+
log?.close();
|
|
83
|
+
log = undefined;
|
|
84
|
+
},
|
|
85
|
+
};
|
|
77
86
|
}
|
|
78
87
|
function withTimeout(p, ms, label) {
|
|
79
88
|
return new Promise((resolve, reject) => {
|
|
@@ -121,35 +130,41 @@ export async function runFirm(opts) {
|
|
|
121
130
|
const { done } = await readLedger(opts.ledgerPath);
|
|
122
131
|
const ctx = { budget: opts.budget, clock: makeBenchClock(1_000_000), runId: opts.runId, gitSha: opts.gitSha };
|
|
123
132
|
let ranThisPass = 0;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
133
|
+
const ledger = createLedgerWriter(opts.ledgerPath);
|
|
134
|
+
try {
|
|
135
|
+
for (const cell of cells) {
|
|
136
|
+
const key = cellKeyStr(cell);
|
|
137
|
+
if (done.has(key)) {
|
|
138
|
+
log("cell_skip_done", { key });
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
log("cell_start", { key });
|
|
142
|
+
const cellId = `${opts.runId}-${cell.taskId}-${armTag(cell.arm)}-${String(cell.seed)}`;
|
|
143
|
+
let built;
|
|
144
|
+
let row;
|
|
145
|
+
try {
|
|
146
|
+
built = opts.mockDepsFactory
|
|
147
|
+
? opts.mockDepsFactory(cell.trap, cell.seed, cellId)
|
|
148
|
+
: buildLiveCellOrThrow(opts, cell.trap, cell.seed, cellId);
|
|
149
|
+
const supStore = cell.arm === "SUP" ? new InMemoryCheckpointStore() : undefined;
|
|
150
|
+
row = await withTimeout(runArm(cell.arm, cell.trap, cell.seed, ctx, built.deps, supStore), cellTimeoutMs, key);
|
|
151
|
+
}
|
|
152
|
+
catch (e) {
|
|
153
|
+
log("cell_infra_failed", { key, error: e instanceof Error ? e.message : String(e) });
|
|
154
|
+
row = infraFailedRow(cell, ctx);
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
await built?.dispose().catch(() => { });
|
|
158
|
+
}
|
|
159
|
+
ledger.append(row);
|
|
160
|
+
ranThisPass++;
|
|
161
|
+
if (row.runStatus !== "infra-failed")
|
|
162
|
+
done.add(key);
|
|
163
|
+
log("cell_done", { key, runStatus: row.runStatus });
|
|
147
164
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
done.add(key);
|
|
152
|
-
log("cell_done", { key, runStatus: row.runStatus });
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
ledger.close();
|
|
153
168
|
}
|
|
154
169
|
const { records } = await readLedger(opts.ledgerPath);
|
|
155
170
|
const lastByKey = new Map();
|
|
@@ -41,7 +41,7 @@ export function createLiveCoordinators(ctx) {
|
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
43
|
const approvalAskAudit = config.toolApprovalEnabled && backend?.kind === "local" && config.localDataRoot !== undefined
|
|
44
|
-
? new FileApprovalAskAuditStore(config.localDataRoot)
|
|
44
|
+
? new FileApprovalAskAuditStore(config.localDataRoot, { warn: (event, fields) => logger.warn(event, fields) })
|
|
45
45
|
: undefined;
|
|
46
46
|
if (approvalAskAudit) {
|
|
47
47
|
const converged = approvalAskAudit.convergeCrashedAsks();
|
|
@@ -12,6 +12,7 @@ import { SessionWatchRegistry } from "../session-watch.js";
|
|
|
12
12
|
import type { TaskListLane } from "./task-list-lane.js";
|
|
13
13
|
import { type RunnerDeps } from "@sema-agent/core";
|
|
14
14
|
import type { MailboxRecipientLifecycle } from "../plugins/mailbox-recipient-lifecycle.js";
|
|
15
|
+
import type { UserInputLedgerStore } from "../user-input-ledger.js";
|
|
15
16
|
export interface SessionFacesCtx {
|
|
16
17
|
config: ServiceConfig;
|
|
17
18
|
logger: Logger;
|
|
@@ -43,6 +44,9 @@ export interface SessionFacesCtx {
|
|
|
43
44
|
/** S-790 件B:后台子代的耐久店(与 `RunnerDeps.backgroundAgentStore` 同实例)—— 删除协调器据它枚举以本会话为根的子代并结束
|
|
44
45
|
* 它们(E21 级联的一条腿,做完才删元数据)。缺席 = 本部署没有耐久后台子代(纯内存 dev);那一形只剩本进程登记表里的活行,照样停。 */
|
|
45
46
|
backgroundAgentStore: RunnerDeps["backgroundAgentStore"];
|
|
47
|
+
/** S-965:用户输入受理账本的**店**(E21 级联的一条腿:该会话的受理行随会话删)。与受理 / 收敛 / 告知读的是同一只(stage-07 取一次)。
|
|
48
|
+
* 缺席 = 本部署没装账本(env-only:没有 backend / run 店)⇒ 无行可删。 */
|
|
49
|
+
userInputLedgerStore: UserInputLedgerStore | undefined;
|
|
46
50
|
}
|
|
47
51
|
export declare function createSessionFaces(ctx: SessionFacesCtx): {
|
|
48
52
|
ownerAware: OwnerAwareSessionStore;
|
|
@@ -8,7 +8,7 @@ import { defaultTaskRegistry, peerSessionBoxHandle } from "@sema-agent/core";
|
|
|
8
8
|
import { reapSessionBackgroundHard } from "../background-session-reap.js";
|
|
9
9
|
import { probeSessionChildren, retireSessionChildren, SessionChildrenStopIncompleteError } from "../session-children-retire.js";
|
|
10
10
|
export function createSessionFaces(ctx) {
|
|
11
|
-
const { config, logger, metrics, localRoot, backend, sessionStore, runStore, checkpointStore, toolResultStore, resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryErasure, workflowCompletionInbox, taskListLane, deviceStore, dropSessionScoped, mailboxStore, mailboxRecipientLifecycle, backgroundAgentStore, } = ctx;
|
|
11
|
+
const { config, logger, metrics, localRoot, backend, sessionStore, runStore, checkpointStore, toolResultStore, resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryErasure, workflowCompletionInbox, taskListLane, deviceStore, dropSessionScoped, mailboxStore, mailboxRecipientLifecycle, backgroundAgentStore, userInputLedgerStore, } = ctx;
|
|
12
12
|
const mysqlPool = backend?.mysqlPool();
|
|
13
13
|
const auditPgPool = backend?.pgPool();
|
|
14
14
|
const auditLocalRoot = backend?.kind === "local" ? (config.localDataRoot ?? localRoot) : undefined;
|
|
@@ -78,6 +78,8 @@ export function createSessionFaces(ctx) {
|
|
|
78
78
|
await sessionPolicyStore.deleteBySession(sessionId);
|
|
79
79
|
if (taskAttachmentStore)
|
|
80
80
|
await taskAttachmentStore.deleteBySession(sessionId);
|
|
81
|
+
if (userInputLedgerStore)
|
|
82
|
+
await userInputLedgerStore.deleteBySession(sessionId);
|
|
81
83
|
if (taskListLane)
|
|
82
84
|
await taskListLane.deleteBySession(sessionId);
|
|
83
85
|
if (deviceStore)
|
|
@@ -24,6 +24,7 @@ export declare function runStage07(ctx: Awaited<ReturnType<typeof runStage06>>):
|
|
|
24
24
|
};
|
|
25
25
|
taskTimeoutSec: number;
|
|
26
26
|
userInputLedger: import("../user-input-ledger.js").UserInputLedger | undefined;
|
|
27
|
+
userInputLedgerStore: import("../user-input-ledger.js").UserInputLedgerStore | undefined;
|
|
27
28
|
webSearchProvider: "brave" | "tavily" | "searxng" | undefined;
|
|
28
29
|
approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
|
|
29
30
|
centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
|
|
@@ -51,10 +51,11 @@ export async function runStage07(ctx) {
|
|
|
51
51
|
if (gateRefusal !== undefined)
|
|
52
52
|
throw new Error(gateRefusal);
|
|
53
53
|
await reclaimOrphansAtBoot(runStore, checkpointStore, logger);
|
|
54
|
-
const
|
|
54
|
+
const userInputLedgerStore = backend && runStore ? backend.userInputLedger() : undefined;
|
|
55
|
+
const userInputLedger = backend && runStore && userInputLedgerStore
|
|
55
56
|
? await (async () => {
|
|
56
57
|
const { ledger, converged } = await createUserInputLedgerAtBoot({
|
|
57
|
-
store:
|
|
58
|
+
store: userInputLedgerStore,
|
|
58
59
|
writer: instanceId,
|
|
59
60
|
evidence: writerGoneEvidenceFor(backend.kind, runStore),
|
|
60
61
|
newId: uuidv7,
|
|
@@ -170,6 +171,6 @@ export async function runStage07(ctx) {
|
|
|
170
171
|
const builtinScenarioNames = Object.keys(scenarioDetails);
|
|
171
172
|
configCenter.applyCenterScenarios({ scenarioDeps, scenarios, scenarioDetails, builtinScenarioNames });
|
|
172
173
|
logger.info("capabilities", { skills: skills.length, scenarios: Object.keys(scenarios), repoTools: Boolean(repoClient) });
|
|
173
|
-
return { ...ctx, approvalExemptionStore, approvalNonceStore, authorize, imageBakes, imageIndex, parkedReviveInheritedGate, parkedReviveSurface, rateLimiter, resumeAnchorStore, runStore, scenarioDetails, scenarios, selectEnvTool, taskLimitCaps, taskTimeoutSec, userInputLedger, webSearchProvider: webSearchCfg?.provider };
|
|
174
|
+
return { ...ctx, approvalExemptionStore, approvalNonceStore, authorize, imageBakes, imageIndex, parkedReviveInheritedGate, parkedReviveSurface, rateLimiter, resumeAnchorStore, runStore, scenarioDetails, scenarios, selectEnvTool, taskLimitCaps, taskTimeoutSec, userInputLedger, userInputLedgerStore, webSearchProvider: webSearchCfg?.provider };
|
|
174
175
|
}
|
|
175
176
|
//# sourceMappingURL=stage-07-capability-layer.js.map
|
|
@@ -37,6 +37,7 @@ export declare function runStage08(ctx: Awaited<ReturnType<typeof runStage07>>):
|
|
|
37
37
|
};
|
|
38
38
|
taskTimeoutSec: number;
|
|
39
39
|
userInputLedger: import("../user-input-ledger.js").UserInputLedger | undefined;
|
|
40
|
+
userInputLedgerStore: import("../user-input-ledger.js").UserInputLedgerStore | undefined;
|
|
40
41
|
webSearchProvider: "brave" | "tavily" | "searxng" | undefined;
|
|
41
42
|
approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
|
|
42
43
|
centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
|
|
@@ -73,6 +73,7 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
|
|
|
73
73
|
};
|
|
74
74
|
taskTimeoutSec: number;
|
|
75
75
|
userInputLedger: import("../user-input-ledger.js").UserInputLedger | undefined;
|
|
76
|
+
userInputLedgerStore: import("../user-input-ledger.js").UserInputLedgerStore | undefined;
|
|
76
77
|
webSearchProvider: "brave" | "tavily" | "searxng" | undefined;
|
|
77
78
|
approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
|
|
78
79
|
centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
|
|
@@ -6,7 +6,7 @@ import { createLeaderFace } from "./leader.js";
|
|
|
6
6
|
import { assertStaticWiringConsistent } from "../http/routes/diagnostics.js";
|
|
7
7
|
import { buildDisplaySafeUrl } from "../trace/redact.js";
|
|
8
8
|
export function runStage09(ctx) {
|
|
9
|
-
const { approvalExemptionStore, backend, backgroundAgentStore, brain, checkpointStore, config, configCenter, deviceLane, dropSessionScoped, durableEnabled, executionEnvFactory, fileHistoryErasure, governanceSeams, localRoot, logger, mailboxRecipientLifecycle, mailboxStore, metrics, pricing, resumeAnchorStore, runStore, runnerDeps, sessionPolicyStore, sessionStore, streamApprovalGate, taskAttachmentStore, taskListLane, toolResultStore, tracer, usageWindowStore, workflowCompletionInbox } = ctx;
|
|
9
|
+
const { approvalExemptionStore, backend, backgroundAgentStore, brain, checkpointStore, config, configCenter, deviceLane, dropSessionScoped, durableEnabled, executionEnvFactory, fileHistoryErasure, governanceSeams, localRoot, logger, mailboxRecipientLifecycle, mailboxStore, metrics, pricing, resumeAnchorStore, runStore, runnerDeps, sessionPolicyStore, sessionStore, streamApprovalGate, taskAttachmentStore, taskListLane, toolResultStore, tracer, usageWindowStore, userInputLedgerStore, workflowCompletionInbox } = ctx;
|
|
10
10
|
const { ownerAware, sessionAudit, sessionWatchRegistry, purgeSession, instrumentDegenerate, planCacheProbe } = createSessionFaces({
|
|
11
11
|
deviceStore: deviceLane?.store,
|
|
12
12
|
dropSessionScoped,
|
|
@@ -14,6 +14,7 @@ export function runStage09(ctx) {
|
|
|
14
14
|
resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryErasure,
|
|
15
15
|
workflowCompletionInbox, taskListLane, mailboxStore, mailboxRecipientLifecycle,
|
|
16
16
|
backgroundAgentStore,
|
|
17
|
+
userInputLedgerStore,
|
|
17
18
|
});
|
|
18
19
|
const leaderEndpoint = createLeaderFace({ config, logger, brain, pricing, executionEnvFactory, toolResultStore, sessionStore, checkpointStore, governanceSeams, backend, tracer, usageWindowStore, promptSource: configCenter.promptSource, onError: runnerDeps.onError });
|
|
19
20
|
const drainState = { draining: false };
|
|
@@ -73,6 +73,7 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
|
|
|
73
73
|
};
|
|
74
74
|
taskTimeoutSec: number;
|
|
75
75
|
userInputLedger: import("../user-input-ledger.js").UserInputLedger | undefined;
|
|
76
|
+
userInputLedgerStore: import("../user-input-ledger.js").UserInputLedgerStore | undefined;
|
|
76
77
|
webSearchProvider: "brave" | "tavily" | "searxng" | undefined;
|
|
77
78
|
approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
|
|
78
79
|
centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
|
|
@@ -20,7 +20,7 @@ export function createWorkflowOrchestration(ctx) {
|
|
|
20
20
|
}))
|
|
21
21
|
: undefined;
|
|
22
22
|
const workflowNotifyJournal = config.selfOrchestrationEnabled && config.workflowRunStoreBackend !== "memory"
|
|
23
|
-
? (sqlWorkflowRunStore ? backend.notifyJournal() : new FileWorkflowNotifyJournalStore(join(config.localDataRoot ?? localRoot, "workflows")))
|
|
23
|
+
? (sqlWorkflowRunStore ? backend.notifyJournal() : new FileWorkflowNotifyJournalStore(join(config.localDataRoot ?? localRoot, "workflows"), undefined, (msg, meta) => logger.warn(msg, meta)))
|
|
24
24
|
: undefined;
|
|
25
25
|
const workflowCompletionInbox = config.selfOrchestrationEnabled
|
|
26
26
|
? config.workflowRunStoreBackend === "memory"
|
package/dist/http/resume-legs.js
CHANGED
|
@@ -20,6 +20,7 @@ import { buildCheckpointAnchorHash } from "../approval-ask-audit-store.js";
|
|
|
20
20
|
import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
|
|
21
21
|
import { withPrincipal } from "../observability/principal-context.js";
|
|
22
22
|
import { withModelReachScope } from "../model-reach-scope.js";
|
|
23
|
+
import { createInstructionSelectionLeg } from "../instruction-selection.js";
|
|
23
24
|
import { modelTextEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
|
|
24
25
|
import { redactSecrets } from "../trace/redact.js";
|
|
25
26
|
import { createLedgerSink, createSerialLedgerAppend } from "../trace/ledger-sink.js";
|
|
@@ -503,7 +504,8 @@ export function createResumeLegs(ctx) {
|
|
|
503
504
|
await args.onResumeCommitted();
|
|
504
505
|
let unregisterEngineNotice;
|
|
505
506
|
try {
|
|
506
|
-
|
|
507
|
+
const instructionLeg = createInstructionSelectionLeg();
|
|
508
|
+
result = await instructionLeg.run(sessionId, () => withModelReachScope(sessionId, () => withPrincipal(principal, async () => {
|
|
507
509
|
const resumeNotifiedKeys = new NotifiedKeys();
|
|
508
510
|
if (deps.runStore && taskId) {
|
|
509
511
|
const rs0 = deps.runStore;
|
|
@@ -526,6 +528,7 @@ export function createResumeLegs(ctx) {
|
|
|
526
528
|
persistThinking: deps.config.traceThinking,
|
|
527
529
|
notifiedKeys: resumeNotifiedKeys,
|
|
528
530
|
...(taskId ? { onActivity: () => recordTurnActivity(taskId) } : {}),
|
|
531
|
+
instructionsSegment: instructionLeg.segment,
|
|
529
532
|
});
|
|
530
533
|
const vAppend = (type, data) => verifySink.append(type, data);
|
|
531
534
|
const verifyTailContentMode = resumeTaskConfig.forwardSubagentEvents === true ? "on" : "progress_only";
|
|
@@ -771,7 +774,7 @@ export function createResumeLegs(ctx) {
|
|
|
771
774
|
break;
|
|
772
775
|
case "wiring_manifest":
|
|
773
776
|
await flush();
|
|
774
|
-
await append("wiring_manifest", wiringManifestEventData(ev));
|
|
777
|
+
await append("wiring_manifest", wiringManifestEventData(ev, instructionLeg.segment()));
|
|
775
778
|
break;
|
|
776
779
|
case "tool_roster_delta": {
|
|
777
780
|
await flush();
|
|
@@ -875,7 +878,7 @@ export function createResumeLegs(ctx) {
|
|
|
875
878
|
return await (deps.sendUserFile && taskId
|
|
876
879
|
? deps.sendUserFile.runWithContext({ taskId, emit: async (frame) => { const { type, ...rest } = frame; await append(type, rest); } }, withElicitation)
|
|
877
880
|
: withElicitation());
|
|
878
|
-
}));
|
|
881
|
+
})));
|
|
879
882
|
}
|
|
880
883
|
catch (e) {
|
|
881
884
|
if (e instanceof CheckpointError) {
|
package/dist/http/routes/runs.js
CHANGED
|
@@ -1155,6 +1155,7 @@ export async function handleRunVerbs(req, res, match, ctx) {
|
|
|
1155
1155
|
}
|
|
1156
1156
|
const handle = resolution?.handle;
|
|
1157
1157
|
if (handle) {
|
|
1158
|
+
let acceptance;
|
|
1158
1159
|
try {
|
|
1159
1160
|
if (verb === "resume") {
|
|
1160
1161
|
if (!handle.resume) {
|
|
@@ -1170,11 +1171,23 @@ export async function handleRunVerbs(req, res, match, ctx) {
|
|
|
1170
1171
|
sendJson(res, 200, { taskId: runId, target, status: "running", delivery: "applied", marker, note: `Message queued for delivery to ${handle.agentName ?? target}; it will continue in the background.` });
|
|
1171
1172
|
return;
|
|
1172
1173
|
}
|
|
1174
|
+
const messageId = uuidv7();
|
|
1175
|
+
const accepted = await acceptLiveUserInput(deps, res, { messageId, sessionId: run.sessionId, taskId: runId, leg: handle });
|
|
1176
|
+
if (accepted === "refused")
|
|
1177
|
+
return;
|
|
1178
|
+
acceptance = accepted;
|
|
1173
1179
|
const marker = await handle.steer(redacted);
|
|
1174
|
-
|
|
1180
|
+
if (acceptance !== undefined) {
|
|
1181
|
+
const ledger = deps.userInputLedger;
|
|
1182
|
+
const settle = () => ledger.settleLeg(handle);
|
|
1183
|
+
void handle.settled.then(settle, settle);
|
|
1184
|
+
}
|
|
1185
|
+
sendJson(res, 200, { taskId: runId, target, status: "running", delivery: "applied", messageId, marker, note: `Message queued for delivery to ${handle.agentName ?? target} at its next tool round.` });
|
|
1175
1186
|
return;
|
|
1176
1187
|
}
|
|
1177
1188
|
catch (e) {
|
|
1189
|
+
if (acceptance !== undefined)
|
|
1190
|
+
await deps.userInputLedger.withdraw(acceptance);
|
|
1178
1191
|
const code = e.code;
|
|
1179
1192
|
if (code === "steering.not_running") {
|
|
1180
1193
|
send409(code, "sub-agent just finished — no longer accepting steers");
|