@sema-agent/server 7.143.0 → 7.145.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 +295 -0
- package/README.md +3 -0
- package/README.zh-CN.md +1 -0
- package/USAGE.md +34 -16
- package/dist/approval-card.d.ts +31 -14
- package/dist/approval-card.js +8 -8
- package/dist/audit.d.ts +2 -21
- package/dist/audit.js +0 -9
- package/dist/boot/execution-env.js +1 -1
- package/dist/boot/runner-deps.d.ts +10 -5
- package/dist/boot/runner-deps.js +5 -4
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +3 -3
- package/dist/boot/shutdown.d.ts +1 -1
- package/dist/boot/shutdown.js +4 -2
- package/dist/boot/stage-01-config.js +2 -0
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-09-leader.d.ts +1 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -1
- package/dist/boot/stage-10-http-server.js +4 -9
- package/dist/capabilities/scenarios.d.ts +14 -18
- package/dist/capabilities/scenarios.js +1 -2
- package/dist/config-d-migration.d.ts +3 -0
- package/dist/delegated-session-owner.d.ts +33 -151
- package/dist/delegated-session-owner.js +1 -83
- package/dist/entry-mode-clamp-notice.d.ts +1 -1
- package/dist/execution-placement.d.ts +7 -4
- package/dist/fleet/fleet-bus.d.ts +13 -3
- package/dist/fleet/fleet-bus.js +17 -3
- package/dist/hooks/classic-stations.d.ts +3 -1
- package/dist/hooks/classic-stations.js +2 -0
- package/dist/http/admission.js +2 -1
- package/dist/http/checkpoint-ctx.d.ts +1 -0
- package/dist/http/checkpoint-ctx.js +1 -0
- package/dist/http/durable-run-leg.js +7 -6
- package/dist/http/principal-gate.d.ts +1 -1
- package/dist/http/refusal-body.d.ts +7 -3
- package/dist/http/refusal-body.js +1 -0
- package/dist/http/resume-legs.js +16 -7
- package/dist/http/route-ctx.d.ts +15 -0
- package/dist/http/routes/approvals-assistant.d.ts +20 -1
- package/dist/http/routes/approvals-assistant.js +28 -6
- package/dist/http/routes/rules.d.ts +1 -1
- package/dist/http/routes/rules.js +5 -2
- package/dist/http/routes/session-sync.js +12 -0
- package/dist/http/routes/tasks.js +12 -9
- package/dist/http/run-meta.d.ts +14 -7
- package/dist/http/run-meta.js +5 -2
- package/dist/http/server.d.ts +13 -5
- package/dist/http/server.js +3 -0
- package/dist/http/submit-ctx.d.ts +12 -0
- package/dist/http/submit-ctx.js +23 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lane-home-probe.d.ts +15 -0
- package/dist/lane-home-probe.js +4 -0
- package/dist/observability/corrupt-read-seat.d.ts +5 -9
- package/dist/observability/corrupt-read-seat.js +1 -2
- package/dist/observability/fail-open.d.ts +26 -10
- package/dist/observability/fail-open.js +26 -10
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/observability/run-terminal.js +2 -2
- package/dist/parked-revive-surface.d.ts +6 -7
- package/dist/peer-inbound-face.d.ts +29 -34
- package/dist/peer-inbound-face.js +28 -63
- package/dist/plugins/approval-ask-store-file.d.ts +16 -2
- package/dist/plugins/approval-ask-store-file.js +21 -1
- package/dist/plugins/approval-ask-store-memory.d.ts +8 -2
- package/dist/plugins/approval-ask-store-memory.js +11 -8
- package/dist/plugins/caching-session-store.d.ts +3 -3
- package/dist/plugins/caching-session-store.js +3 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +62 -37
- package/dist/plugins/checkpoint-store-sql.js +123 -40
- package/dist/plugins/file-run-store.d.ts +13 -1
- package/dist/plugins/file-run-store.js +11 -1
- package/dist/plugins/fork-routing-session-store.d.ts +8 -1
- package/dist/plugins/fork-routing-session-store.js +6 -0
- package/dist/plugins/local-checkpoint-store.d.ts +17 -8
- package/dist/plugins/local-checkpoint-store.js +26 -7
- package/dist/plugins/local-session-store.d.ts +105 -34
- package/dist/plugins/local-session-store.js +168 -65
- package/dist/plugins/local-session-window.d.ts +4 -2
- package/dist/plugins/memory-run-store.d.ts +2 -0
- package/dist/plugins/memory-run-store.js +6 -0
- package/dist/plugins/one-time-migrations.d.ts +12 -12
- package/dist/plugins/one-time-migrations.js +13 -13
- package/dist/plugins/pg-session-storage.d.ts +1 -9
- package/dist/plugins/pg-session-storage.js +2 -19
- package/dist/plugins/remote-env-host.d.ts +8 -4
- package/dist/plugins/remote-env-host.js +7 -9
- package/dist/plugins/remote-shell.js +12 -2
- package/dist/plugins/run-store-sql.d.ts +4 -0
- package/dist/plugins/run-store-sql.js +3 -0
- package/dist/plugins/schema-consistency.d.ts +5 -1
- package/dist/plugins/session-fork.d.ts +1 -1
- package/dist/plugins/session-ownership.d.ts +9 -0
- package/dist/plugins/session-ownership.js +6 -0
- package/dist/plugins/session-store.d.ts +13 -16
- package/dist/plugins/session-store.js +9 -10
- package/dist/plugins/sql-json-column.d.ts +24 -2
- package/dist/plugins/sql-json-column.js +38 -3
- package/dist/plugins/store-backend.js +3 -2
- package/dist/plugins/tidb-session-store.d.ts +2 -14
- package/dist/plugins/tidb-session-store.js +2 -20
- package/dist/prompt-screen.d.ts +21 -0
- package/dist/prompt-screen.js +8 -0
- package/dist/rules-consent.d.ts +63 -51
- package/dist/rules-consent.js +6 -10
- package/dist/run-acceptance.d.ts +40 -10
- package/dist/run-acceptance.js +38 -5
- package/dist/run-local.js +2 -1
- package/dist/runs.js +5 -1
- package/dist/security.d.ts +10 -7
- package/dist/session-agent.d.ts +39 -0
- package/dist/session-agent.js +16 -0
- package/dist/session-error-wire.d.ts +25 -26
- package/dist/session-error-wire.js +4 -5
- package/dist/session-incarnation.d.ts +2 -2
- package/dist/session-notify-door.d.ts +2 -2
- package/dist/session-notify-door.js +5 -1
- package/dist/tool-approval.d.ts +3 -1
- package/dist/tool-approval.js +47 -18
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +1 -1
- package/dist/trace/ledger-sink.js +12 -4
- package/dist/trace/model-segment.d.ts +26 -6
- package/dist/trace/model-segment.js +58 -9
- package/dist/trace/project.d.ts +8 -2
- package/dist/trace/project.js +6 -4
- package/dist/trace/redact.d.ts +77 -2
- package/dist/trace/redact.js +234 -99
- package/dist/trace/self-minted-wire.d.ts +160 -9
- package/dist/trace/self-minted-wire.js +52 -3
- package/package.json +3 -3
- package/wire-changes/server@7.144.0.json +291 -0
- package/wire-changes/server@7.145.0.json +146 -0
- package/dist/session-records-skipped-notice.d.ts +0 -21
- package/dist/session-records-skipped-notice.js +0 -27
package/USAGE.md
CHANGED
|
@@ -173,6 +173,11 @@ ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION /
|
|
|
173
173
|
引擎注入的 `<system-reminder>` / `<task-notification>` / `<attachment>` 块,以及壳铸进用户轮的**本地命令回显**(`<command-name>` / `<command-message>` / `<command-args>`、`<local-command-stdout>` / `<local-command-stderr>` / `<local-command-caveat>`、
|
|
174
174
|
`<bash-input>` / `<bash-stdout>` / `<bash-stderr>` / `<bash-exit-code>`、`<user-memory-input>`)都不算你写的:`/compact` 一类本地命令之后的下一条提交,预览与标题取的是随后那句话,不再是回显;只有回显、没有你的话 ⇒ run / leader 行的预览是空串(任务名折成 `(notification turn)`),会话列表的预览回落到更早的一句、都没有则缺席(`null`)。
|
|
175
175
|
代价:正文里真的写了这些开标签字面的那一段在预览 / 标题里被剥(只是**提到**这些词的原样保留),原文在会话历史读面完整可见。
|
|
176
|
+
- **装了 prompt 屏的部署,预览与标题只取引擎原样交付的目标**(7.145.0+):部署座的 mod 包或请求的 `settings.hooks` 声明了 `prompt.submit` / `classic.UserPromptSubmit` 的腿,建 run 行时不写预览、续跑输入里的目标留空、fleet 行不带名;
|
|
177
|
+
目标的受理帧原样送达(不带 `rewrittenBy`)⇒ 那一拍补上预览、fleet 行名并起标题;屏改写过 / 丢弃 / 抛错 ⇒ 这些面一直缺席,标题不自动起 —— 屏前文本不落进 run 行、续跑输入,也不送进标题模型。
|
|
178
|
+
续跑输入里的目标在这类腿上一直留空,所以 wake 新建的 run 行没有预览、续跑腿的 fleet 行不带名(原样交下也一样)。订 `prompt.*` / `classic.*` 通配的只观察型运维 mod 也被算成屏(判据读声明、与引擎的通配语义一致),这类部署上只回结果的腿(`verify` / `cascade`)与 wake / 续跑腿同样缺席,其余腿在受理那一拍补上。
|
|
179
|
+
leader 车道 durable worker 的子目标续跑输入不在这条律内:它不经受理座,屏前原文照旧落盘(另票)。
|
|
180
|
+
`verify` 形提交的续跑输入照旧存请求原文(停驻续跑要把它交给验证器;不进 wake 新行的预览与 fleet 行名);目标里的内联 `@名` 选中的模型(请求没带 `model` 时)记在续跑输入里,续跑照旧跑它;只回结果的 `cascade` 提交有屏时预览与标题缺席。嵌入方:`createHttpServer` 的 `deploymentMods` 必填,传交给 Runner 的同一只 `mods` 数组(没有传 `[]`)。
|
|
176
181
|
- **缺省推断**:`MODEL_PROVIDER` **未设**、但显式配了 Anthropic 协议 base URL(`ANTHROPIC_BASE_URL`)**和**对应凭证(`ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`)时,缺省自动判 `anthropic` 并打一行启动 warn(`model_provider_inferred`)——纯净机直连 Anthropic 兼容上游不再需要显式第七个键。显式 `MODEL_PROVIDER` 恒赢;只给 base URL 或只给凭证不推断;两者皆无 = `gateway`,与以前逐字节一致。
|
|
177
182
|
- 启动日志的 `brain` 字段会回显当前组合(`failover` / `anthropicRoute` / `anthropicCacheBreakpoints`)。
|
|
178
183
|
- **在服务进程之外读同一套 env 路由**(子路径 `@sema-agent/server/model-route-env`;纯数据 + 纯函数,import 无副作用,值依赖只有 `@sema-agent/core` 与 `@sema-agent/settings-schema`):
|
|
@@ -624,8 +629,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
624
629
|
下一轮不必从店重新唤醒整只会话。每次命中先读店里这只会话的当前叶(SQL 后端一条主键 SELECT):与暖份的叶相同 ⇒ 照用;
|
|
625
630
|
不同(这只会话在别的副本上写过)或会话已不在 ⇒ 丢掉暖份、从店重读再用。所以 chart 缺省 Service、轮询负载均衡这类**任意路由**
|
|
626
631
|
下,同一会话轮流落到不同副本不会撞 `conflict`;会话亲和只提高命中率(少重读),不是正确性前提。代价:每次命中多一次读头
|
|
627
|
-
—— SQL 后端一条主键 SELECT;文件后端(`DB_BACKEND=local`)是把这只会话的存档文件再读一遍(
|
|
628
|
-
帧)。命中却已陈旧的次数计在 `session_cache_stale_hit_total`(高 = 同一会话在 TTL 内常换副本,加亲和可省掉这些重读)。
|
|
632
|
+
—— SQL 后端一条主键 SELECT;文件后端(`DB_BACKEND=local`)是把这只会话的存档文件再读一遍(只读头,不回放日志)。命中却已陈旧的次数计在 `session_cache_stale_hit_total`(高 = 同一会话在 TTL 内常换副本,加亲和可省掉这些重读)。
|
|
629
633
|
`0` = 每一轮都从店唤醒,不复用。⚠️ 核的是叶:另一只副本用会话同步**整段替换**(含 `overwrite-dst`)把已有条目的内容改了、
|
|
630
634
|
末叶却没变的那一形,别的副本上的暖份在 TTL 内仍是旧内容(与改前同;写不会因此失败)。依赖这一形即时可见的部署设 `0`。
|
|
631
635
|
- 数据驻留提示:`DB_BACKEND=local` 下显式 `SESSION_BACKEND=memory` 会被收编为 **durable(local)**
|
|
@@ -653,7 +657,10 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
653
657
|
行与编号高水位一并抹掉(同 anchors/附件/快照的级联)——会话 id 可被重新认领,残留就等于把上一位
|
|
654
658
|
主人的任务标题与编号进度交给下一个化身。
|
|
655
659
|
- **会话的行事实只住在数据根的 `session-incarnations.json`**(每只会话一格:属主 / 化身 / 执行放置 / 建档时刻 / fork 谱系);`sessions/*.jsonl` 是匿名的对话内容。
|
|
656
|
-
备份 / 迁数据根时两样一起搬;更早版本写下的带属主戳的存档由起服一次性迁移改写(改不了的点名,fork 与改日志推送答 409),快照目录 `session-archive-m33/` 非空且消化不了 ⇒ 拒启(MIGRATION 写了出路)。
|
|
660
|
+
备份 / 迁数据根时两样一起搬;更早版本写下的带属主戳的存档由起服一次性迁移改写(改不了的点名,fork 与改日志推送答 409 `conflict.session_archive_stamped`;会话同步导入的第一阶段对 fork / stale 且没带同意的推送也直接答它),快照目录 `session-archive-m33/` 非空且消化不了 ⇒ 拒启(MIGRATION 写了出路)。
|
|
661
|
+
`session-incarnations.json` 本构建读不出(更新的引擎写过、格里多出不认得的键;或文件坏了 / 读不了)⇒ 起服拒 `config.schema_mismatch`(与 SQL 后端列册对账同一个码,拒句点名文件与键;
|
|
662
|
+
出路 = 部署写下它的那一版,或恢复升级前的数据根备份;读不了 ⇒ 修好权限 / 盘再起);起服之后才读不出(跳过了 `ensureSchema()` 的嵌入方;或违规共享的数据根上另一只引擎写了它)⇒ 同码拒,
|
|
663
|
+
形读不出则那只边档的每一读每一写拒到重启,盘上字节不被盖掉。
|
|
657
664
|
- 多实例边界:同一数据根同时只允许一个实例(BootLock 独占,第二个进程拒启;死主自愈含 SIGKILL)。
|
|
658
665
|
core 5.54.0 起拒启错误带**可分派 code**(`FileStoreLockError`,message 点名占用方 pid),运维按码
|
|
659
666
|
三动作:`store.dir_in_use`=另一个活进程持有该目录 → 停掉它(或换数据根/迁 SQL 后端);
|
|
@@ -769,9 +776,12 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
769
776
|
- **服务进程被 `SIGKILL` 时,暂存目录会留在目标旁(可直接删)**:上一条的清理是服务进程自己做的 —— `kill -9`、内存杀手、容器被强停这类**没有机会跑清理**的退出,正在传输的那一次写留下 `.<名>.stage-<12 位十六进制>/`,里面是在传的新内容(上传途中被杀只有前半截,传完才被杀则是整份);**目标逐字是旧内容**,整文件替换的保证不受影响,多出来的只是这只目录。ssh 车道实测(被强杀的写都留下了);其余四条远端车道走同一段暂存脚本,同形。取消(Ctrl-C 中断那一次写)不留 —— 取消路径上清理那一往返照跑。**本版不自动清**:下一次对同一目标的写不会去扫陈旧的暂存目录,它会一直留着,确认没有另一个进程正在写同一个目标之后 `rm -rf` 即可(名字里的十六进制每次写都不同,在写的那一只和陈旧的那一只光看名字分不出)。host 车道的暂存是引擎的,同形的残留名是 `<目标>.tmp.<进程号>.<十六进制>`,同样可以删。
|
|
770
777
|
- 暂存自创建起不比目标更可读:远端五车道的暂存只在私有目录里(别的用户进不去;替换 0600 的文件时新内容在传输期间只有写入方与 root 读得到),device 的暂存件以 0600 打开;
|
|
771
778
|
host 车道的这一保证在引擎原语里:引擎 7.60.0 起目标存在时暂存件以 0600 打开。新建的文件是写入方身份的 umask 形(local-docker = 容器内镜像用户的)。
|
|
772
|
-
私有目录建出后要读回核实是 0700
|
|
779
|
+
私有目录建出后要读回核实是 0700 且属写入方、且目录上没有 ACL 条目:macOS 的原生 ACL 与 FreeBSD / illumos ZFS 的 NFSv4 ACL 不经模式位 —— 工作区父目录带可继承的允许条目时,新建的私有目录一建出来就带着它,读回 `700` 也挡不住,
|
|
780
|
+
所以读到条目就去掉(macOS `chmod -N`、FreeBSD `setfacl -b`、illumos `chmod A-`)、去完再读;**核不过(`chmod` 被拒、文件系统不认权限位、缺省 ACL 的掩码收不下来、继承来的 ACL 条目去不掉或读不出、目标机的系统不在 Linux / macOS / FreeBSD / illumos 之内、目标机没有 `stat`)⇒ 不暂存,退回就地写目标**
|
|
773
781
|
(两只文件面、五条腿都有;非原子;ssh / local-docker / k8s 写进原 inode、权限位不变,e2b 的 `files.write` 与 adb 的 `adb push` 整份写目标、inode / 权限位由对端定;
|
|
774
|
-
遥测计 `server.remote-write.stage-seal-fallback`)
|
|
782
|
+
遥测计 `server.remote-write.stage-seal-fallback`);读 ACL 的臂按**目标机**的 `uname -s` 选(Linux:POSIX ACL 由模式位读回的掩码覆盖,不另读 · Darwin · FreeBSD · SunOS);FreeBSD / illumos 两臂按系统文档写、未上真机,读 ACL 的工具报错 / 输出形不对 / `uname` 退出状态不成都按读不出处理(读 ACL 的工具自己静默吞掉的读取错误看不出);
|
|
783
|
+
Linux 上经 NFS 挂载、由服务端自己执行的 NFSv4 ACL 在客户端没有可读的条目,不在覆盖内;其它系统(OpenBSD / NetBSD / AIX / Cygwin 一类)的目标机每一次写都走就地写并计数。
|
|
784
|
+
host 车道的暂存是引擎的,device 车道的暂存是设备侧执行器的 —— 这一道不管它们;adb 的追加 = 7.138.x 的形:读旧 + 拼 + 整份 push(push 没有追加写)。Android 共享存储(`/sdcard` 一类)上 adb 的写按 sdcardfs / MediaProvider FUSE 的权限语义**推断**每一次都走这一形(未上真机,真机读数候 test)—— adb 的工作区放在 `/data/local/tmp` 一类认权限位的目录即照常暂存。
|
|
775
785
|
- 一个身份:准备目录、上传、提交、清理都以同一个身份做。local-docker 的上传是容器内以镜像的用户经 stdin 收字节(不是守护进程以 root 的 `docker cp`)——
|
|
776
786
|
写文件与代理的 shell 同一套权限,镜像用户写不了的路径答 `permission_denied` —— 镜像里没预建的工作区目录由容器启动以 root 建出,镜像用户 ≠ root 时要在镜像里预建并归镜像用户。
|
|
777
787
|
- 回落(host 与五条远端车道,与引擎同一套;device 由设备侧执行器自定,非常规文件拒写):单跳符号链接 ⇒ 替换它指的那只常规文件(链接仍是链接;远端车道先在那只文件的目录里重新暂存,rename 只在一个目录之内);链接链 / FIFO / 设备文件 / rename 被拒(如单文件绑定挂载)⇒ 由已落定的暂存件就地拷入(非原子);
|
|
@@ -1089,6 +1099,9 @@ UNREACHED_ASK_TTL_MS=3600000
|
|
|
1089
1099
|
非终态的问行(`STREAM_PENDING` / `PARKING`)永不按龄删 —— 它们归对账器与起服收敛。节律:首拍即清扫一次,之后每小时至多一次;每次先从 run 账、后台子代账与 checkpoint 店列出还能动的东西(任一读不出 ⇒ 这次不删),
|
|
1090
1100
|
问账一批至多 500 行、一次清扫删到不满一批为止。读数:起服一行 `approval_ask_retention`(`windowMs`;`0` = 关),真删了东西时一行 `approval_ask_retention_swept`
|
|
1091
1101
|
(`cutoffMs` / `asks` / `batches` / `auditAsks` / `auditRecords`;半途失败时已经删掉的照样打,带 `incomplete: true`)+ 计数器 `approval_ask_retention_rows_deleted_total{ledger=asks|batches|audit}`。
|
|
1102
|
+
local 的问账日志上,任何真删了行的调用(留存清扫的两只动词与 `deleteByTask`)的那一拍,同步把整本原子重写成存活行的快照 —— `asks.jsonl` 里同拍不再有被删的问的字节(含 `Write` / `Edit` 这类工具实参的字面);没删东西的调用不落记录、不重写;
|
|
1103
|
+
压实失败不影响调用(行照删,计 `fail_open_total{tag="server.approval-ask-journal.compaction-failed"}`,日志里留着删行记录,下一次写或下一次起服再压实)。
|
|
1104
|
+
起服时因撕尾 / 坏行隔离出去的原件 `asks.jsonl.corrupt-<ts>` 是取证材料,**没有清扫腿**,里面仍有当时的字节,要清由人来(正常部署恒零)。
|
|
1092
1105
|
**关法**:`APPROVAL_ASK_RETENTION_MS=0` = 永久保留(要在升级后的第一次清扫之前设 —— 首拍即扫);正值低于 1 天钳到 1 天并在启动告警里点名(刚结束的 run 的客户端还可能对它的卡重试 / 晚答,
|
|
1093
1106
|
一天吸收这一段);负值 / 非整数 / 非数拒启。run 结束超过窗之后才对它的卡作答 ⇒ 决断口与流内审批口答 404。
|
|
1094
1107
|
|
|
@@ -1457,7 +1470,7 @@ TASK_WRITE_FACE_ESCALATION=allow # 词表 allow|refuse;坏词启动期响亮
|
|
|
1457
1470
|
只有 `__proto__` 名的成员坏 ⇒ 剔掉它、其余照交。server 照挂,运维 warn `mcp_transport_members_dropped`(`server` / `lost` / `lostTotal?` / `note`)点名没交出去的键 / 成员与整键没交的
|
|
1458
1471
|
`transport.args` / `transport.principalHeader`。A2A 对端 `headers` 同读:值不是串 ⇒ 对端照旧不挂;只有 `__proto__` 成员坏 ⇒ 续跑照挂、剔掉它,一行 `a2a_peer_headers_members_dropped`
|
|
1459
1472
|
(`peer` / `lost` / `note`);新提交上两形都整条不挂。
|
|
1460
|
-
- **续跑时换表(7.143.0 起)**:停着的任务续跑(
|
|
1473
|
+
- **续跑时换表(7.143.0 起)**:停着的任务续跑(`POST /v1/approvals/:sessionId/decide` · `POST /v1/assistant/tasks/:id/plan_review` · `POST /v1/assistant/tasks/:id/resume` · `POST /v1/sessions/:id/wake`,各续各的门:工具审批卡 / `plan_review` 门 / 资源限停驻 / `task_done` 停驻)默认重放提交时存下的 `mcpServers` / `a2aPeers`;续跑请求体带上同名键(与提交体同形、同一只判官)就以它为准,
|
|
1461
1474
|
并写回成之后续跑的表 —— 用户移除过一台 MCP 服务器之后,壳送此刻的整张表,续跑就不再重连那一台(停在那一台调用上的任务由引擎判:引擎 7.61.0 起三码各答各形,以 core 为准:那只工具不在了 ⇒ `resume.tool_unavailable`(引擎重开检查点:卡仍 pending、可再决);同名而已不是批准时那一只 ⇒ `resume.tool_contract_mismatch`(同样重开);还是那一只而它的面或声明变了 ⇒ `resume.target_moved`(这次调用不执行、批准用掉,须重新发起),本服务逐字过境);直连门部署的两道回决门拒收这两键(契约 §4e-bis)。
|
|
1462
1475
|
|
|
1463
1476
|
**可选 — MCP 入站表单(elicitation,E23):默认关**
|
|
@@ -1987,9 +2000,9 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1987
2000
|
## 7. 其它接口(按需)
|
|
1988
2001
|
| 端点 | 用途 |
|
|
1989
2002
|
|---|---|
|
|
1990
|
-
| `GET /v1/sessions/<id>` | 审计回溯:当前上下文 + 摘要(owner 校验);消息里模型 / 工具产出的文本与 live 帧、账本帧同一只脱敏(密钥形换占位,其余字节不变);Read 图像 / PDF 的媒体卡(工具结果消息的 `details`)的 `file.base64` 是字节编码,逐字。**委派子代的转录**也走这一口:`<id>` = 子代帧 / fleet 子代行上的 `transcriptId`(未压缩过的子代首条用户消息是委派 prompt;子代跑过的工具是 assistant 消息里的 `toolCall` 段 + 同 `toolCallId` 的 `toolResult` 行;整段逐字条目走 `…/sync/entries`)。体顶层加性键 `agent: { id?, parentSessionId?, rootSessionId? }`(**7.133.0+**)说这是哪只子代:只投影会话行上存下来的委派元组,`id` 与 fleet 行的 `id`、`GET /v1/agents/roster` 的 `handle` 同一个串;键缺席 = 普通会话 / 老 server / 会话店不存元组(
|
|
1991
|
-
| `GET /v1/approvals?owner=user:42` | 高危写审批(durable,需 `DURABLE_APPROVAL=true`):**operator** 看待办队列(可按 owner 过滤);非 operator 只看自己的。队列里某一行的存储体读不出来、重试不会好时,答 `500` + `errorCode:"approvals.read_corrupt"`(**7.134.0+**;与订阅流同码、同一只判官;运维处置那一行后再读,别原样重试);此刻读不出队列(连接 / 超时 / 店抖动)⇒ `503` + `errorCode:"approvals.read_unavailable"`(**7.135.0+**,此前 500 `internal.error`;稍后重发,这一答不表示队列为空)。`GET /v1/assistant/inbox` / `GET /v1/assistant/tasks` 读同一个队列,两种读失败同码同状态(**7.135.0+**)。停驻的**技能卡**(对引擎 `Skill` 工具的调用)的行带 `boundGrantDigest` / `boundGrants`(批准它还授予的东西:技能名 + 两只工具清单;清单只在哈希等于摘要时才有,是部署文本,渲染要按上下文转义),分类器没给判词的卡带 `classifierNoRuling`(`error` / `timeout` / `parse_error`)—— **7.136.0+**,与 `GET /v1/assistant/inbox` 行逐字同值(此前只有收件箱行与活卡帧带);普通卡三位都缺席。`GET /v1/assistant/inbox` 行的 `objective` 是门所属 run 的**目标预览**(写入时已脱敏、截到约 120 字符,与 `GET /v1/assistant/tasks` 读同一列;委派子代的门读派它的那条 run;没有 run 账本 ⇒ `null`;**7.136.0+**,此前原样回显存着的任务目标,运维 `?owner=` 跨看也是原文) |
|
|
1992
|
-
| `GET /v1/approvals/stream` | 同一队列的 SSE 推送(范围同上):首拍快照 → `pending` / `resolved` 增量(开了工具审批时另有 `live_pending` / `live_resolved`),空闲时心跳,15 分钟上限(`STREAM_MAX_DURATION` 帧后重连)。**心跳只表示流活着,不表示这一拍读过队列**:队列读不出时每次读失败发一帧 `error`(`approvals.read_unavailable`,流留着、下一拍再读,恢复后同一条流继续);某一行的存储体读不出来、重试不会好时发 `approvals.read_corrupt` 并收流(运维处置那一行后再重连)。15 分钟上限对读失败的拍同样算数。`pending` 帧带的行与 `GET /v1/approvals` 同一只投影(含上面的技能卡三位,**7.136.0+**) |
|
|
2003
|
+
| `GET /v1/sessions/<id>` | 审计回溯:当前上下文 + 摘要(owner 校验);消息里模型 / 工具产出的文本与 live 帧、账本帧同一只脱敏(密钥形换占位,其余字节不变);Read 图像 / PDF 的媒体卡(工具结果消息的 `details`)的 `file.base64` 是字节编码,逐字。**委派子代的转录**也走这一口:`<id>` = 子代帧 / fleet 子代行上的 `transcriptId`(未压缩过的子代首条用户消息是委派 prompt;子代跑过的工具是 assistant 消息里的 `toolCall` 段 + 同 `toolCallId` 的 `toolResult` 行;整段逐字条目走 `…/sync/entries`)。体顶层加性键 `agent: { id?, parentSessionId?, rootSessionId? }`(**7.133.0+**)说这是哪只子代:只投影会话行上存下来的委派元组,`id` 与 fleet 行的 `id`、`GET /v1/agents/roster` 的 `handle` 同一个串;键缺席 = 普通会话 / 老 server(`DB_BACKEND=local` 到 7.140.x 也是)/ 会话店不存元组(`DB_BACKEND=memory`;嵌入方自建、不声明委派分区的店),读作「没存」、回落 fleet 行 / 名册上的 id。子代会话的属主是引擎判的委派树属主(**7.144.0+**:没人派生的 run 读自己那一行的属主、按化身绑一次,原样带给每一层子代;三只持久会话店〔MySQL 协议 / PostgreSQL / local〕建行那一笔原样记引擎交来的属主,`null` = 根匿名;停驻挂点只比对宿主行属主与引擎交来的属主,不认领、不推断。**7.133.0–7.143.x** 是本服务在建行那一笔按根 / 父推断:根会话的行在 ⇒ 它的属主,根行已不在 ⇒ 它记录的父会话的,都不在 ⇒ 哨兵 `_orphan:<id>`;7.132.x 及更早的 SQL 后端上子代会话无主、谁都读得到)⇒ 用发起子代的那位主体读;别的主体与不存在的 id 同一个 `404 not_found.session`。属主为空的委派子代行(根会话本就无主,或升级前写下、还没回填)只有 fleet 调用方读得到(出示 service 凭据、不带主体头;dev-open 部署不钉人),任何具名主体、没有 service 凭据的无身份调用方同样 404(存量无主会话互认臂 —— 单用户形下 `default` 主对无主的普通会话放行,fork / 删除 / `/sync/*`(同步族自 7.134.0 起)同走这一只 —— 对这些 placed 行同样不放行,审批豁免、notify 同样不放行;fork / 删除 / `/sync/*` 的 fleet 调用方与此处同一个判据:出示 service 凭据、不带主体头,带主体头的 service 凭据请求是那位主体,7.134.0 起);SQL 后端两店每次起服由一次性迁移 M23 在它的窗内回填升级前写下的无主 placed 行(按 7.133.0 起的读法:根的属主,根行不在取记录的父的,都不在写 `_orphan:<id>`;起服读数行 `placedSessionOwnersRemaining` 非零 = 还有没定下的,再起直到为 0),local 后端没有回填臂(**7.145.0+** local 后端建子代行先记属主、再建转录存档,进程崩在两笔之间留下的是一只没有转录的属主格 —— 不是子代行,不再新生无主子代行;更早版本崩在两笔之间留下的无主子代行照旧无主、不回填,出路是取消 / 删掉那只后台子代)。属主 `_orphan:<id>` 由 7.133.0–7.143.x 的建行推断与 M23 回填写下(7.144.0 起本服务不再推哨兵,建行原样记引擎交来的属主 —— 已有的哨兵属主随引擎的树属主原样带到它下面新建的子代行上):任何主体都不匹配,只有 fleet-wide / operator 读得到,前缀永久保留。停驻时宿主行属主与引擎交来的不等 ⇒ 停驻否决(那张待批卡作废、那一只子代以失败收尾) |
|
|
2004
|
+
| `GET /v1/approvals?owner=user:42` | 高危写审批(durable,需 `DURABLE_APPROVAL=true`):**operator** 看待办队列(可按 owner 过滤);非 operator 只看自己的。队列里某一行的存储体读不出来、重试不会好时,答 `500` + `errorCode:"approvals.read_corrupt"`(**7.134.0+**;与订阅流同码、同一只判官;运维处置那一行后再读,别原样重试);此刻读不出队列(连接 / 超时 / 店抖动)⇒ `503` + `errorCode:"approvals.read_unavailable"`(**7.135.0+**,此前 500 `internal.error`;稍后重发,这一答不表示队列为空)。`GET /v1/assistant/inbox` / `GET /v1/assistant/tasks` 读同一个队列,两种读失败同码同状态(**7.135.0+**)。一张停驻行的存储体读不出摘要(手改成 `{}`、整段缺 `state` 等)只坏它自己那一行(**7.145.0+**,MySQL 协议 / PostgreSQL 后端):收件箱与运维队列照列它、带引擎判官的 `rowVerdict`(只缺 `pendingAction` 的行判官答 `ok`,引擎侧已知缺口),任务视图照列那条 run,三个面都不再整张答 500 / 503;别的卡的决断照常,这张卡自己的决断答 409 带码拒;`DB_BACKEND=local` 上列表面本版仍整张 503(决断已逐行)。停驻的**技能卡**(对引擎 `Skill` 工具的调用)的行带 `boundGrantDigest` / `boundGrants`(批准它还授予的东西:技能名 + 两只工具清单;清单只在哈希等于摘要时才有,是部署文本,渲染要按上下文转义),分类器没给判词的卡带 `classifierNoRuling`(`error` / `timeout` / `parse_error`)—— **7.136.0+**,与 `GET /v1/assistant/inbox` 行逐字同值(此前只有收件箱行与活卡帧带);普通卡三位都缺席。`GET /v1/assistant/inbox` 行的 `objective` 是门所属 run 的**目标预览**(写入时已脱敏、截到约 120 字符,与 `GET /v1/assistant/tasks` 读同一列;委派子代的门读派它的那条 run;没有 run 账本 ⇒ `null`;**7.136.0+**,此前原样回显存着的任务目标,运维 `?owner=` 跨看也是原文)。停驻行带 `agent`(**7.145.0+**):行的会话在委派树里的位置 `{ id?, parentSessionId?, rootSessionId? }`,与 `GET /v1/sessions/<sessionId>` 体顶层的 `agent` 同一只读口、逐值同答 —— 后台委派子代停驻的行(`taskId` 为 `null`、`sessionId` 是子代自己的会话)凭它连回发起委派的会话(`rootSessionId` / `parentSessionId` = 本端会话 ⇒ 本端的卡);缺席 = 这台 server 没有说(宿主会话自己的停驻、同步委派的子代、`subagent_type: "fork"` 的后台子代、不存委派元组的店、那一读没读成 —— 只这一行缺键、列表照出),按原读法判,不读作「不是子代」 |
|
|
2005
|
+
| `GET /v1/approvals/stream` | 同一队列的 SSE 推送(范围同上):首拍快照 → `pending` / `resolved` 增量(开了工具审批时另有 `live_pending` / `live_resolved`),空闲时心跳,15 分钟上限(`STREAM_MAX_DURATION` 帧后重连)。**心跳只表示流活着,不表示这一拍读过队列**:队列读不出时每次读失败发一帧 `error`(`approvals.read_unavailable`,流留着、下一拍再读,恢复后同一条流继续);某一行的存储体读不出来、重试不会好时发 `approvals.read_corrupt` 并收流(运维处置那一行后再重连)。15 分钟上限对读失败的拍同样算数。`pending` 帧带的行与 `GET /v1/approvals` 同一只投影(含上面的技能卡三位,**7.136.0+**;含 `agent`,新订阅首拍补给的帧与之后推出的帧同值,**7.145.0+**) |
|
|
1993
2006
|
| `POST /v1/approvals/<sessionId>/decide` `{"decision":"approve"|"deny","reason":"…"}` | 批/否并恢复挂起任务(CAS,重复决议 409);**仅 operator**(非 operator → 403)。5.0.0 起旧轮询腿 `POST /v1/approvals/<id>` 已退役。停驻的后台子代**复活不了**(行上没有根会话锚;未起名的后台子代不在此列 —— 引擎 7.57.0 起每只子代都有名字,升级前的无名行读作派生名,照常复活)⇒ `deny` 作废这张卡:`200 {taskId, status:"failed", decision:"deny"}`,卡离开待决队列、子代终局 `failed`;`approve` 答 `409 decide.row_unrevivable`、拒句指路 deny(**7.136.0+**;此前两个决定都 409,卡处理不掉)。有根会话锚的停驻子代卡上,`decide` 与删根会话 / 卡作废并发:写口放行之后卡已被作废 ⇒ `422 parked_resume.startup_failed`(卡已作废、什么都没复活;与 `409 decide.gate_moved` 并列为并发终局码,调用方须同时处理 409 / 422) |
|
|
1994
2007
|
| `GET /v1/tool-approvals/<approvalId>` | 按问读口(**7.133.0+**;与 `POST …/respond` 同门同身份,不是你的 ask 与查无同形 404;**7.136.0+** 两扇决议口 —— `POST …/respond` 与耐久决断口 `POST /v1/tasks/<taskId>/asks/<askId>/decision` —— 与本口同一只属主判据:有行的问读行上当下的属主,收编之后新属主三口都认、前属主三口都 404;本口在店读不出时只对本副本自己铸的活登记回落登记(登记属主认你 ⇒ `pending`),起服接管的登记不回落、答 404 —— 与同一刻的应答口同答,此前本副本上的活卡读的是铸卡那一刻记下的属主):一只审批问此刻在哪一态,`state` ∈ `pending` / `decided` / `parked` / `void`(闭集)。停驻卡被答过 ⇒ `decided`、停驻 run 被取消 ⇒ `void`(**7.134.0+**;7.133.x 恒答 `parked`);停驻行的门已结算却读不出可用的决议(含坏行:状态词 / 种类 / 当事方不在闭集、结算记录过不了引擎的不变量筛、缺必填成员、时刻 / 方向读不出)⇒ 404 `tool_approval.not_pending`,运维日志一行 error(`defect` 首条缺陷句 · `defectCount`)。停驻行的 `decided` 可回到 `parked`:续跑腿在被批准的动作开跑前失败时,引擎重开同一张门 —— 读口答 `parked` + `decidePath` 并另带 `reopened`(**7.135.0+**):`reason` 是引擎的重开因由词(`env_failed` 按原决议经 `decidePath` 再交一次 —— 本副本还留着首次受理回执时(缺省 1 小时内)`…/respond` 回放那张回执、不驱续跑;`tool_unavailable` / `constraint_rejected` 重新决;`config_refused` 改好配置再交或重新决),`decision` / `decidedAtMs` / `decisionNote?` 是重开之前的那次决议(读不出 ⇒ 只带 `reason`);再决之后回到 `decided`。续跑被取消 ⇒ 重开的门被收掉,读作 `void`。全文见契约 §4a-bis-read |
|
|
1995
2008
|
| `GET /v1/agents/roster` | 后台 agent **名册**(7.29 起,需 durable background-agent store,否则 501 `capability.background_agent_store_required`):调用方自己 scope 的 `a…` 行,新→旧。行只带 `handle`/`name`/`agentType`/`status`/`spawnedAt`/`updatedAt`/`settledAt`(content-free;子代产物正文走 `GET /v1/runs/<id>/subagents/<handle>/output`)。查询:`?limit=`(1..100,默认 20)、`?before=`(上一页的 `nextBefore` 游标)、`?status=`(running\|parked\|completed\|failed\|killed)、`?session=`(按会话树窄化)、`?scope=`(**仅** fleet 调用方:不带主体头的 service 凭据 / 只读 `TRACE_TOKEN`(只在没配 service 凭据的部署上够得着这一口)/ dev-open;带主体头的调用方 —— 含带 service 凭据的 —— 锁定自己)。回体 `{agents,total,nextBefore?,truncated?}`;`truncated:true` = 租户行数超过名册窗(500),`total` 此时是窗内数。非属主的行**不出现也不计数**(无存在性 oracle) |
|
|
@@ -2259,9 +2272,10 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
2259
2272
|
> 没有家目录读数,座缺席,起服响一行同一只 warn。`~/` 形规则的下单门只认声明(提交那一刻执行环境还没开,读不到实测值):要写 `~/` 形规则就声明 `REMOTE_EXEC_HOME_DIR`。
|
|
2260
2273
|
> **声明与出厂答案都会被对证**(本版起六条车道同一只判官):k8s、local-docker、ssh、adb、e2b 在每只执行环境开起来那一拍问一次 `$HOME`(与命令同一条 shell、同一份 env;
|
|
2261
2274
|
> adb 经设备 shell、e2b 是建 / 恢复沙箱时的一次 `commands.run`),host 读它自己组合给子进程的那份 env 里的 `HOME`(会话 `settings.env` 改了 `HOME` 也算);
|
|
2262
|
-
> 与主张(`REMOTE_EXEC_HOME_DIR`,或 host 的本进程家目录 / e2b 的 `/home/user`)不一致(去掉尾斜杠后逐字比)或问不出 ⇒
|
|
2263
|
-
> (把 `REMOTE_EXEC_HOME_DIR` 设成实测值、删掉错的声明,或让环境的 `HOME` 读主张那个值)
|
|
2264
|
-
>
|
|
2275
|
+
> 与主张(`REMOTE_EXEC_HOME_DIR`,或 host 的本进程家目录 / e2b 的 `/home/user`)不一致(去掉尾斜杠后逐字比)或问不出 ⇒ 那只执行环境不开,拒句点名两个值与改法
|
|
2276
|
+
> (把 `REMOTE_EXEC_HOME_DIR` 设成实测值、删掉错的声明,或让环境的 `HOME` 读主张那个值)。host 车道的读数在铸环境时就到手 ⇒ 环境不铸,run 在开跑前失败
|
|
2277
|
+
> (码 `env.home_unconfirmed`,同步腿 409;一条命令没跑、模型一次没调,命令带不带路径都是这一句;7.144.0 起,此前拒句落在工具结果里);远端五车道的环境也在开跑前打开、拒在那一拍判出,但 run 照样开跑 ⇒
|
|
2278
|
+
> 拒句落在工具结果里(带路径的命令可能先撞读边界、ssh / adb 上带附件的会话开跑前就失败、从快照恢复的续跑腿答 `resume.env_failed`,逐形见 §9 的 `env.home_unconfirmed` 行),环境答了的不一致记在那只环境上(此后每次回同一句,不再起容器 / pod / 沙箱、不再问),问不出的那一次不记、下一次重问。e2b 恢复出来的沙箱对不上时尝试重新挂起(挂起成功快照留着;挂起失败只记一行 warn,沙箱按自己的寿命到期),不杀。
|
|
2265
2279
|
> e2b 的出厂答案 `/home/user` 已于 2026-10-29 在真沙箱上实测一致(缺省模板:用户 `user`、`$HOME` = `/home/user`);自定义模板换了用户的,`$HOME` 若不是它,没声明 `REMOTE_EXEC_HOME_DIR` 的 e2b 部署的每只沙箱建起来就拒,把声明设成沙箱里 `echo $HOME` 的结果(声明同样被对证)。
|
|
2266
2280
|
> device 车道读不了家目录:声明即座,起服响一行 `execution_lane_home_unmeasured`(点名声明值与「未实测」)。
|
|
2267
2281
|
>
|
|
@@ -2381,6 +2395,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
2381
2395
|
| HTTP `409`(`/v1/runs`) | 同 session 已有活跃 run | 等它完成 / 用返回的 `activeTaskId` |
|
|
2382
2396
|
| HTTP `409` + `errorCode:"conflict.idempotency_payload_mismatch"`(7.126.0 起;`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`) | `Idempotency-Key` 已绑定在一条你读得到的 run 上,而绑定它的那次请求的体与这次不同(规范化:按键排序、去掉 `taskId`;契约 §0.7「同一请求 = 同一份体」)—— 不回放、不新建、那一条不动,体不回显键与那一行;流式腿在开流之后才撞上时,是终帧 `done.result` 带同一 `errorCode` 的 `failed`。A2A 不会遇到 | 重试就重发同一份体;不同的请求换一把新键。与 `conflict.run_exists` 不同:后者是那一行你读不到、或带的 `taskId` 不是那一行 |
|
|
2383
2397
|
| HTTP `429` | 限流 | 看 `Retry-After` 退避 |
|
|
2398
|
+
| HTTP `409` + `errorCode:"conflict.session_archive_stamped"`(7.144.0 起;`DB_BACKEND=local`;`POST /v1/sessions/:id/fork` 与会话同步导入的提交 `POST …/sync/import/:stagingId/entries`;7.145.0 起第一阶段 `POST …/sync/import` 对按 id 分类出 fork / stale 且没带同意的推送也直接答它,此前先答通用 `conflict`;7.141.0–7.143.0 答的码是 `conflict`) | 这只会话的存档仍带更早版本写下的属主戳(一次性迁移改写不了的,或跳过带迁移的版本升上来的数据根;起服 warn `local_archive_kept_stamped` 点名):不在它上面造新存档、不原地换它的日志(干净追加 / 载荷分叉 / fork / stale 各形的推送同答),什么都没写;读 / 续跑 / 拉取照常,「id 与内容都相同」的重推照常落定 | 按体里的固定句办:删掉这只会话(`DELETE /v1/sessions/:id`)再推。与暂存提交时属主变了的 `errorCode:"conflict"`(出路相反:从第一阶段重来)按码分开 |
|
|
2384
2399
|
| HTTP `429` + `errorCode:"limit.cost_quota_exceeded"` | per-principal 累计成本配额越顶(`MAX_PRINCIPAL_COST_USD`;**进场门**,不打断在跑的 run) | 看 `Retry-After` / 体 `retryAfterSec` 退避(体 `windowResetsAt` = 当代窗滚过的时刻);窗滚过或调高上限后放行 |
|
|
2385
2400
|
| HTTP `400` + `errorCode:"request.id_invalid"` | 调用方给的 id 形不对。**会话 id 的形门只有一只** = 任意版本 canonical uuid(壳预设的 v4 也过门):会话口 `:id`(fork / 抹除 / 会话策略 / 2c 同步)与提交口(`POST /v1/tasks` / `/v1/tasks/stream` / `/v1/runs` 体里的 `sessionId`,A2A 的 `contextId` 同)同判,提交口的句子在句首之后点名「改用 uuid、自己记映射」;`GET /v1/sendfile-links?before=` 游标与 `body.taskId` 幂等键要求 uuidv7(时间序 / 幂等键契约) | 按 `errorCode` 分支;句子各自点名要求的形(`must be a canonical uuid` / `must be a uuidv7 …`),只作展示、别锚文案 |
|
|
2386
2401
|
| HTTP `400` + `errorCode:"request.invalid_json"` + `reason:"not_json"` \| `"not_object"`(`reason` 7.133.0 起) | 请求体不是合法 JSON(`not_json`;所有读体的口同一个体 —— 含 `notify` / `wake` / 权限规则这几口,它们 7.132.x 及以前答 `request.body_shape`,其余多数口答 `request.rejected`),或合法 JSON 却不是这一口要的对象(`not_object`,`POST /v1/runs/:id/compact`)。读体超 8 MiB 是 `413 request.payload_too_large`(`field:"body"` / `reason:"too_large"` / `cap`,没有 `count`),不是本码 | 修请求体;按 `errorCode` + `reason` 分支,句子只作展示 |
|
|
@@ -2397,10 +2412,10 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
2397
2412
|
| HTTP `422`(`request settings cannot be applied as written: …`)+ `detail.problems[].reason:"home_unresolvable"`,句子说 `user-settings path rule rooted at the configuration directory`(7.141.0 起) | 请求 `settings.permissions.deny` / `ask` 里一条 `layer:"userSettings"` 的 `/` 形路径规则,而这条沙箱车道既没把 `SEMA_CONFIG_DIR` 转进沙箱、也没有可知的 home(k8s / local-docker / ssh / adb / device 且未声明 `REMOTE_EXEC_HOME_DIR`)—— 配置根读不出,不猜目录(续跑同体 409 同句) | 往沙箱 env 名单里加 `SEMA_CONFIG_DIR`,或声明 `REMOTE_EXEC_HOME_DIR`,或把规则写成 `//` 绝对形 / 放进项目层(读在任务根);见 9.4d |
|
|
2398
2413
|
| HTTP `429` + `errorCode:"usage.window_exhausted"` | 部署级治理窗耗尽(`USAGE_WINDOWS`,token 或 $ 天花板先满者) | 看响应体 `retryAfterSec` 退避;窗滑动/桶到期后放行 |
|
|
2399
2414
|
| `terminal:{kind:"failed", code:"usage.window_exhausted"}` | 已受理的 run 在 **turn 边界**撞上治理窗且无法 durable 挂起 | 同上退避后重投;配好 checkpoint 基建则改为 `suspended` 等窗自动续跑 |
|
|
2415
|
+
| HTTP `409` + `errorCode:"env.home_unconfirmed"`(同步 `POST /v1/tasks`;流式 / 后台腿是 `terminal:{kind:"failed", code:"env.home_unconfirmed"}`;7.144.0 起) | 执行环境的家目录主张证不了:部署给环境的家目录(`REMOTE_EXEC_HOME_DIR`,没设时 host 车道 = 服务进程的家目录)与环境里命令看到的 `HOME`(含会话 `settings.env` 里写的 `HOME`)对不上 / 读不出 / 不是绝对路径 ⇒ 环境在开跑前就不铸:一条命令没跑、模型一次没调、一个 token 没烧,不管模型要跑的命令带不带路径都是这一句(句子点名两个家目录与改法)。今天只有 host 车道(含 `run-local`)这样答;远端五车道的环境也在开跑前打开、拒在那一拍判出,但 run 照样开跑:不带路径的命令拿到同一句(工具结果里);读边界要先读环境才判得了的命令先被读边界拦下、句子不点名家目录(路径不在读拒表上时;全 shell 面要人批:`~/` 下的递归读五条车道都是,`~/` 下的普通读在 ssh / adb,工作区内相对路径的普通读在 adb;`AUTONOMY=read-only` 上 adb 的工作区相对路径读被拒);ssh / adb 上会话绑了附件 ⇒ 开跑前 `failed`、终局不带码(句子以 `attachment materialization failed` 起头);从快照恢复的续跑腿(e2b;配了快照存储的 k8s)⇒ `resume.env_failed` | 把 `REMOTE_EXEC_HOME_DIR` 设成环境里 `echo $HOME` 的结果(或删掉它 —— host / e2b 删掉后主张回落车道的出厂答案,仍要与 `HOME` 一致),或让环境的 `HOME` 等于声明,或去掉会话 `settings.env` 里的 `HOME`;原样重试不改判 |
|
|
2400
2416
|
| HTTP `501`(`/v1/runs`) | 内存模式不支持异步 | 配持久存储(`DB_BACKEND=mysql` / `pg` + 坐标;7.106.0 起给了坐标必须点名 `DB_BACKEND`) |
|
|
2401
|
-
| HTTP `500` + `errorCode:"corrupt_session"`(7.104.0 起;流式口在应答头之后则是 `done` 帧的 `result.terminal.code`,后台 run 是 run 行的 `errorCode`) | 这只会话的**存档读不出**、唤不醒(文件后端:会话日志没有会话头;SQL 后端:一行事件读不出)。这一轮没跑,一个 token 没烧。直接读这只会话的口(头探针 / 事件订阅 / 会话视图 / 改名 / 导出 / 分叉 / 2c 同步)同答(契约 §17;文件后端上事件订阅与改名 7.136.0 起同答,此前一个 200 起流却零帧、一个 404)。会话列表 `GET /v1/sessions` 里它仍是一行,行带 `unreadable: { errorCode }`,码就是这些口对它答的那一个(文件后端,7.142.0 起;此前它从列表里消失;存档文件本身读不出 —— 权限 / I/O —— 时码是 `internal.error`) | **别重试**(确定性失败)。**文件后端**:运维看 `fail_open_total{tag="server.session-repo.corrupt-record-skipped"}` 的计数、stderr 契约行 `[sema] fail-open: server.session-repo.corrupt-record-skipped` 与随带的一条 `fail_open` warn(后两者每进程每 tag 只一次,warn 带首条明细);逐条明细(文件路径、会话 id、行号、成因)每命中一行进探针文件 —— 起服前设 `SEMA_FAILOPEN_PROBE=<文件路径>`(会话头那一行本身读不出时,它就是其中一条);修或移开那份存档。**SQL 后端**不经这只座:坏的那一行由错误句子点名(`session_event.payload` + 会话 id + 序号 `seq`)。要继续工作就换一个新会话 |
|
|
2402
|
-
|
|
|
2403
|
-
| 流里一帧 `engine_notice` `code:"session.records_skipped"`,`detail:{ dropped, entriesKept, sessionId }`(7.104.0 起,只在文件后端) | 这只会话的存档打开时有 `dropped` 条记录读不出、被跳过了;这一轮**照跑**,但模型读到的历史只剩 `entriesKept` 条。存档没修好之前,每次真正打开这只会话都会再说一次(会话缓存命中的轮次不打开、不说;`SESSION_CACHE_TTL_SEC=0` 时每一轮都说) | 告诉用户「这段对话的一部分历史丢了」;运维按 `fail_open_total{tag="server.session-repo.corrupt-record-skipped"}` 的计数与探针文件(`SEMA_FAILOPEN_PROBE=<文件路径>`,每命中一行)里的逐条明细(文件路径、会话 id、行号、成因)查那份文件。非流式 `POST /v1/tasks` 收不到通告帧(只有运维面的计数与探针) |
|
|
2417
|
+
| HTTP `500` + `errorCode:"corrupt_session"`(7.104.0 起;流式口在应答头之后则是 `done` 帧的 `result.terminal.code`,后台 run 是 run 行的 `errorCode`) | 这只会话的**存档读不出**、唤不醒(文件后端:会话日志没有会话头;SQL 后端:一行事件读不出);或(7.144.0 起)存着的日志读得出、**走不成一棵树**:某条目的父不在日志里而回放没丢记录(悬空)、条目是自己的祖先(自指 / 成环)、叶子不在日志里(7.137.0–7.143.0 这一形答 `invalid_session`;句子点名条目 id,读整条分支的口 —— 会话视图 `GET /v1/sessions/:id` / `…/memory-status` —— 同答,事件订阅与头探针只读会话头的叶子,照旧 200 / 起流)。这一轮没跑,一个 token 没烧。直接读这只会话的口(头探针 / 事件订阅 / 会话视图 / 改名 / 导出 / 分叉 / 2c 同步)同答(契约 §17;文件后端上事件订阅与改名 7.136.0 起同答,此前一个 200 起流却零帧、一个 404)。会话列表 `GET /v1/sessions` 里它仍是一行,行带 `unreadable: { errorCode }`,码就是这些口对它答的那一个(文件后端,7.142.0 起;此前它从列表里消失;存档文件本身读不出 —— 权限 / I/O —— 时码是 `internal.error`) | **别重试**(确定性失败)。**文件后端**:运维看 `fail_open_total{tag="server.session-repo.corrupt-record-skipped"}` 的计数、stderr 契约行 `[sema] fail-open: server.session-repo.corrupt-record-skipped` 与随带的一条 `fail_open` warn(后两者每进程每 tag 只一次,warn 带首条明细);逐条明细(文件路径、会话 id、行号、成因)每命中一行进探针文件 —— 起服前设 `SEMA_FAILOPEN_PROBE=<文件路径>`(会话头那一行本身读不出时,它就是其中一条);修或移开那份存档。**SQL 后端**不经这只座:坏的那一行由错误句子点名(`session_event.payload` + 会话 id + 序号 `seq`)。要继续工作就换一个新会话 |
|
|
2418
|
+
| 流里一帧 `engine_notice` `code:"session.lineage_rerooted"`,`detail:{ break, recordsDropped, recordsTotal, leaf, entriesOnPath, sessionId, runId }`(7.144.0 起,只在文件后端;7.104.0–7.143.0 这件事由 `session.records_skipped` 说,已退役) | 这只会话的日志回放时有 `recordsDropped` 条记录读不出、被跳过了;这一轮**照跑**,模型读到的历史是 `entriesOnPath` 条。`break: true` = 被跳过的是分支上的祖先(`missingParentId`),这一轮从点名它作父的那一条(`rootedAt`)续上、`entriesCut` 条更早的条目不在上下文里(7.143.0 及以前这一形整轮失败);`break: false` = 分支完整,从 `leaf` 续上(`leaf: null` = 一条都没剩,从空对话起跑)。日志没修好之前每一轮都会再说一次(会话缓存命中的轮次照样说);进程已持有这只会话时盘上才坏的行,这一轮读的是内存里那份完整历史、不说,重启后的第一轮才说 | 告诉用户「这段对话的一部分历史丢了」;运维按 `fail_open_total{tag="server.session-repo.corrupt-record-skipped"}` 的计数与探针文件(`SEMA_FAILOPEN_PROBE=<文件路径>`,每命中一行)里的逐条明细(文件路径、会话 id、行号、成因)查那份文件。非流式 `POST /v1/tasks` 收不到通告帧(只有运维面的计数与探针) |
|
|
2404
2419
|
| 流里一帧 `engine_notice` `code:"route.finish_reason_unknown"`,`detail:{ finishReason, sessionId, runId }`(7.126.0 起) | 提供方用一个引擎读不懂的结束原因结束了这一轮:回答按模型留下的样子保留,但可能被提供方截短了 | 告诉用户「这一段回答可能不完整」;同一个词同一 run 只说一次。运维按 `finishReason` 查是哪家网关 / 模型发的 |
|
|
2405
2420
|
| 流里一帧 `engine_notice` `code:"route.stream_end_ignored"`,`detail:{ statement, word, frames, sessionId, runId }`(7.126.0 起) | 提供方已经结束了这一轮,它的流却又发来了东西(又一个结束 / 一个错误 / 内容);第一个结束算数,之后的没进这一轮 | 一般无需处置;频繁出现 ⇒ 运维查那条网关的流式实现(`statement` / `word` 说的是迟到的第一帧) |
|
|
2406
2421
|
|
|
@@ -2891,7 +2906,10 @@ plan 起步的任务把计划交给人之后停在 `plan_review` 门上。`POST
|
|
|
2891
2906
|
出路:转 `SEMA_CONFIG_DIR`、声明 `REMOTE_EXEC_HOME_DIR`,或把规则写成 `//` 绝对形。转进去的值不是绝对路径 ⇒ deny / ask 422 `unsupported.path_base`(allow 桶见下面「allow 桶同读」)。
|
|
2892
2907
|
- **层不改准入**:多租部署上的准入档只按这次请求验过的凭据推(9 节速查 `below_trust_floor` 一行);条目上的层只决定规则读在哪。
|
|
2893
2908
|
- **回滚 / 混版副本**:本版之前的服务对带层条目是桶门 `bad_entry`(新提交 400,续跑 409),不会静默读丢;端见位才发,所以没升级的服务上它照旧只收到纯串。
|
|
2894
|
-
- **allow 桶同读**:`"allow": [{ "rule": "
|
|
2909
|
+
- **allow 桶同读**:`"allow": [{ "rule": "Read(/secret/**)", "layer": "userSettings" }]` 放的是配置根下 `secret` 的读,不是任务根下的;别的层与纯串照旧读在任务根。
|
|
2910
|
+
**配置根下的写不归 allow 放**:配置根在每条车道上都在引擎缺省写保护表的 `.sema` 行上(声明的 `SEMA_CONFIG_DIR` 及其以下全命中,转进沙箱的是同名 env 的原值、同一只目录;缺省 `~/.sema` 与沙箱车道缺省的 `<home>/.sema` 带 `.sema` 段),
|
|
2911
|
+
那里的写照问(卡上 `mandate: "write_protection"`;规则答不了这一类问),带层条目、纯串、`//` 绝对形的 allow 都清不掉 —— user 层 `Edit(/secret/**)` 放不掉配置根下 `secret` 的写。
|
|
2912
|
+
两处不归这一行:引擎数据根(壳布局 `~/.sema/engine-data`)坐在配置根下时,根里的写按根内的相对路径判;整表替换写保护表(`WRITE_PROTECTED_TABLE_REPLACE`)去掉这一行时随之不在。
|
|
2895
2913
|
一条一员按位置交给引擎(命中的 `positions` = settings 表的下标);配置根读不出时与 deny / ask 同一个 422;转进沙箱的值不是绝对路径时受理口不拒、引擎开跑时拒这条 run
|
|
2896
2914
|
(`config.invalid_permission_rules`,issue `unsupported.path_base`)。任何一层的 deny / ask 照旧压过它。
|
|
2897
2915
|
|
package/dist/approval-card.d.ts
CHANGED
|
@@ -517,24 +517,18 @@ export declare function parkRowFacts(stored: unknown): ParkRowFacts;
|
|
|
517
517
|
* (「never a list beside a digest that binds another」—— 一份与摘要对不上的清单会让批准人看着一份清单、批的却是另一份绑定);成因词只在闭集成员时回显。
|
|
518
518
|
* 本仓**不重算哈希**(core 的 `boundInputHashOf` 不在公开导出面上,清单校验读口 `readBoundBinding` 同样不导出),所以**不许**拿寄存行 `pendingAction` 上的原清单直接投 ——
|
|
519
519
|
* 原值上的清单是未经哈希校验的(这正是它此前不上队列行的原因,见 `trace/core-keyset-guard.ts` ⑧b 记账的旧注)。两只店各自拿到「这一行的摘要」的办法不同
|
|
520
|
-
* (LOCAL 孪生本来就握着 core 对同一行投的摘要;SQL
|
|
520
|
+
* (LOCAL 孪生本来就握着 core 对同一行投的摘要;SQL 孪生在判官要的那一次整行读里调 core 的 `summarizeCheckpoint` —— 两位与 `rowVerdict` 同一次读、
|
|
521
|
+
* 同一份按字节摘要的记忆,见 `plugins/checkpoint-store-sql.ts` 的 `storedRowProjections`),
|
|
521
522
|
* 但投影只有这一只:OMIT 契约 —— 缺席即缺席,恒不铸 `undefined` 值的键。
|
|
522
523
|
*
|
|
523
524
|
* `boundGrants` 是**部署文本**(技能名、清单条目):本函数不脱敏、不转义,与收件箱行逐字同口径 —— 渲染按上下文转义是客户端的事(契约同句)。
|
|
524
525
|
* 本投影的产物不进 `card_json`(它是停驻面的投影);问行卡信封的活面事实座(7.143.0)存的是呈卡帧上同名两位(活铸素材,与停驻孪生同一只工厂铸)。
|
|
525
526
|
*
|
|
526
|
-
* 🚧 清单校验读口 `readBoundBinding` 若成为 core
|
|
527
|
-
*
|
|
527
|
+
* 🚧 清单校验读口 `readBoundBinding` 若成为 core 的公开导出:`grantBindingOf` 改成它的薄包装(那时 core 是绑定读法的唯一属主)。SQL 孪生的整行读不随之退役 ——
|
|
528
|
+
* 7.144.0 起它首先是判官 `judgeCheckpointRow` 要的([ref]:判官读要求表要整行),两位随它同一次读、同一份记忆,不另开读法。
|
|
528
529
|
*/
|
|
529
530
|
export type SkillCardFacts = Pick<CoreCheckpointSummary, "boundGrants" | "classifierNoRuling">;
|
|
530
531
|
export declare function skillCardFacts(summary: SkillCardFacts): SkillCardFacts;
|
|
531
|
-
/**
|
|
532
|
-
* 这一行的寄存行(`pendingAction`,读自持久层 ⇒ `unknown`)**有没有可能**让 core 的摘要投出两位里的任何一位 —— 只回答「值不值得为它取一次摘要」,**不投值**:
|
|
533
|
-
* `tool_approval` 臂上摘要字段是串(core 只在这时回显清单),或成因词键在场(是不是闭集成员由 core 的摘要判)。判错的方向只有一个 —— 多取一次摘要(摘要读数为空),
|
|
534
|
-
* 绝不漏取:摘要字段不是串 ⇒ core 的摘要没有清单;成因词键不在 ⇒ 无成因词 —— 两者都不满足的行,摘要里这两位恒空。普通卡(绝大多数行)因此零额外读。
|
|
535
|
-
* 只读键在不在 / 是不是串,不读值的内容,所以不需要 {@link readClosedWordSeat} 那样的计数。
|
|
536
|
-
*/
|
|
537
|
-
export declare function parkRowCarriesSkillCardFacts(stored: unknown): boolean;
|
|
538
532
|
/** [ref](core 7.4.0 [ref])—— 回落卡的计数与窗。`limit` 是**闭二词**(core d.ts 逐字),两个计数是
|
|
539
533
|
* 非负整数,`autoDenyAfterMs` 是 core 契约里的 `0..2147483647` 非负整数(`0` = 本卡不武装窗:部署把
|
|
540
534
|
* 旋钮关了,或 TOTAL 档的卡按 [ref]① 恒 0 等人)。四成员**全必填**——core 的 `DenialLimitFallback` 上
|
|
@@ -599,6 +593,8 @@ export declare const ApprovalCardSchema: z.ZodObject<{
|
|
|
599
593
|
fetch: "fetch";
|
|
600
594
|
workflow: "workflow";
|
|
601
595
|
generic: "generic";
|
|
596
|
+
"computer-access": "computer-access";
|
|
597
|
+
"computer-control": "computer-control";
|
|
602
598
|
}>>;
|
|
603
599
|
inputHasBidi: z.ZodOptional<z.ZodLiteral<true>>;
|
|
604
600
|
allowRuleShadowed: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -775,6 +771,8 @@ export declare const ApprovalCardEnvelopeSchema: z.ZodObject<{
|
|
|
775
771
|
fetch: "fetch";
|
|
776
772
|
workflow: "workflow";
|
|
777
773
|
generic: "generic";
|
|
774
|
+
"computer-access": "computer-access";
|
|
775
|
+
"computer-control": "computer-control";
|
|
778
776
|
}>>;
|
|
779
777
|
inputHasBidi: z.ZodOptional<z.ZodLiteral<true>>;
|
|
780
778
|
allowRuleShadowed: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -1194,10 +1192,29 @@ export declare function buildRevokeFrame(batchId: string, askIds: readonly strin
|
|
|
1194
1192
|
*/
|
|
1195
1193
|
export declare function buildReplayFrame(row: Pick<AskRow, "askId" | "taskId" | "expiresAtMs" | "cardJson">, nowMs: number): ApprovalRequestFrame | undefined;
|
|
1196
1194
|
/**
|
|
1197
|
-
*
|
|
1198
|
-
*
|
|
1199
|
-
*
|
|
1200
|
-
*
|
|
1195
|
+
* 「这一问要人」(core `requiresRealApproval`:只有真人逐次答得了 —— 一揽子席、记住的放行、策略的放行都不行)的**唯一读法**:
|
|
1196
|
+
* 引擎在**这一腿**请求上盖的那一位 ∪ 这只问**行上那张卡**记着的那一位(`risk.requiresRealApproval`;新铸的卡上那一位就是本函数对铸卡那一腿的读数,
|
|
1197
|
+
* 见 {@link buildApprovalCard} 第三参)。任一为真即真 —— 并集只升不降:同一只问被另一腿再问(换副本重入、混版窗)读到不同的引擎事实时取严,
|
|
1198
|
+
* 永不因采信行或采信请求而放宽。
|
|
1199
|
+
* 两个输入各自可缺,调用方只交「手里有什么」:手里还没有行(会话授权短路、新铸那张卡、无问店)⇒ `recordCard` 缺席;手里没有这一腿的请求
|
|
1200
|
+
* (起服接管的登记、耐久问回决口 —— 只有行)⇒ `legRequest` 缺席。请求上的位按引擎的契约 `=== true` 严判(真才带;别的真值是照另一份契约写的)。
|
|
1201
|
+
* 读者:`tool-approval.ts` 的活审批腿(读侧会话授权短路的事实、铸卡、条目定稿 —— 条目 / 「本会话放行」判据 / 策略放行门 / 两族帧 / 列表行、
|
|
1202
|
+
* 起服接管登记、策略层自己交出去的问)与本文件的 {@link storedCardDemandsAPerson}。core 停驻行(`PendingAction.tool_approval`)上的孪生座是
|
|
1203
|
+
* 另一只店、另一个写者,读法是 {@link readParkRowTrueSeat}。
|
|
1204
|
+
*/
|
|
1205
|
+
export declare function askDemandsAPerson(legRequest: {
|
|
1206
|
+
readonly requiresRealApproval?: unknown;
|
|
1207
|
+
} | undefined, recordCard: Pick<ApprovalCard, "risk"> | undefined): boolean;
|
|
1208
|
+
/**
|
|
1209
|
+
* 问行上存的卡(`card_json`)交给 {@link askDemandsAPerson} 的那份**记录**:读得出 ⇒ 那张卡;读不出 ⇒ 一份「要人」的记录。
|
|
1210
|
+
* 🔴 读不出按要人(fail-closed):这一位只用来**拒**(策略的放行、记住的放行),读坏的行上拒一次可以改成人答 / 策略拒再来;反方向(读坏当不要人)
|
|
1211
|
+
* 会让一次引擎终将拒掉的放行先落成一条宣称 approve 的行、或让一张要人卡被记住。读者:{@link storedCardDemandsAPerson} 与 `tool-approval.ts` 里
|
|
1212
|
+
* 「行在、但这一腿没有采信行上信封」的那一臂(`ensureAsk` 报错后复核读读到行)。
|
|
1213
|
+
*/
|
|
1214
|
+
export declare function storedPersonDemandRecord(cardJson: unknown): Pick<ApprovalCard, "risk">;
|
|
1215
|
+
/**
|
|
1216
|
+
* [ref] —— 持久行上那张卡**要不要人**(手里只有行:经 {@link askDemandsAPerson},请求缺席;读不出按要人,见 {@link storedPersonDemandRecord})。
|
|
1217
|
+
* 读者 = 耐久 ask 回决口在裁决之前交给引擎判官 `policyCannotAnswer` 的那一问(`host-decision.ts` 的 `heldAskOf`)。
|
|
1201
1218
|
*/
|
|
1202
1219
|
export declare function storedCardDemandsAPerson(cardJson: unknown): boolean;
|
|
1203
1220
|
/** {@link buildApprovalPreamble} 的返回形——帧 + 两个**计数**(调用方按自己的 logger 记 warn;本模块
|
package/dist/approval-card.js
CHANGED
|
@@ -399,12 +399,6 @@ export function skillCardFacts(summary) {
|
|
|
399
399
|
...(summary.classifierNoRuling !== undefined ? { classifierNoRuling: summary.classifierNoRuling } : {}),
|
|
400
400
|
};
|
|
401
401
|
}
|
|
402
|
-
export function parkRowCarriesSkillCardFacts(stored) {
|
|
403
|
-
if (typeof stored !== "object" || stored === null || !("kind" in stored) || stored.kind !== "tool_approval")
|
|
404
|
-
return false;
|
|
405
|
-
const digest = "boundGrantDigest" in stored ? stored.boundGrantDigest : undefined;
|
|
406
|
-
return typeof digest === "string" || ("classifierNoRuling" in stored && stored.classifierNoRuling !== undefined);
|
|
407
|
-
}
|
|
408
402
|
export const DenialLimitFallbackSchema = z
|
|
409
403
|
.object({
|
|
410
404
|
consecutive: z.number().int().nonnegative(),
|
|
@@ -667,9 +661,15 @@ export function buildReplayFrame(row, nowMs) {
|
|
|
667
661
|
return undefined;
|
|
668
662
|
return buildApprovalRequestFrame({ askId: row.askId, taskId: row.taskId, approvalId: row.askId, card: stored.card, expiresAtMs: row.expiresAtMs, nowMs, live: stored.liveFacts });
|
|
669
663
|
}
|
|
664
|
+
export function askDemandsAPerson(legRequest, recordCard) {
|
|
665
|
+
return legRequest?.requiresRealApproval === true || recordCard?.risk.requiresRealApproval === true;
|
|
666
|
+
}
|
|
667
|
+
const UNREADABLE_CARD_DEMANDS_A_PERSON = { risk: { requiresRealApproval: true } };
|
|
668
|
+
export function storedPersonDemandRecord(cardJson) {
|
|
669
|
+
return readStoredCardEnvelope(cardJson)?.card ?? UNREADABLE_CARD_DEMANDS_A_PERSON;
|
|
670
|
+
}
|
|
670
671
|
export function storedCardDemandsAPerson(cardJson) {
|
|
671
|
-
|
|
672
|
-
return parsed === undefined || parsed.card.risk.requiresRealApproval;
|
|
672
|
+
return askDemandsAPerson(undefined, storedPersonDemandRecord(cardJson));
|
|
673
673
|
}
|
|
674
674
|
export function buildApprovalPreamble(rows, nowMs, replayMax) {
|
|
675
675
|
const frames = [];
|
package/dist/audit.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type StoredPromptEpoch } from "@sema-agent/core";
|
|
2
2
|
import type { Pool } from "mysql2/promise";
|
|
3
3
|
import type { Pool as PgPool } from "pg";
|
|
4
4
|
import { type LocalSessionRows } from "./plugins/local-session-window.js";
|
|
@@ -30,31 +30,12 @@ export interface SessionAudit {
|
|
|
30
30
|
* the key entirely (undefined). */
|
|
31
31
|
promptEpoch?: StoredPromptEpoch;
|
|
32
32
|
}
|
|
33
|
-
/**
|
|
34
|
-
* 会话视图体顶层 additive 键 `agent` —— **这只会话是哪只委派子代的转录**(`GET /v1/sessions/:id`;读法:终端侧链投影方按 fleet 帧的 `transcriptId`
|
|
35
|
-
* 读同一只会话读面,从体上拿到子代身份,不必再回头翻 fleet 行 —— 行一离场就没有了)。
|
|
36
|
-
*
|
|
37
|
-
* 只投影**存下来的 placement 元组**(`session_meta` 的 `placement_*` 列,首写不可变;店的 `placementOf` 一行 PK 探针),**不另铸**:
|
|
38
|
-
* · `id` = 元组里的 `handle` —— 耐久 agent 行的 `a…` 键,与 fleet 行的 `id`、`GET /v1/agents/roster` 的 `handle` 是同一个串(core 铸元组时 `handle: taskId`);
|
|
39
|
-
* · `parentSessionId` / `rootSessionId` = 元组里的同名格(派它的会话 / 委派树的根会话);
|
|
40
|
-
* · 元组缺哪一格这里就缺哪一格(present-iff;不铸空串);元组的 `scope`(租户键)/ `placedAt` / `kind` 不上 wire。
|
|
41
|
-
* 键在场 = 这是一只委派子代的转录(哪怕元组只存了 `kind`,那时 `agent` 是 `{}`);键缺席 = 普通会话,**或这台部署的店不存 placement 元组**
|
|
42
|
-
* (local 后端:子代转录停驻前住私有短 TTL 店、停驻时拷进来的那一份是普通会话行 —— 事实没存就不报,消费方在 fleet 行 / `GET /v1/agents/roster` 上本就有 `id`)。
|
|
43
|
-
* 元组由引擎在**后台**委派子代(带耐久 agent 行、店声明了 placements)建会话时铸;同步委派的子代转录不落宿主店,没有元组。
|
|
44
|
-
*/
|
|
45
|
-
export interface SessionAgent {
|
|
46
|
-
id?: string;
|
|
47
|
-
parentSessionId?: string;
|
|
48
|
-
rootSessionId?: string;
|
|
49
|
-
}
|
|
50
|
-
/** {@link SessionAgent} 的唯一投影:店答出的 placement 元组 → 体上的 `agent` 值;`undefined`(普通会话 / 店没有这一面)⇒ 调用方不铸键。 */
|
|
51
|
-
export declare function sessionAgentOf(placement: SessionPlacementRecord | undefined): SessionAgent | undefined;
|
|
52
33
|
/**
|
|
53
34
|
* 用户消息行的**回退手柄**表:用户条目 id → 回退时交给 `resumeAt` 的那个 `event_id`(R8 用户消息锚按 run 的 taskId 键入)。
|
|
54
35
|
* 真源 = `resume_anchor`(锚店 `listBySession` 的读数),本表只是它对**一位读者**的一次投影。
|
|
55
36
|
*/
|
|
56
37
|
export type RewindHandles = ReadonlyMap<string, string>;
|
|
57
|
-
/** 会话视图读口(`ServiceDeps.sessionAudit`)的读者参数:`reader` = 读这张视图的**已验身份**(无身份 = `null`)。给了 ⇒ 这是一次**视图读**:用户消息行投回退手柄、体顶层投 `agent`(
|
|
38
|
+
/** 会话视图读口(`ServiceDeps.sessionAudit`)的读者参数:`reader` = 读这张视图的**已验身份**(无身份 = `null`)。给了 ⇒ 这是一次**视图读**:用户消息行投回退手柄、体顶层投 `agent`(`session-agent.ts` 的 `readSessionAgent`);
|
|
58
39
|
* 不给(只要归属的调用方:memory-status / 自助抹除)⇒ 两样投影的读都不付。 */
|
|
59
40
|
export interface SessionAuditReadOpts {
|
|
60
41
|
readonly reader: string | null;
|
package/dist/audit.js
CHANGED
|
@@ -6,15 +6,6 @@ import { redactModelText } from "./observability/run-terminal.js";
|
|
|
6
6
|
import { redactDeepWith } from "./trace/redact.js";
|
|
7
7
|
import { projectToolStructured } from "./trace/tool-output-media.js";
|
|
8
8
|
import { INJECTED_BLOCK_RE, isAttachmentFrame } from "./user-authored-text.js";
|
|
9
|
-
export function sessionAgentOf(placement) {
|
|
10
|
-
if (placement === undefined)
|
|
11
|
-
return undefined;
|
|
12
|
-
return {
|
|
13
|
-
...(placement.handle !== undefined ? { id: placement.handle } : {}),
|
|
14
|
-
...(placement.parentSessionId !== undefined ? { parentSessionId: placement.parentSessionId } : {}),
|
|
15
|
-
...(placement.rootSessionId !== undefined ? { rootSessionId: placement.rootSessionId } : {}),
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
9
|
export function buildRewindHandles(anchors, reader) {
|
|
19
10
|
const byEntry = new Map();
|
|
20
11
|
for (const a of anchors) {
|
|
@@ -416,7 +416,7 @@ export function createExecutionEnv(ctx) {
|
|
|
416
416
|
});
|
|
417
417
|
}
|
|
418
418
|
else if (config.remoteExec?.provider === "host") {
|
|
419
|
-
const gitBaseEnv = new RemoteHostExecutionEnv({
|
|
419
|
+
const gitBaseEnv = new RemoteHostExecutionEnv({ workspaceDir: config.worktreeIsolation.repoRoot, inheritEnv: "all", backgroundShell: config.requirePrincipal !== true });
|
|
420
420
|
executionEnvFactory = withWorktreeIsolation(executionEnvFactory, {
|
|
421
421
|
repoRoot: config.worktreeIsolation.repoRoot,
|
|
422
422
|
...(config.worktreeIsolation.allowedRoots ? { allowedRoots: config.worktreeIsolation.allowedRoots } : {}),
|
|
@@ -25,7 +25,6 @@ import type { ServiceWorkflowJournalStore, SessionCaptureRecordSource, StoreBack
|
|
|
25
25
|
import type { createSessionStore } from "../plugins/session-store.js";
|
|
26
26
|
import type { QuestionCoordinator } from "../question.js";
|
|
27
27
|
import type { ToolApprovalCoordinator } from "../tool-approval.js";
|
|
28
|
-
import { type PeerInboundFace } from "../peer-inbound-face.js";
|
|
29
28
|
import type { MemoryBackend } from "@sema-agent/core";
|
|
30
29
|
import type { MemorySyncRunner } from "../memory-sync-client.js";
|
|
31
30
|
import type { MailboxRecipientLifecycle } from "../plugins/mailbox-recipient-lifecycle.js";
|
|
@@ -281,10 +280,16 @@ export interface EngineNoticeMetricsSeat {
|
|
|
281
280
|
}
|
|
282
281
|
export declare function createEngineNoticeForwarder(logger: {
|
|
283
282
|
warn: (msg: string, fields?: Record<string, unknown>) => void;
|
|
284
|
-
}, router?: EngineNoticeRouter, metrics?: EngineNoticeMetricsSeat
|
|
285
|
-
/**
|
|
286
|
-
*
|
|
287
|
-
|
|
283
|
+
}, router?: EngineNoticeRouter, metrics?: EngineNoticeMetricsSeat): NonNullable<RunnerDeps["onNotice"]>;
|
|
284
|
+
/**
|
|
285
|
+
* [ref](core 7.62.0 [ref]):**进程缺省通告席** —— 引擎在没有自己席的地方说的话(四个无席收值口:品牌 brain 构造 / 一次请求的路由读 / 对端拨号 /
|
|
286
|
+
* 直建蒸馏座;core 读法:在场的席恒赢,缺席才落这里)交给与有席通告**同一只**转发体({@link createEngineNoticeForwarder}:先结构化 `engine_notice` 日志、再分流)。
|
|
287
|
+
* 修前这些话落 `console.warn` 裸句 —— 同一种事实两种写法。构造引擎的两个进程入口(`boot/stage-01-config.ts` / `run-local.ts`)在 logger 建起之后各装一次;
|
|
288
|
+
* 装之前(声明自有凭据名、读配置那几步)引擎若无席说话仍落 `console.warn`(那几步不建 brain,四个收值口一个都不经过)。
|
|
289
|
+
*/
|
|
290
|
+
export declare function installDefaultEngineNoticeSeat(logger: {
|
|
291
|
+
warn: (msg: string, fields?: Record<string, unknown>) => void;
|
|
292
|
+
}, metrics?: EngineNoticeMetricsSeat): void;
|
|
288
293
|
/**
|
|
289
294
|
* `onNotice` **席**的装配判据本身 —— 「`warn` 真在场才铸席」这一条从此是**一个可直接调用的函数**,
|
|
290
295
|
* 不是散在装配点、只能靠扫源码文本去猜的一行三元(codex 交叉复审 R1-[medium],验真后修)。
|
package/dist/boot/runner-deps.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createFileWorkflowScriptStore } from "@sema-agent/core";
|
|
1
|
+
import { createFileWorkflowScriptStore, setDefaultEngineNoticeSeat } from "@sema-agent/core";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { lkgReadDenyRows } from "../config-lkg.js";
|
|
4
4
|
import { auditReadDenyAnchoring } from "./read-deny-anchor-audit.js";
|
|
@@ -14,7 +14,6 @@ import { makeLoadProjectMemory, makeProbeInstructionSources } from "../project-m
|
|
|
14
14
|
import { createInstructionSourceSeat } from "../instruction-source-notice.js";
|
|
15
15
|
import { workflowModelAllowlistFor } from "../task-workflow.js";
|
|
16
16
|
import { buildCrossSessionInboundLayers, crossSessionDialogExpiryOf } from "../cross-session-settings.js";
|
|
17
|
-
import { defaultPeerInboundFace } from "../peer-inbound-face.js";
|
|
18
17
|
import { createPeerDirectory } from "../peer-directory.js";
|
|
19
18
|
import { storeFormRefusalInIncident } from "../memory-store-refusal.js";
|
|
20
19
|
import { coreFileHistoryCaptureSeat } from "../file-history-capture.js";
|
|
@@ -48,7 +47,7 @@ export const PROMPT_CACHE_BREAK_CAUSE_LEVEL = {
|
|
|
48
47
|
};
|
|
49
48
|
const PROMPT_CACHE_BREAK_LEVELS = new Map(Object.entries(PROMPT_CACHE_BREAK_CAUSE_LEVEL));
|
|
50
49
|
const CEILING_ORIGINS = new Set(["walltime", "turns"]);
|
|
51
|
-
export function createEngineNoticeForwarder(logger, router = defaultEngineNoticeRouter, metrics
|
|
50
|
+
export function createEngineNoticeForwarder(logger, router = defaultEngineNoticeRouter, metrics) {
|
|
52
51
|
return (notice) => {
|
|
53
52
|
const detail = redactNoticeRuleText(notice.code, notice.detail);
|
|
54
53
|
const message = redactNoticeMessage(notice.message);
|
|
@@ -58,10 +57,12 @@ export function createEngineNoticeForwarder(logger, router = defaultEngineNotice
|
|
|
58
57
|
if (typeof origin === "string" && CEILING_ORIGINS.has(origin))
|
|
59
58
|
metrics.inc("limit_ceiling_after_answer_total", { origin });
|
|
60
59
|
}
|
|
61
|
-
peerInbound.observe(notice);
|
|
62
60
|
router.route(notice);
|
|
63
61
|
};
|
|
64
62
|
}
|
|
63
|
+
export function installDefaultEngineNoticeSeat(logger, metrics) {
|
|
64
|
+
setDefaultEngineNoticeSeat(createEngineNoticeForwarder(logger, defaultEngineNoticeRouter, metrics));
|
|
65
|
+
}
|
|
65
66
|
export function createEngineNoticeSeat(logger, router = defaultEngineNoticeRouter) {
|
|
66
67
|
const warn = logger?.warn?.bind(logger);
|
|
67
68
|
return warn ? { onNotice: createEngineNoticeForwarder({ warn }, router) } : {};
|
|
@@ -66,7 +66,7 @@ export interface SessionFacesCtx {
|
|
|
66
66
|
export declare function createSessionFaces(ctx: SessionFacesCtx): {
|
|
67
67
|
ownerAware: OwnerAwareSessionStore;
|
|
68
68
|
sessionAudit: ((sessionId: string, opts?: SessionAuditReadOpts) => Promise<{
|
|
69
|
-
agent?: import("../
|
|
69
|
+
agent?: import("../session-agent.js").SessionAgent | undefined;
|
|
70
70
|
owner: string | null;
|
|
71
71
|
sessionId: string;
|
|
72
72
|
createdAt: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { buildRewindHandles, buildSessionAudit, buildSessionAuditLocal, buildSessionAuditPg
|
|
1
|
+
import { buildRewindHandles, buildSessionAudit, buildSessionAuditLocal, buildSessionAuditPg } from "../audit.js";
|
|
2
|
+
import { readSessionAgent } from "../session-agent.js";
|
|
2
3
|
import { makeDegenerateInstrument } from "../degenerate-instrument.js";
|
|
3
4
|
import { PlanCacheProbe } from "../plan-cache-probe.js";
|
|
4
5
|
import { purgeScratchpadDir } from "../env-facts.js";
|
|
@@ -29,9 +30,8 @@ export function createSessionFaces(ctx) {
|
|
|
29
30
|
: await buildSessionAuditLocal(auditLocalRows, sessionId, handles);
|
|
30
31
|
if (!audit)
|
|
31
32
|
return undefined;
|
|
32
|
-
const [head,
|
|
33
|
+
const [head, agent] = await Promise.all([headOf?.(sessionId), opts !== undefined ? readSessionAgent(ownerAware, sessionId) : undefined]);
|
|
33
34
|
const owner = head?.owner;
|
|
34
|
-
const agent = sessionAgentOf(placement);
|
|
35
35
|
return { ...audit, owner: owner ?? null, ...(agent !== undefined ? { agent } : {}) };
|
|
36
36
|
}
|
|
37
37
|
: undefined;
|
package/dist/boot/shutdown.d.ts
CHANGED
|
@@ -98,7 +98,7 @@ export interface ShutdownCtx {
|
|
|
98
98
|
/**
|
|
99
99
|
* [ref]:被扣跨会话来信的收信方决断面(`peer-inbound-face.ts`,进程单例)的关停口 —— CC `HBe` 的宿主半场(core 不调 `PeerHeldQueue.shutdown()`)。
|
|
100
100
|
* hardShutdown 起点与审批面收尾**并行**调它、`backend.close()` 之前等它(结清要走信箱店的 CAS 与回执投递):本进程仍被扣的信按 core 的规则
|
|
101
|
-
* 带 `expired` 回执结清(reason `shutdown`)。缺席 = 本装配不接(测试装配)。
|
|
101
|
+
* 带 `expired` 回执结清(reason `shutdown`),core 等回执落箱再兑现(没落的点名 seq ⇒ 读数 `receiptFaults`)。缺席 = 本装配不接(测试装配)。
|
|
102
102
|
*/
|
|
103
103
|
peerInbound?: {
|
|
104
104
|
shutdown(): Promise<PeerInboundShutdownReading>;
|
package/dist/boot/shutdown.js
CHANGED
|
@@ -98,8 +98,10 @@ export function installShutdownHandlers(ctx) {
|
|
|
98
98
|
: (async () => {
|
|
99
99
|
const deadline = new AbortController();
|
|
100
100
|
const settled = peerInbound.shutdown().then((reading) => {
|
|
101
|
-
if (reading.
|
|
102
|
-
logger.info("peer_inbound_settled_at_shutdown", { queues: reading.queues,
|
|
101
|
+
if (reading.cards > 0)
|
|
102
|
+
logger.info("peer_inbound_settled_at_shutdown", { queues: reading.queues, cards: reading.cards });
|
|
103
|
+
for (const fault of reading.receiptFaults)
|
|
104
|
+
logger.warn("peer_inbound_shutdown_receipt_unlanded", { err: seatText("operator-log-text", fault) });
|
|
103
105
|
}, (err) => logger.warn("peer_inbound_shutdown_failed", { err: seatText("operator-log-text", String(err)) }));
|
|
104
106
|
const timedOut = delay(Math.max(0, graceDeadlineMs - Date.now()), undefined, { signal: deadline.signal }).then(() => "deadline", () => "done");
|
|
105
107
|
const winner = await Promise.race([settled.then(() => "done"), timedOut]);
|
|
@@ -15,6 +15,7 @@ import { setRedactionObserver } from "../trace/redact.js";
|
|
|
15
15
|
import { installOutboundDispatcher } from "../outbound-dispatcher.js";
|
|
16
16
|
import { provisionSemaConfigDir } from "../sema-config-dir.js";
|
|
17
17
|
import { bindOwnCredentialsConfig, declareServerWithheldEnv } from "../server-secret-env.js";
|
|
18
|
+
import { installDefaultEngineNoticeSeat } from "./runner-deps.js";
|
|
18
19
|
export function runStage01() {
|
|
19
20
|
declareServerWithheldEnv();
|
|
20
21
|
const config = loadConfig();
|
|
@@ -27,6 +28,7 @@ export function runStage01() {
|
|
|
27
28
|
const metrics = createMetrics();
|
|
28
29
|
installFailOpenRecorder({ logger, metrics, redactDetail: redactSecrets });
|
|
29
30
|
installSecretEnvScrubRecorder({ logger, metrics });
|
|
31
|
+
installDefaultEngineNoticeSeat(logger, metrics);
|
|
30
32
|
installFileHistoryFailureRecorder({ logger, metrics });
|
|
31
33
|
logConfigDiagnostics(logger);
|
|
32
34
|
for (const w of drainNumEnvWarnings())
|
|
@@ -172,7 +172,7 @@ export async function runStage07(ctx) {
|
|
|
172
172
|
.catch((e) => logger.warn("web_search_probe_failed", { provider: webSearchCfg.provider, error: seatText("operator-log-text", e instanceof Error ? e.message : String(e)) }));
|
|
173
173
|
}
|
|
174
174
|
const ensureChildSessionDurable = checkpointStore && resolveSubagentTranscriptTier(backgroundAgentStore !== undefined, sessionStore) === "full"
|
|
175
|
-
? (sessionId,
|
|
175
|
+
? (sessionId, given) => attestChildSessionDurable(sessionStore, sessionId, given)
|
|
176
176
|
: undefined;
|
|
177
177
|
const webFetchSummarize = createWebFetchSummarizeLane({ brain, config, getKeyResolver: () => webAdmin.getKeyResolver(), logger });
|
|
178
178
|
const scenarioDeps = {
|
|
@@ -18,7 +18,7 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
|
|
|
18
18
|
}>) | undefined;
|
|
19
19
|
webAdminJwtVerifier: import("../auth-bridge.js").WebAdminJwtVerifier | undefined;
|
|
20
20
|
sessionAudit: ((sessionId: string, opts?: import("../audit.js").SessionAuditReadOpts) => Promise<{
|
|
21
|
-
agent?: import("../
|
|
21
|
+
agent?: import("../session-agent.js").SessionAgent | undefined;
|
|
22
22
|
owner: string | null;
|
|
23
23
|
sessionId: string;
|
|
24
24
|
createdAt: string;
|
|
@@ -17,7 +17,7 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
|
|
|
17
17
|
}>) | undefined;
|
|
18
18
|
webAdminJwtVerifier: import("../auth-bridge.js").WebAdminJwtVerifier | undefined;
|
|
19
19
|
sessionAudit: ((sessionId: string, opts?: import("../audit.js").SessionAuditReadOpts) => Promise<{
|
|
20
|
-
agent?: import("../
|
|
20
|
+
agent?: import("../session-agent.js").SessionAgent | undefined;
|
|
21
21
|
owner: string | null;
|
|
22
22
|
sessionId: string;
|
|
23
23
|
createdAt: string;
|