@sema-agent/server 7.51.0 → 7.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +83 -20
- package/dist/adoption/plan.d.ts +4 -4
- package/dist/adoption/plan.js +11 -0
- package/dist/approval-card.d.ts +26 -2
- package/dist/approval-card.js +9 -0
- package/dist/approval-hmac.d.ts +1 -1
- package/dist/approval-reconciler.d.ts +2 -2
- package/dist/approval-reconciler.js +1 -1
- package/dist/bench/l8/artifact.d.ts +1 -1
- package/dist/bench/s1/arms.d.ts +1 -1
- package/dist/bench/s1/live-deps.d.ts +1 -1
- package/dist/bench/s1/oracle.d.ts +1 -1
- package/dist/bench/s1/row.d.ts +1 -1
- package/dist/boot/budget-tracing.d.ts +1 -1
- package/dist/boot/budget-tracing.js +2 -5
- package/dist/boot/coordinators.js +3 -1
- package/dist/boot/deferred-sandbox-path-env.d.ts +1 -1
- package/dist/boot/engine-lease.d.ts +1 -1
- package/dist/boot/execution-env.d.ts +1 -1
- package/dist/boot/governance-seams.d.ts +2 -2
- package/dist/boot/leader.d.ts +1 -1
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/memory-consolidation.d.ts +6 -6
- package/dist/boot/org-memory.d.ts +1 -1
- package/dist/boot/parked-revive-gate.d.ts +3 -3
- package/dist/boot/reapers.d.ts +2 -2
- package/dist/boot/reapers.js +17 -4
- package/dist/boot/resolve-spec.d.ts +12 -0
- package/dist/boot/resolve-spec.js +29 -7
- package/dist/boot/retention-lane.d.ts +2 -2
- package/dist/boot/runner-deps.d.ts +17 -7
- package/dist/boot/runner-deps.js +4 -2
- package/dist/boot/runtime-caps.d.ts +23 -0
- package/dist/boot/runtime-caps.js +24 -2
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +14 -3
- package/dist/boot/stores.js +31 -1
- package/dist/boot/task-list-lane.d.ts +2 -2
- package/dist/brain.js +51 -31
- package/dist/budget.d.ts +2 -2
- package/dist/capabilities/center-plugins.d.ts +1 -1
- package/dist/capabilities/collab-workflows.d.ts +1 -1
- package/dist/capabilities/hands-lane.d.ts +3 -3
- package/dist/capabilities/scenarios.d.ts +3 -3
- package/dist/capabilities/skills.d.ts +1 -1
- package/dist/config-center/hot-keys-registry.js +5 -0
- package/dist/config-center/http-client.d.ts +2 -2
- package/dist/config-center/read-face.d.ts +1 -1
- package/dist/config-center/restart-signal.d.ts +1 -1
- package/dist/config-types.d.ts +78 -18
- package/dist/config.js +45 -10
- package/dist/deployment-governance.d.ts +1 -1
- package/dist/device-enrollment.d.ts +2 -2
- package/dist/device-store.d.ts +3 -3
- package/dist/device-ws-protocol.d.ts +2 -2
- package/dist/env-facts.d.ts +1 -1
- package/dist/execution-lane-caps.d.ts +1 -1
- package/dist/fleet/fleet-bus.d.ts +1 -1
- package/dist/fleet/fleet-reconciler.d.ts +1 -1
- package/dist/fleet/fleet-terminal-window.d.ts +1 -1
- package/dist/http/active-run-conflict.d.ts +2 -2
- package/dist/http/principal-gate.d.ts +1 -1
- package/dist/http/route-ctx.d.ts +24 -4
- package/dist/http/routes/admin-memory-optout.d.ts +48 -0
- package/dist/http/routes/admin-memory-optout.js +75 -0
- package/dist/http/routes/agents-roster.d.ts +1 -1
- package/dist/http/routes/approvals-assistant.js +21 -21
- package/dist/http/routes/capabilities.js +20 -9
- package/dist/http/routes/diagnostics.d.ts +1 -1
- package/dist/http/routes/leader.d.ts +1 -1
- package/dist/http/routes/memory-bundle.d.ts +1 -1
- package/dist/http/routes/memory-bundle.js +3 -1
- package/dist/http/routes/memory-compliance.d.ts +4 -4
- package/dist/http/routes/memory-origin.d.ts +4 -4
- package/dist/http/routes/memory-origin.js +3 -1
- package/dist/http/routes/memory-policy.js +6 -2
- package/dist/http/routes/retention-ops.d.ts +1 -1
- package/dist/http/routes/rules.d.ts +5 -2
- package/dist/http/routes/rules.js +84 -5
- package/dist/http/routes/run-memory-capture.d.ts +34 -0
- package/dist/http/routes/run-memory-capture.js +97 -0
- package/dist/http/routes/runs.d.ts +1 -0
- package/dist/http/routes/runs.js +212 -6
- package/dist/http/routes/session-sync.d.ts +4 -6
- package/dist/http/routes/session-sync.js +60 -47
- package/dist/http/routes/sessions.js +61 -188
- package/dist/http/routes/shared-memory.d.ts +1 -1
- package/dist/http/routes/tasks.js +1 -1
- package/dist/http/routes/workflows.js +19 -0
- package/dist/http/server.d.ts +35 -13
- package/dist/http/server.js +105 -12
- package/dist/http/verify-rounds.d.ts +1 -1
- package/dist/http/wire-types.d.ts +41 -8
- package/dist/leader/endpoint.d.ts +1 -1
- package/dist/leader/leader.d.ts +1 -1
- package/dist/leader/repair-oracle.d.ts +2 -2
- package/dist/leader/repair-oracle.js +1 -1
- package/dist/main.js +13 -6
- package/dist/memory-bundle-engine.d.ts +1 -1
- package/dist/memory-operator-faces.d.ts +5 -5
- package/dist/memory-origin-fence.d.ts +50 -0
- package/dist/memory-origin-fence.js +15 -0
- package/dist/memory-posture.d.ts +13 -1
- package/dist/memory-posture.js +1 -0
- package/dist/memory-scope.d.ts +6 -1
- package/dist/memory-scope.js +3 -2
- package/dist/model-select.d.ts +2 -2
- package/dist/observability/fail-open.d.ts +17 -5
- package/dist/observability/fail-open.js +17 -5
- package/dist/orchestration/hardened-vm-runner.d.ts +2 -2
- package/dist/orchestration/workflow-completion-inbox.d.ts +2 -2
- package/dist/plugins/background-shell-support.d.ts +2 -2
- package/dist/plugins/blob-backend.d.ts +7 -2
- package/dist/plugins/blob-backend.js +22 -18
- package/dist/plugins/caching-session-store.d.ts +7 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +2 -2
- package/dist/plugins/file-history-store-sql.d.ts +170 -0
- package/dist/plugins/file-history-store-sql.js +827 -0
- package/dist/plugins/file-run-store.d.ts +44 -5
- package/dist/plugins/file-run-store.js +85 -13
- package/dist/plugins/fork-routing-session-store.d.ts +1 -1
- package/dist/plugins/fork-routing-session-store.js +2 -2
- package/dist/plugins/leader-run-store-sql.d.ts +1 -1
- package/dist/plugins/local-session-store.d.ts +9 -1
- package/dist/plugins/memory-embedder-fingerprint.d.ts +1 -1
- package/dist/plugins/memory-optout-grant-store-sql.d.ts +139 -0
- package/dist/plugins/memory-optout-grant-store-sql.js +157 -0
- package/dist/plugins/memory-origin-law.d.ts +2 -2
- package/dist/plugins/memory-run-store.d.ts +4 -3
- package/dist/plugins/memory-run-store.js +2 -1
- package/dist/plugins/permission-rule-store-file.d.ts +16 -6
- package/dist/plugins/permission-rule-store-file.js +77 -13
- package/dist/plugins/permission-rule-store-sql.d.ts +156 -8
- package/dist/plugins/permission-rule-store-sql.js +123 -21
- package/dist/plugins/pg-pool.js +59 -0
- package/dist/plugins/pg-safe-json.d.ts +1 -1
- package/dist/plugins/remote-env-device.d.ts +1 -1
- package/dist/plugins/remote-env-k8s.d.ts +1 -1
- package/dist/plugins/retention-lane-store-sql.d.ts +3 -3
- package/dist/plugins/retention-store-sql.d.ts +7 -7
- package/dist/plugins/retention-store-sql.js +6 -0
- package/dist/plugins/run-store-sql.d.ts +9 -3
- package/dist/plugins/run-store-sql.js +2 -2
- package/dist/plugins/send-user-file.d.ts +1 -1
- package/dist/plugins/session-placement.d.ts +1 -1
- package/dist/plugins/sql-errors.d.ts +1 -1
- package/dist/plugins/store-backend.d.ts +59 -29
- package/dist/plugins/store-backend.js +75 -15
- package/dist/plugins/store-contracts.d.ts +5 -3
- package/dist/plugins/task-list-store-sql.d.ts +1 -1
- package/dist/plugins/tidb-pool.js +59 -0
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/prompts-domain-validate.js +1 -1
- package/dist/resource-suspend.d.ts +1 -1
- package/dist/resource-window.d.ts +3 -3
- package/dist/rules-consent.d.ts +231 -9
- package/dist/rules-consent.js +411 -13
- package/dist/run-local.js +1 -0
- package/dist/runs.d.ts +15 -1
- package/dist/runs.js +19 -0
- package/dist/runtime-caps-resolver.d.ts +36 -1
- package/dist/runtime-caps-resolver.js +25 -0
- package/dist/runtime-governance.d.ts +1 -1
- package/dist/security.d.ts +5 -5
- package/dist/security.js +1 -1
- package/dist/session-sync.d.ts +52 -37
- package/dist/session-sync.js +19 -16
- package/dist/shared-memory-scope-authorizer.d.ts +1 -1
- package/dist/sighup-idle.d.ts +1 -1
- package/dist/spec-fields.d.ts +22 -1
- package/dist/spec-fields.js +11 -0
- package/dist/store-live-probe.d.ts +2 -2
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-cwd.d.ts +1 -1
- package/dist/task-mcp.d.ts +3 -3
- package/dist/task-settings.d.ts +3 -3
- package/dist/task-workflow.d.ts +1 -1
- package/dist/text-bidi.d.ts +2 -2
- package/dist/tool-approval.d.ts +191 -9
- package/dist/tool-approval.js +248 -29
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/engine-notice-wire.d.ts +6 -4
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/ledger-events.d.ts +1 -1
- package/dist/trace/ledger-sink.d.ts +2 -2
- package/dist/trace/project.d.ts +3 -2
- package/dist/trace/project.js +1 -0
- package/dist/turn-activity.d.ts +1 -1
- package/dist/wall-clock-jump-guard.d.ts +3 -3
- package/package.json +2 -2
- package/dist/http/tar.d.ts +0 -17
- package/dist/http/tar.js +0 -53
- package/dist/http/workspace-content.d.ts +0 -11
- package/dist/http/workspace-content.js +0 -20
- package/dist/plugins/file-snapshot-store-sql.d.ts +0 -170
- package/dist/plugins/file-snapshot-store-sql.js +0 -213
|
@@ -91,7 +91,7 @@ export declare function assertStaticWiringConsistent(facts: StaticWiringFacts, l
|
|
|
91
91
|
* 走认证(全局门),不进本臂。
|
|
92
92
|
*
|
|
93
93
|
* 判源=TCP 对端地址(`req.socket.remoteAddress`),**非任何头**——XFF 类头在无反代的 turnkey 形上本就
|
|
94
|
-
*
|
|
94
|
+
* 是自造面;有反代的部署必然配 token(反代后 remoteAddress 恒 loopback,门自然放行,语义仍对:该形的
|
|
95
95
|
* 边界由反代+token 承担)。`remoteAddress` 缺席(socket 已断等边缘)⇒ 判拒(fail-closed,安全轴方向)。
|
|
96
96
|
*/
|
|
97
97
|
export declare function zeroCredentialLoopbackDenied(remoteAddress: string | undefined, config: {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* design/158 A9 域模块 —— v2 leader 端点(design/50)。
|
|
3
3
|
*
|
|
4
4
|
* 路由:`POST /v1/leader`(异步,形同 `/v1/runs`)· `GET /v1/leader/:id`。整域由 `deps.leaderEndpoint`
|
|
5
|
-
* 的存在与否开关;身份层与 `/v1/runs` 同款(BL-4
|
|
5
|
+
* 的存在与否开关;身份层与 `/v1/runs` 同款(BL-4:此前整条路跳过 authorize/requirePrincipal 且不记 owner,
|
|
6
6
|
* 任何持服务令牌者能提交/读任意租户的 leader run),owner 用 crypto 验证过的 `gatedPrincipal`。本域零可变状态。
|
|
7
7
|
*/
|
|
8
8
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* · 记忆引擎整个没接线(`MEMORY_ENGINE=off` / 多租 file 底座等暗态);
|
|
28
28
|
* · 引擎接了但后端没有 bundle 复合面(pg/tidb 孪生)—— 这一支在**挂载期**就判掉了
|
|
29
29
|
* (`createMemoryBundleFaces` 返回 undefined ⇒ 两口整个不挂),能力位 `memoryBundle` 因此与「路由真能用」
|
|
30
|
-
* 严格同真。🔴 首版把它当运行期事实(照常挂载、让 core 每次抛 409/422),被 codex
|
|
30
|
+
* 严格同真。🔴 首版把它当运行期事实(照常挂载、让 core 每次抛 409/422),被 codex 交叉复审驳倒并改此形:
|
|
31
31
|
* 面在不在是一次**属性检查**、boot 期判得出、且是**永久**缺席 ⇒ 照常挂着就是一个恒假的 yes,
|
|
32
32
|
* 壳会据此给用户开出一条走不通的迁移流程。
|
|
33
33
|
* ⚠️ 与**运行期**拒别混:后端**有**复合面但托管链 chain-degraded 时,core 照样抛
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
|
+
import { buildOriginFence, readOriginAware } from "../../memory-origin-fence.js";
|
|
3
4
|
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
4
5
|
export const MEMORY_BUNDLE_EXPORT_PATH = "/v1/memory/export";
|
|
5
6
|
export const MEMORY_BUNDLE_IMPORT_PATH = "/v1/memory/import";
|
|
@@ -93,7 +94,8 @@ async function handleMemoryBundleBody(req, res, url, ctx, miss) {
|
|
|
93
94
|
}
|
|
94
95
|
try {
|
|
95
96
|
const bundle = await engineExport(parsed.data.scopes);
|
|
96
|
-
|
|
97
|
+
const fence = buildOriginFence(bundle.entries, readOriginAware(new URL(req.url ?? "", "http://x").searchParams));
|
|
98
|
+
sendJson(res, 200, fence.withheldOriginTagged !== undefined ? { ...bundle, entries: fence.entries, withheldOriginTagged: fence.withheldOriginTagged } : bundle);
|
|
97
99
|
}
|
|
98
100
|
catch (err) {
|
|
99
101
|
if (!sentClassifiedFailure(res, ctx, "export", err))
|
|
@@ -73,19 +73,19 @@
|
|
|
73
73
|
*
|
|
74
74
|
* ── 改写门(`isCredentialGatedRewrite`):**两口都进** ──────────────────────────────────────────
|
|
75
75
|
* 该门自 #277 起的判据是「持久改写 **或** 授权唯一输入是 principal 头、且**没有属主门兜底**的治理面」:
|
|
76
|
-
* · `POST /v1/memory/erase`
|
|
76
|
+
* · `POST /v1/memory/erase` 落在**前**一项上,而且是最深的一种:它把条目从本店**删掉**。自造一个在册
|
|
77
77
|
* operator 头即可对任一租户执行一次不可撤销的删除 —— 无凭证部署上缺的正是「上游会验这个头」这个前提。
|
|
78
78
|
* · `GET …/:entryId/provenance` 落在**后**一项上。🔴 这一条**推翻了 design/316 §3 的原始裁定**
|
|
79
|
-
* (稿子把它豁免在门外,理由是「爆炸半径=一条」);codex
|
|
79
|
+
* (稿子把它豁免在门外,理由是「爆炸半径=一条」);codex 交叉复审 [high] 指出该理由站不住,亲验后采信:
|
|
80
80
|
* ⑴ 稿子援引来类比的两条豁免读(`GET /v1/adoption/:id` / `GET /v1/rules`)在门外的**真正**原因是
|
|
81
81
|
* 它们各有**属主门**兜底,不是「读得少」;本口的授权判据就只有 `explicitOperatorOk`,过了就直接答;
|
|
82
|
-
* ⑵
|
|
82
|
+
* ⑵ 无凭证部署上那个头**可自造**,于是「一条」这个上界实际退化成「知道 id 就能读」——拿一个不可猜的
|
|
83
83
|
* id 当唯一的授权,是本仓在别处明令不接受的形;
|
|
84
84
|
* ⑶ 一条账里有:绑定的 scope+slug、贡献会话 id 与污染理由、仓内 ingest 路径与内容哈希、托管链事件。
|
|
85
85
|
* 先例逐字同源:`POST /v1/memory/export` 当初也「按原措辞本不该进」,同一条论证把它收了进来
|
|
86
86
|
* ——「把它留在门外只为守住一句措辞,是把措辞看得比它要保护的东西更重」。
|
|
87
87
|
* 🔴 判据里**刻意不写**「本口 side-effect-free」:那不是这道门的判据。该门拦的是「授权的唯一输入是
|
|
88
|
-
*
|
|
88
|
+
* 一个可自造的头」这件事 —— 用「没有副作用」当豁免理由,正是 #277 那次把 `POST /v1/memory/export`
|
|
89
89
|
* (纯读、跨租户全库)漏在门外的那句话。
|
|
90
90
|
* ⚠️ 代价与补偿如实成文:无凭证部署上两口都答 503 `auth.service_token_required`。补偿是**既有旋钮、
|
|
91
91
|
* 零新增** —— 配 `SERVICE_AUTH_TOKEN`(正路),或本地开发显式 `ALLOW_UNAUTHED_WRITES=true`
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
* · 而这份账**新长一个键**时,静默上 wire 恰恰是危险的那一侧(它是托管面,不是事实陈述面)。
|
|
44
44
|
* ⇒ 显式白名单 + **编译期差集门**(`test/wire-whitelist-exhaustiveness.test.ts` 的 `KnownExcluded`
|
|
45
45
|
* = `entryText`,逐字写理由):core 加一个键 ⇒ TS2344 ⇒ 由人处置,而不是悄悄上 wire 或悄悄漏掉。
|
|
46
|
-
* 🔴 白名单是**深**的,不是只做一层(codex
|
|
47
|
-
* —— 一层白名单 +
|
|
46
|
+
* 🔴 白名单是**深**的,不是只做一层(codex 交叉复审 [high] 抓的第一版病):嵌套的 `origin` 也逐键投影
|
|
47
|
+
* —— 一层白名单 + 一个整只透传的嵌套对象,等于给这条排除留了一个旁路,全文见 {@link buildOriginWire}。
|
|
48
48
|
* 🔴 **剥了不等于不说**:投影补一个 `custodyBytes`(托管字节数)。这是 #196 absence-reports-not-silent-green
|
|
49
49
|
* 在本口的落法 —— operator 判「这个 pending 行还揣着一条记忆吗」只需要这一位;悄悄把整个托管概念从
|
|
50
50
|
* 审计面上抹掉,会让一个**必须由人处置**的崩溃恢复席看上去不存在。
|
|
@@ -57,12 +57,12 @@
|
|
|
57
57
|
*
|
|
58
58
|
* ── 改写门(`isCredentialGatedRewrite`):**三口全进** ──────────────────────────────────────────
|
|
59
59
|
* 该门自 #277 起的判据是「持久改写 **或** 授权唯一输入是 principal 头的跨租户治理读」:
|
|
60
|
-
* · `POST …/clear` 落在**前**一项:它把一条条目的外源披露**永久摘掉**(墓碑 + 同 id 重录)
|
|
60
|
+
* · `POST …/clear` 落在**前**一项:它把一条条目的外源披露**永久摘掉**(墓碑 + 同 id 重录),自造一个
|
|
61
61
|
* 在册 operator 头即可让任意租户的一条外来记忆看上去是本店自产的 —— 而记忆是模型每次挂载都读的面;
|
|
62
62
|
* · `GET …/external` 落在**后**一项,而且是这一族里爆炸半径最大的读:一次调用取回**任意 scope**的
|
|
63
63
|
* 条目 + 每条的完整出处账(贡献会话 id、污染理由、仓内 ingest 路径与内容哈希、托管链事件)。判据与
|
|
64
64
|
* `POST /v1/memory/export`(#277 收进门的那条纯读)逐字同构:它没有属主门兜底,operator 头过了就直接答。
|
|
65
|
-
* · `GET …/clearances` —— 🔴 **本车第一版把它留在门外,codex
|
|
65
|
+
* · `GET …/clearances` —— 🔴 **本车第一版把它留在门外,codex 交叉复审 [high] 推翻,亲验后采信**。
|
|
66
66
|
* 那一版的理由是「投影后不含条目正文,爆炸半径差一个量级」,而这个理由**用错了基准**:本门的判据
|
|
67
67
|
* 从来不是「含不含正文」,而是「授权的**唯一**输入是 principal 头」∧「没有属主门兜底」。按那条判据,
|
|
68
68
|
* 一口**整部署**的清标账(每个租户的 scope+slug、entryId、rev、operator 手写的自由文本 `reason`、
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { ControlPlaneCorruptError } from "@sema-agent/core";
|
|
3
3
|
import { sendJson, sendError } from "../send.js";
|
|
4
|
+
import { buildOriginFence, readOriginAware } from "../../memory-origin-fence.js";
|
|
4
5
|
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
5
6
|
export const MEMORY_ORIGIN_EXTERNAL_PATH = "/v1/memory/origin/external";
|
|
6
7
|
export const MEMORY_ORIGIN_CLEARANCES_PATH = "/v1/memory/origin/clearances";
|
|
@@ -141,7 +142,8 @@ async function handleMemoryOriginBody(req, res, url, ctx, miss) {
|
|
|
141
142
|
return;
|
|
142
143
|
}
|
|
143
144
|
try {
|
|
144
|
-
|
|
145
|
+
const fence = buildOriginFence(await faces.listExternal(scopes), readOriginAware(new URL(req.url ?? "", "http://x").searchParams));
|
|
146
|
+
sendJson(res, 200, { scopes, entries: fence.entries, ...(fence.withheldOriginTagged !== undefined ? { withheldOriginTagged: fence.withheldOriginTagged } : {}) });
|
|
145
147
|
}
|
|
146
148
|
catch (err) {
|
|
147
149
|
if (!sentClassifiedFailure(res, ctx, "external", err))
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { formatUserScope } from "@sema-agent/core";
|
|
2
2
|
import { parseMemorySyncRequest } from "../../memory-sync.js";
|
|
3
3
|
import { sendJson, sendError } from "../send.js";
|
|
4
|
+
import { buildOriginFence, readOriginAware } from "../../memory-origin-fence.js";
|
|
4
5
|
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
5
6
|
const ORG_PREFIX = "org:";
|
|
6
7
|
export const MEMORY_EXPORT_MAX_LIMIT = 500;
|
|
@@ -101,9 +102,11 @@ async function handleMemoryPolicyBody(req, res, url, ctx, miss) {
|
|
|
101
102
|
return;
|
|
102
103
|
}
|
|
103
104
|
try {
|
|
104
|
-
const
|
|
105
|
+
const fence = buildOriginFence(await deps.memoryExport(scope), readOriginAware(new URL(req.url ?? "", "http://x").searchParams));
|
|
106
|
+
const entries = fence.entries;
|
|
107
|
+
const withheld = fence.withheldOriginTagged !== undefined ? { withheldOriginTagged: fence.withheldOriginTagged } : {};
|
|
105
108
|
if (limitRaw === null) {
|
|
106
|
-
sendJson(res, 200, { scope, exportedAt: new Date().toISOString(), entries });
|
|
109
|
+
sendJson(res, 200, { scope, exportedAt: new Date().toISOString(), entries, ...withheld });
|
|
107
110
|
return;
|
|
108
111
|
}
|
|
109
112
|
const sorted = [...entries].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
@@ -116,6 +119,7 @@ async function handleMemoryPolicyBody(req, res, url, ctx, miss) {
|
|
|
116
119
|
exportedAt: new Date().toISOString(),
|
|
117
120
|
entries: page,
|
|
118
121
|
...(hasMore ? { truncated: true, nextBefore: encodeExportCursor({ id: last.id }) } : {}),
|
|
122
|
+
...withheld,
|
|
119
123
|
});
|
|
120
124
|
}
|
|
121
125
|
catch (err) {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* `checkAdoptionWidths` 同一条判据):一次静默截断会让 hold 落在一个**别的**域上。
|
|
25
25
|
*
|
|
26
26
|
* ── 审计行为什么放置/解除都要写 ────────────────────────────────────────────────────────────
|
|
27
|
-
* hold 本身就是一次留存治理动作,不留痕的 hold
|
|
27
|
+
* hold 本身就是一次留存治理动作,不留痕的 hold 是审计缺陷(设计稿 §5 逐字)。行**每次成功调用都写**,
|
|
28
28
|
* 而不是"只在状态真的翻转时写":审计表记的是**发生过的动作**(operator 在某时刻按了这个按钮),不是
|
|
29
29
|
* 状态机的边。一次对已冻结域的重复 PUT 同样是一次需要留痕的操作。
|
|
30
30
|
*
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
* 因此**导入两口**只认已验明的 principal,连 `explicitOperatorOk` 的旁路都不给:一个 operator 想给
|
|
12
12
|
* 自己导规则,用他自己的 principal 走同一条路即可(那时他就是租户)。
|
|
13
13
|
*
|
|
14
|
-
* 🔴 **撤销面两口多一条 operator
|
|
14
|
+
* 🔴 **撤销面两口多一条 operator 超范围域,而这不是上面那句话的例外,是它的对偶**(design/203 §2 设计题
|
|
15
15
|
* 一裁定):`?principal=` / body 里的 `principal` 让 operator 读**或收回**任一租户的规则。方向决定授权:
|
|
16
16
|
* 收回是**收紧**(core 的 `removePersistedRule` 头注逐字:「a host may narrow on a user's behalf, it may
|
|
17
17
|
* not widen」),而多租户管理员能回收权限正是撤销面的本义。读那一半同样只开给 operator ——
|
|
18
|
-
* 规则文本里含命令样式,跨 principal
|
|
18
|
+
* 规则文本里含命令样式,跨 principal 读是信息面外泄,所以**不开第三方 principal 相互读**。
|
|
19
19
|
* 判据用 `explicitOperatorOk`(空名单 = 全拒),**绝不**从 service token 推断身份:一个共享的部署凭证
|
|
20
20
|
* 不是一个人。
|
|
21
21
|
*
|
|
@@ -29,6 +29,9 @@ import type { IncomingMessage, ServerResponse } from "node:http";
|
|
|
29
29
|
import type { RouteCtx } from "../route-ctx.js";
|
|
30
30
|
export declare const RULES_CC_IMPORT_PREPARE_PATH = "/v1/rules/cc-import/prepare";
|
|
31
31
|
export declare const RULES_CC_IMPORT_REDEEM_PATH = "/v1/rules/cc-import/redeem";
|
|
32
|
+
/** #265 A7:File 规则直通票口两条(与 cc-import 两口并列,票语义分家见 `rules-consent.ts` 的 A7 段)。 */
|
|
33
|
+
export declare const RULES_LOCAL_IMPORT_PREPARE_PATH = "/v1/rules/local-import/prepare";
|
|
34
|
+
export declare const RULES_LOCAL_IMPORT_REDEEM_PATH = "/v1/rules/local-import/redeem";
|
|
32
35
|
/** 撤销面两口共用的字面路径(GET 列举 / DELETE 撤销)。 */
|
|
33
36
|
export declare const RULES_PATH = "/v1/rules";
|
|
34
37
|
export declare function handleRules(req: IncomingMessage, res: ServerResponse, url: string, ctx: RouteCtx): Promise<boolean>;
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { sendJson, sendError } from "../send.js";
|
|
2
|
+
import { sendJson, sendError, httpErrorCode } from "../send.js";
|
|
3
3
|
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
4
4
|
import { createLogger } from "../../observability/logger.js";
|
|
5
5
|
import { RULE_IMPORT_RETRY_AFTER_SEC, MAX_RULE_SCOPE_CHARS, parseRuleScope, serializeRuleScope } from "../../rules-consent.js";
|
|
6
|
+
import { CONTROL_AND_BIDI_CHARS } from "../../text-bidi.js";
|
|
7
|
+
import { HttpError } from "../../security.js";
|
|
8
|
+
import { MAX_RULE_TEXT_CHARS } from "@sema-agent/core";
|
|
6
9
|
const logger = createLogger();
|
|
7
10
|
export const RULES_CC_IMPORT_PREPARE_PATH = "/v1/rules/cc-import/prepare";
|
|
8
11
|
export const RULES_CC_IMPORT_REDEEM_PATH = "/v1/rules/cc-import/redeem";
|
|
12
|
+
export const RULES_LOCAL_IMPORT_PREPARE_PATH = "/v1/rules/local-import/prepare";
|
|
13
|
+
export const RULES_LOCAL_IMPORT_REDEEM_PATH = "/v1/rules/local-import/redeem";
|
|
9
14
|
export const RULES_PATH = "/v1/rules";
|
|
10
15
|
const RULES_PAGE_DEFAULT = 50;
|
|
11
16
|
const RULES_PAGE_MAX = 200;
|
|
@@ -26,7 +31,30 @@ const CcImportPrepareBodySchema = z
|
|
|
26
31
|
.max(MAX_LAYERS),
|
|
27
32
|
})
|
|
28
33
|
.strict();
|
|
29
|
-
const
|
|
34
|
+
const TICKET_CONTROL_CHARS_RE = new RegExp(`[${CONTROL_AND_BIDI_CHARS}]`);
|
|
35
|
+
const TicketStringSchema = z.string().min(1).max(190).refine((t) => !TICKET_CONTROL_CHARS_RE.test(t));
|
|
36
|
+
const CcImportRedeemBodySchema = z.object({ ticket: TicketStringSchema }).strict();
|
|
37
|
+
const LocalImportAddSchema = z
|
|
38
|
+
.object({
|
|
39
|
+
dot: z.object({ actor: z.string().min(1).max(190), counter: z.number().int().nonnegative().safe() }).strict(),
|
|
40
|
+
origin: z.enum(["user", "imported-cc", "starter"]),
|
|
41
|
+
createdAt: z.string().min(1).max(64),
|
|
42
|
+
})
|
|
43
|
+
.strict();
|
|
44
|
+
const LocalImportRowSchema = z
|
|
45
|
+
.object({
|
|
46
|
+
rule: z.string().min(1).max(MAX_RULE_TEXT_CHARS),
|
|
47
|
+
scope: z.string().min(1).max(MAX_RULE_SCOPE_CHARS),
|
|
48
|
+
tool: z.literal("Bash"),
|
|
49
|
+
match: z.enum(["exact", "prefix"]),
|
|
50
|
+
command: z.string().min(1).max(MAX_RULE_TEXT_CHARS),
|
|
51
|
+
adds: z.array(LocalImportAddSchema).max(64),
|
|
52
|
+
})
|
|
53
|
+
.strict();
|
|
54
|
+
const LOCAL_IMPORT_ROW_SHAPE_PIN = true;
|
|
55
|
+
void LOCAL_IMPORT_ROW_SHAPE_PIN;
|
|
56
|
+
const LocalImportPrepareBodySchema = z.object({ rows: z.array(LocalImportRowSchema).min(1) }).strict();
|
|
57
|
+
const LocalImportRedeemBodySchema = z.object({ ticket: TicketStringSchema }).strict();
|
|
30
58
|
const RuleRevokeBodySchema = z
|
|
31
59
|
.object({
|
|
32
60
|
rule: z.string().min(1).max(1024),
|
|
@@ -60,9 +88,11 @@ async function handleRulesBody(req, res, url, ctx, miss) {
|
|
|
60
88
|
const { readJson, rateLimited } = ctx.helpers;
|
|
61
89
|
const isPrepare = req.method === "POST" && url === RULES_CC_IMPORT_PREPARE_PATH;
|
|
62
90
|
const isRedeem = req.method === "POST" && url === RULES_CC_IMPORT_REDEEM_PATH;
|
|
91
|
+
const isLocalPrepare = req.method === "POST" && url === "/v1/rules/local-import/prepare";
|
|
92
|
+
const isLocalRedeem = req.method === "POST" && url === "/v1/rules/local-import/redeem";
|
|
63
93
|
const isList = req.method === "GET" && url === "/v1/rules";
|
|
64
94
|
const isRevoke = req.method === "DELETE" && url === "/v1/rules";
|
|
65
|
-
if (!isPrepare && !isRedeem && !isList && !isRevoke) {
|
|
95
|
+
if (!isPrepare && !isRedeem && !isList && !isRevoke && !isLocalPrepare && !isLocalRedeem) {
|
|
66
96
|
miss.fell = true;
|
|
67
97
|
return;
|
|
68
98
|
}
|
|
@@ -86,7 +116,11 @@ async function handleRulesBody(req, res, url, ctx, miss) {
|
|
|
86
116
|
try {
|
|
87
117
|
rawBody = await readJson(req);
|
|
88
118
|
}
|
|
89
|
-
catch {
|
|
119
|
+
catch (err) {
|
|
120
|
+
if (err instanceof HttpError && err.status !== 400) {
|
|
121
|
+
sendError(res, err.status, httpErrorCode(err.status, err.code), err.message);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
90
124
|
sendError(res, 400, "request.body_shape", "body must be valid JSON");
|
|
91
125
|
return;
|
|
92
126
|
}
|
|
@@ -94,6 +128,46 @@ async function handleRulesBody(req, res, url, ctx, miss) {
|
|
|
94
128
|
await handleRuleRevoke(res, rawBody, principal, lane, deps.config.operatorPrincipals);
|
|
95
129
|
return;
|
|
96
130
|
}
|
|
131
|
+
if (isLocalPrepare) {
|
|
132
|
+
const parsed = LocalImportPrepareBodySchema.safeParse(rawBody);
|
|
133
|
+
if (!parsed.success) {
|
|
134
|
+
sendError(res, 400, "request.body_shape", "body must be {rows:[{rule,scope,tool,match,command,adds}]}");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const prepared = await lane.prepareLocalImport(principal, parsed.data.rows);
|
|
138
|
+
if (!prepared.ok) {
|
|
139
|
+
logger.info("local rule import batch refused", prepared.reason === "too-many-rows" ? { reason: prepared.reason, rows: prepared.rows } : { reason: prepared.reason, bytes: prepared.bytes });
|
|
140
|
+
sendError(res, 413, "request.payload_too_large", "too many rule rows in this batch — split it and import again");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
sendJson(res, 200, { preview: prepared.preview, ...(prepared.ticket !== undefined ? { ticket: prepared.ticket, expiresAtMs: prepared.expiresAtMs } : {}) });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (isLocalRedeem) {
|
|
147
|
+
const parsed = LocalImportRedeemBodySchema.safeParse(rawBody);
|
|
148
|
+
if (!parsed.success) {
|
|
149
|
+
sendError(res, 400, "request.body_shape", "body must be {ticket}");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const redeemed = await lane.redeemLocalImport(principal, parsed.data.ticket);
|
|
153
|
+
if (redeemed.ok) {
|
|
154
|
+
sendJson(res, 200, { members: redeemed.outcome.members, rev: redeemed.outcome.rev });
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
logger.info("local rule import ticket refused", { reason: redeemed.reason, detail: redeemed.detail, retryable: redeemed.reason !== "ticket-unusable" && redeemed.reason !== "in-flight" && redeemed.retryable === true });
|
|
158
|
+
if (redeemed.reason === "in-flight") {
|
|
159
|
+
res.setHeader("retry-after", String(RULE_IMPORT_RETRY_AFTER_SEC));
|
|
160
|
+
sendError(res, 409, "conflict.rule_import_in_flight", "another redemption of this import ticket is still in flight — retry with the same ticket", { retryAfterSec: RULE_IMPORT_RETRY_AFTER_SEC });
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (redeemed.reason !== "ticket-unusable" && redeemed.retryable === true) {
|
|
164
|
+
res.setHeader("retry-after", String(RULE_IMPORT_RETRY_AFTER_SEC));
|
|
165
|
+
sendError(res, 503, "state.rule_import_retry", "the rule import could not be completed right now; the ticket is still usable — retry", { retryAfterSec: RULE_IMPORT_RETRY_AFTER_SEC });
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
sendError(res, 404, "not_found.rule_ticket", "no such import ticket");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
97
171
|
if (isPrepare) {
|
|
98
172
|
const parsed = CcImportPrepareBodySchema.safeParse(rawBody);
|
|
99
173
|
if (!parsed.success) {
|
|
@@ -115,7 +189,12 @@ async function handleRulesBody(req, res, url, ctx, miss) {
|
|
|
115
189
|
}
|
|
116
190
|
const redeemed = await lane.redeemImport(principal, parsed.data.ticket);
|
|
117
191
|
if (!redeemed.ok) {
|
|
118
|
-
logger.info("rule import ticket refused", { reason: redeemed.reason, detail: redeemed.detail, retryable: redeemed.retryable === true });
|
|
192
|
+
logger.info("rule import ticket refused", { reason: redeemed.reason, detail: redeemed.detail, retryable: redeemed.reason !== "state-unreadable" && redeemed.retryable === true });
|
|
193
|
+
if (redeemed.reason === "state-unreadable") {
|
|
194
|
+
res.setHeader("retry-after", String(RULE_IMPORT_RETRY_AFTER_SEC));
|
|
195
|
+
sendError(res, 503, "state.rule_ticket_unreadable", "the import ticket's state could not be read right now — retry with the same ticket, and re-run the import if it then reports not found", { retryAfterSec: RULE_IMPORT_RETRY_AFTER_SEC });
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
119
198
|
if (redeemed.retryable === true) {
|
|
120
199
|
res.setHeader("retry-after", String(RULE_IMPORT_RETRY_AFTER_SEC));
|
|
121
200
|
sendError(res, 503, "state.rule_import_retry", "the rule import could not be completed right now; the ticket is still usable — retry", { retryAfterSec: RULE_IMPORT_RETRY_AFTER_SEC });
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design/383 §2.1 / §3.4 S-8①(server S-49;[5596] 认领)—— **中途翻转 verb** 的 wire 面:
|
|
3
|
+
*
|
|
4
|
+
* `POST /v1/runs/:id/memory/capture-optout` body `{ reason?: string }`
|
|
5
|
+
* → 200 `{ taskId, sessionId, outcome: "created" | "existed" }`
|
|
6
|
+
*
|
|
7
|
+
* 语义 = core `TaskStream.optOutMemoryCapture()`(「聊着聊着发现不该记」):从这一刻起本会话**零提交**长期记忆、
|
|
8
|
+
* 已提交贡献退出整理候选(回溯 A2 臂)、写根残留边界隔离清扫。**单向**(无反向动词;重复调用幂等 `existed`),
|
|
9
|
+
* 回到采集只能开新会话。
|
|
10
|
+
*
|
|
11
|
+
* ── 权限法条(steer 车道逐字)────────────────────────────────────────────────────────────────────
|
|
12
|
+
* core 不认证:持有活流句柄即能力。**本面拥有 verified-principal / 会话所有权判定**:裸 sessionId 中继会让任何人
|
|
13
|
+
* 关掉任何人的记忆(可用性攻击)。所以:身份来自 `gatedPrincipal`(验证过的身份,不是可自造的头),run 属主不符
|
|
14
|
+
* ⇒ 404 `not_found.run` **同串反枚举**(与 steer/interrupt 同判)。core 在翻转时刻**重新裁决** per-principal
|
|
15
|
+
* entitlement(`RuntimeCaps.allowMemoryOptOut` under `memoryCapturePolicy`,fresh resolve)—— 被拒 ⇒ typed
|
|
16
|
+
* `memory.capture_optout_denied` + 零记录残留;server 不复刻那条裁决。
|
|
17
|
+
*
|
|
18
|
+
* ── 四臂(steer 车道同形,但**没有 park 腿**)──────────────────────────────────────────────────────
|
|
19
|
+
* (a) 本副本活流在场 ⇒ 调 verb,200 `{outcome}`;core 的 typed 拒闭集逐码转发(见 switch);
|
|
20
|
+
* (b) 非 live(parked / 他副本 / 无活流 / 终态)⇒ 409 `steering.not_running` + 指路**声明形**:opt-out 的持久
|
|
21
|
+
* 形是 `TaskSpec.memory.capture:"off"`(wire `memoryCapture:"off"`),下一次 resume/新会话提交时带上即可 ——
|
|
22
|
+
* core 明写声明与 verb 是同一条记录的两个 ingress,**不存在**「park 一个翻转等 resume 再翻」的形(记录是
|
|
23
|
+
* 一次性单向的,resume 时带声明就是幂等重写),所以本面刻意不铸 park 腿。
|
|
24
|
+
*
|
|
25
|
+
* ── 计费/门 ─────────────────────────────────────────────────────────────────────────────────────
|
|
26
|
+
* 零模型工作(不进 billable;不吃 drain / roster-pending);mutating ⇒ `rateLimited`。**不进** `isCredentialGatedRewrite`:
|
|
27
|
+
* 有属主门兜底(run.owner),爆炸半径 = 本人会话。能力位 `runMemoryCaptureOptOut`(= 持久 run 账面在场,与
|
|
28
|
+
* `sessionBackgroundable` 同谓词):老 worker 键缺席 ⇒ 壳不渲入口(假 affordance 纪律)。
|
|
29
|
+
*/
|
|
30
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
31
|
+
import { type RouteCtx } from "../route-ctx.js";
|
|
32
|
+
export declare const RUN_MEMORY_CAPTURE_OPTOUT_RE: RegExp;
|
|
33
|
+
export declare function handleRunMemoryCapture(req: IncomingMessage, res: ServerResponse, url: string, ctx: RouteCtx): Promise<boolean>;
|
|
34
|
+
//# sourceMappingURL=run-memory-capture.d.ts.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { isParkedRunStatus, isTerminalRunStatus } from "../../plugins/store-contracts.js";
|
|
3
|
+
import { sendJson, sendError } from "../send.js";
|
|
4
|
+
import { gatedPrincipal } from "../principal-gate.js";
|
|
5
|
+
import { RUN_NOT_FOUND_MESSAGE, runNotFoundMessage } from "../route-ctx.js";
|
|
6
|
+
export const RUN_MEMORY_CAPTURE_OPTOUT_RE = /^\/v1\/runs\/([^/]+)\/memory\/capture-optout$/;
|
|
7
|
+
const BodySchema = z.object({ reason: z.string().min(1).max(500).optional() }).strict();
|
|
8
|
+
export async function handleRunMemoryCapture(req, res, url, ctx) {
|
|
9
|
+
const miss = { fell: false };
|
|
10
|
+
await handleBody(req, res, url, ctx, miss);
|
|
11
|
+
return !miss.fell;
|
|
12
|
+
}
|
|
13
|
+
async function handleBody(req, res, url, ctx, miss) {
|
|
14
|
+
const { deps } = ctx;
|
|
15
|
+
const m = req.method === "POST" ? RUN_MEMORY_CAPTURE_OPTOUT_RE.exec(url) : null;
|
|
16
|
+
if (m === null) {
|
|
17
|
+
miss.fell = true;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (ctx.helpers.rateLimited(req, res))
|
|
21
|
+
return;
|
|
22
|
+
if (!deps.runStore) {
|
|
23
|
+
sendError(res, 501, "capability.run_store_required", "async runs require a durable run store (DB_BACKEND=mysql|pg|local)");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const principal = gatedPrincipal(req, deps.config);
|
|
27
|
+
if (deps.config.requirePrincipal && principal === undefined) {
|
|
28
|
+
sendError(res, 401, "auth.principal_required", `missing principal header '${deps.config.principalHeader}'`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const taskId = m[1];
|
|
32
|
+
const parsed = BodySchema.safeParse((await ctx.helpers.readJson(req)) ?? {});
|
|
33
|
+
if (!parsed.success) {
|
|
34
|
+
sendError(res, 400, "request.body_shape", "body must be {} or { reason: string (non-empty, at most 500 chars) } — the session memory-capture opt-out verb takes no other keys");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const run = await deps.runStore.getRun(taskId);
|
|
38
|
+
if (!run) {
|
|
39
|
+
sendError(res, 404, "not_found.run", runNotFoundMessage(taskId));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (run.owner !== null && run.owner !== principal) {
|
|
43
|
+
sendError(res, 404, "not_found.run", RUN_NOT_FOUND_MESSAGE);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const live = ctx.registry.steerableRuns.get(taskId);
|
|
47
|
+
if (live) {
|
|
48
|
+
try {
|
|
49
|
+
const { outcome } = await live.optOutMemoryCapture(parsed.data.reason !== undefined ? { reason: parsed.data.reason } : {});
|
|
50
|
+
sendJson(res, 200, { taskId, sessionId: run.sessionId, outcome });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
const code = e.code;
|
|
55
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
56
|
+
switch (code) {
|
|
57
|
+
case "memory.capture_optout_denied":
|
|
58
|
+
sendError(res, 403, "memory.capture_optout_denied", msg);
|
|
59
|
+
return;
|
|
60
|
+
case "memory.capture_optout_unpersisted":
|
|
61
|
+
sendError(res, 503, "memory.capture_optout_unpersisted", msg);
|
|
62
|
+
return;
|
|
63
|
+
case "memory.capture_optout_unavailable":
|
|
64
|
+
sendError(res, 409, "memory.capture_optout_unavailable", msg);
|
|
65
|
+
return;
|
|
66
|
+
case "memory.capture_optout_sweep_failed":
|
|
67
|
+
sendError(res, 409, "memory.capture_optout_sweep_failed", msg);
|
|
68
|
+
return;
|
|
69
|
+
case "config.memory_capture_unsupported":
|
|
70
|
+
sendError(res, 409, "config.memory_capture_unsupported", msg);
|
|
71
|
+
return;
|
|
72
|
+
case "steering.invalid_content":
|
|
73
|
+
sendError(res, 422, "steering.invalid_content", msg);
|
|
74
|
+
return;
|
|
75
|
+
case "steering.not_running":
|
|
76
|
+
sendError(res, 409, "steering.not_running", "run just finished — the memory-capture opt-out verb needs a live run; declare it on the next submit instead (memoryCapture: 'off')");
|
|
77
|
+
return;
|
|
78
|
+
default: throw e;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (isParkedRunStatus(run.status)) {
|
|
83
|
+
sendError(res, 409, "steering.not_running", "run is parked on a pending decision — the memory-capture opt-out verb needs a live turn, and a resumed continuation rebuilds its spec from the persisted body (it cannot take a new declaration); the opt-out becomes effective on this session's next NEW submit (POST /v1/tasks with this sessionId and memoryCapture: 'off') — content captured until then stays captured");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (isTerminalRunStatus(run.status)) {
|
|
87
|
+
sendError(res, 409, "steering.not_running", "run already ended — the memory-capture opt-out verb needs a live run; submit the next turn of this session with memoryCapture: 'off' (the declaration writes the same one-way session record)");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const provenElsewhere = typeof run.instanceId === "string" && run.instanceId.length > 0 && deps.instanceId !== undefined && run.instanceId !== deps.instanceId;
|
|
91
|
+
if (provenElsewhere) {
|
|
92
|
+
sendError(res, 409, "steering.not_running", "run is live on another replica — the memory-capture opt-out verb must reach the replica holding the stream; resend it there, or declare memoryCapture: 'off' on the next submit");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
sendError(res, 409, "steering.not_running", "no live steerable face for this run on this replica — verify/cascade execution lanes expose none; declare memoryCapture: 'off' on the next submit of this session instead");
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=run-memory-capture.js.map
|
|
@@ -20,6 +20,7 @@ export declare const RUN_CANCEL_RE: RegExp;
|
|
|
20
20
|
export declare const RUN_STEER_RE: RegExp;
|
|
21
21
|
export declare const RUN_DETACH_RE: RegExp;
|
|
22
22
|
export declare const RUN_COMPACT_RE: RegExp;
|
|
23
|
+
export declare const RUN_INTERRUPT_RE: RegExp;
|
|
23
24
|
/** PAIR-REVIEW F-10:runs 面 404 文案统一富形——a\* 形 id(background-agent registry 句柄,`a`+hex 短形)
|
|
24
25
|
* 是「结构性不在 run 店」的常客([1347]⑤ 记档裁量:不改路由,只给引导)。识别句法形即补引导;
|
|
25
26
|
* 非 a\* 形保通用富文案(cancel 腿 [888]① 同款)。 */
|