@sema-agent/server 7.103.1 → 7.104.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 +362 -3
- package/README.md +6 -1
- package/README.zh-CN.md +5 -1
- package/USAGE.md +279 -18
- package/deploy/sema-up/README.md +57 -7
- package/deploy/sema-up/cluster-up.sh +18 -4
- package/deploy/sema-up/kube-up.sh +16 -0
- package/deploy/sema-up/lib/dataroot.sh +142 -0
- package/deploy/sema-up/lib/flagshape.sh +69 -0
- package/deploy/sema-up/lib/lanes.py +14 -4
- package/deploy/sema-up/lib/shell-access.sh +41 -0
- package/deploy/sema-up/preflight.sh +14 -1
- package/deploy/sema-up/sema-up.sh +134 -37
- package/deploy/sema-up/smoke.sh +5 -0
- package/dist/auto-mode-face.js +3 -4
- package/dist/boot/budget-tracing.js +1 -1
- package/dist/boot/config-center.d.ts +6 -2
- package/dist/boot/config-center.js +5 -59
- package/dist/boot/execution-env.d.ts +11 -0
- package/dist/boot/execution-env.js +14 -0
- package/dist/boot/execution-lane.d.ts +25 -0
- package/dist/boot/execution-lane.js +95 -0
- package/dist/boot/memory-consolidation.d.ts +185 -9
- package/dist/boot/memory-consolidation.js +192 -32
- package/dist/boot/resolve-spec.d.ts +2 -0
- package/dist/boot/resolve-spec.js +28 -24
- package/dist/boot/runner-deps.d.ts +6 -2
- package/dist/boot/runner-deps.js +4 -0
- package/dist/boot/session-faces.d.ts +5 -0
- package/dist/boot/session-faces.js +18 -2
- package/dist/boot/session-incarnation-seat.d.ts +41 -0
- package/dist/boot/session-incarnation-seat.js +69 -0
- package/dist/boot/stage-01-config.js +2 -2
- package/dist/boot/stage-02-registry-pull.js +3 -1
- package/dist/boot/stage-04-stores.d.ts +2 -0
- package/dist/boot/stage-04-stores.js +3 -1
- package/dist/boot/stage-05-execution-env.d.ts +4 -0
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +4 -1
- package/dist/boot/stage-06-runners.js +7 -14
- package/dist/boot/stage-07-capability-layer.d.ts +4 -1
- package/dist/boot/stage-08-reapers.d.ts +4 -1
- package/dist/boot/stage-09-leader.d.ts +6 -1
- package/dist/boot/stage-09-leader.js +2 -1
- package/dist/boot/stage-10-http-server.d.ts +6 -1
- package/dist/boot/stage-10-http-server.js +4 -1
- package/dist/brain.d.ts +1 -1
- package/dist/config-catalog.d.ts +16 -1
- package/dist/config-catalog.js +9 -9
- package/dist/config-center/restart-signal.d.ts +4 -4
- package/dist/config-invariants.d.ts +7 -1
- package/dist/config-invariants.js +37 -6
- package/dist/config-provider.d.ts +42 -10
- package/dist/config-provider.js +18 -16
- package/dist/config-types.d.ts +37 -16
- package/dist/config-types.js +1 -0
- package/dist/config.d.ts +13 -28
- package/dist/config.js +53 -70
- package/dist/env-facts.d.ts +13 -3
- package/dist/env-facts.js +15 -3
- package/dist/execution-lane-caps.d.ts +15 -0
- package/dist/execution-lane-caps.js +8 -0
- package/dist/file-history-capture.d.ts +42 -35
- package/dist/file-history-capture.js +15 -10
- package/dist/host-lsp-manager.d.ts +1 -1
- package/dist/http/admission.d.ts +11 -4
- package/dist/http/admission.js +5 -1
- package/dist/http/dispatch.js +3 -3
- package/dist/http/principal-gate.d.ts +22 -0
- package/dist/http/principal-gate.js +20 -0
- package/dist/http/resume-legs.js +9 -5
- package/dist/http/routes/agents-roster.d.ts +7 -0
- package/dist/http/routes/agents-roster.js +6 -2
- package/dist/http/routes/approvals-assistant.d.ts +48 -19
- package/dist/http/routes/approvals-assistant.js +117 -50
- package/dist/http/routes/capabilities.js +3 -2
- package/dist/http/routes/config-catalog.js +1 -0
- package/dist/http/routes/memory-consolidation.d.ts +3 -2
- package/dist/http/routes/memory-policy.js +3 -3
- package/dist/http/routes/rules.js +2 -2
- package/dist/http/routes/runs.js +27 -2
- package/dist/http/routes/session-sync.js +8 -14
- package/dist/http/routes/sessions-list.js +3 -2
- package/dist/http/routes/sessions.js +44 -23
- package/dist/http/routes/shared-memory.js +2 -2
- package/dist/http/routes/tasks.js +22 -19
- package/dist/http/routes/trace-usage.js +4 -4
- package/dist/http/send.d.ts +12 -0
- package/dist/http/send.js +12 -1
- package/dist/http/server.d.ts +21 -4
- package/dist/http/server.js +7 -4
- package/dist/http/sync-submit-body.d.ts +43 -0
- package/dist/http/sync-submit-body.js +4 -0
- package/dist/http/wire-types.d.ts +23 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/leader/merge.d.ts +8 -3
- package/dist/memory-incident-sink.js +2 -1
- package/dist/model-select.d.ts +9 -0
- package/dist/model-select.js +5 -1
- package/dist/observability/corrupt-read-seat.d.ts +151 -16
- package/dist/observability/corrupt-read-seat.js +158 -15
- package/dist/observability/fail-open.d.ts +12 -0
- package/dist/observability/fail-open.js +12 -0
- package/dist/observability/run-terminal.d.ts +15 -1
- package/dist/observability/run-terminal.js +4 -0
- package/dist/parked-decide.d.ts +8 -3
- package/dist/parked-decide.js +56 -20
- package/dist/parked-revive-surface.d.ts +14 -7
- package/dist/parked-revive-surface.js +5 -1
- package/dist/plugins/approval-ask-store-file.d.ts +4 -1
- package/dist/plugins/approval-ask-store-file.js +9 -12
- package/dist/plugins/approval-ask-store-sql.d.ts +4 -0
- package/dist/plugins/approval-ask-store-sql.js +4 -1
- package/dist/plugins/approval-exemption-store.d.ts +21 -1
- package/dist/plugins/approval-exemption-store.js +36 -7
- package/dist/plugins/approval-nonce-store.d.ts +16 -2
- package/dist/plugins/approval-nonce-store.js +17 -7
- package/dist/plugins/caching-session-store.d.ts +5 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/file-resume-anchor-store.d.ts +7 -2
- package/dist/plugins/file-resume-anchor-store.js +19 -5
- package/dist/plugins/file-run-store.d.ts +21 -8
- package/dist/plugins/file-run-store.js +60 -38
- package/dist/plugins/local-session-store.d.ts +22 -0
- package/dist/plugins/local-session-store.js +74 -11
- package/dist/plugins/memory-run-store.d.ts +7 -5
- package/dist/plugins/memory-run-store.js +11 -4
- package/dist/plugins/one-time-migrations.d.ts +38 -1
- package/dist/plugins/one-time-migrations.js +143 -15
- package/dist/plugins/permission-rule-store-file.d.ts +9 -2
- package/dist/plugins/permission-rule-store-file.js +37 -51
- package/dist/plugins/pg-session-storage.d.ts +3 -0
- package/dist/plugins/pg-session-storage.js +19 -10
- package/dist/plugins/run-store-sql.d.ts +12 -4
- package/dist/plugins/run-store-sql.js +15 -13
- package/dist/plugins/send-file-ledger.d.ts +5 -1
- package/dist/plugins/send-file-ledger.js +11 -6
- package/dist/plugins/session-event-payload.d.ts +14 -4
- package/dist/plugins/session-event-payload.js +5 -2
- package/dist/plugins/store-backend.d.ts +7 -3
- package/dist/plugins/store-backend.js +10 -6
- package/dist/plugins/store-contracts.d.ts +7 -2
- package/dist/plugins/tidb-pool.js +8 -2
- package/dist/plugins/tidb-session-store.d.ts +6 -1
- package/dist/plugins/tidb-session-store.js +17 -10
- package/dist/project-identity.d.ts +4 -6
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +72 -3
- package/dist/request-value-closure.js +12 -4
- package/dist/roster-bound-http-status.d.ts +6 -0
- package/dist/roster-bound-http-status.js +6 -0
- package/dist/run-local.js +22 -27
- package/dist/runs.d.ts +22 -2
- package/dist/runs.js +29 -7
- package/dist/sealed-key.d.ts +31 -4
- package/dist/sealed-key.js +9 -0
- package/dist/security.d.ts +5 -1
- package/dist/server-secret-env.d.ts +2 -2
- package/dist/session-children-retire.d.ts +92 -0
- package/dist/session-children-retire.js +85 -0
- package/dist/session-error-wire.d.ts +58 -0
- package/dist/session-error-wire.js +38 -0
- package/dist/session-incarnation.d.ts +61 -0
- package/dist/session-incarnation.js +43 -0
- package/dist/session-records-skipped-notice.d.ts +20 -0
- package/dist/session-records-skipped-notice.js +27 -0
- package/dist/tool-approval.d.ts +65 -0
- package/dist/tool-approval.js +36 -9
- package/dist/trace/core-keyset-guard.d.ts +8 -7
- package/dist/trace/engine-notice-wire.d.ts +3 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/project.d.ts +1 -1
- package/dist/trace/project.js +9 -0
- package/dist/trace/wire-projection-faces.d.ts +10 -3
- package/dist/trace/wire-projection-faces.js +8 -2
- package/package.json +3 -3
|
@@ -1,35 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* S-589 —— 文件历史的**装配单点**与能力词 `fileHistoryCapture` 的闭集(叶模块:只 type-import store-backend)。
|
|
2
|
+
* S-589 → S-724 —— 文件历史的**装配单点**与能力词 `fileHistoryCapture` 的闭集(叶模块:只 type-import store-backend 与 core 型)。
|
|
3
3
|
*
|
|
4
|
-
* ##
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
4
|
+
* ## 病史
|
|
5
|
+
* S-589(7.97.0):`boot/budget-tracing.ts` 只要后端有文件历史店就无条件交给 core(`RunnerDeps.fileHistoryStore`)。core 的首次
|
|
6
|
+
* 触碰捕获当时是**进程级**的:接了店就恒捕获,没有任何单会话 / 单 run 的杠杆。于是用户在客户端关掉「文件检查点」关掉的只是
|
|
7
|
+
* 提示,文件字节照旧进 `file_history_*`。S-589 给了部署旋钮 `FILE_HISTORY_CAPTURE`,而 core 那时只有一种写法表达「不捕获」——
|
|
8
|
+
* **不接店** ⇒ 旋钮关 = 摘店,代价是已存历史也一起读不了(restore / rewind / 2c sync 一并关)。
|
|
9
|
+
* S-724(7.104.0):core 7.31.0 收了两件 —— 部署的**捕获位** `RunnerDeps.fileHistoryCapture: "off"`(店照接,只停新捕获,
|
|
10
|
+
* 已存历史照常服务 `restoreFiles` / `rewindFilesTo`)与请求级关断词 `TaskSpec.fileHistory: "disabled"`。旋钮改落 core 的同名座:
|
|
11
|
+
* **名字说的是「捕获」,就只关捕获** —— 一条旋钮一个意思。
|
|
9
12
|
*
|
|
10
|
-
* ##
|
|
11
|
-
* 「这台部署有没有文件历史店」只在 {@link assembleFileHistory}
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
13
|
+
* ## 两件事,两个单点
|
|
14
|
+
* · 「这台部署有没有文件历史店」只在 {@link assembleFileHistory} 判一次(后端在即在,**与旋钮无关**),产出两张脸:
|
|
15
|
+
* **店** `store` 喂 core 的 `RunnerDeps.fileHistoryStore`(还原族)、rewind 族能力位(`rewindFiles` / `restoreFiles` /
|
|
16
|
+
* `rewindFilesTo` / `sessionSync`)与 2c sync 路由族;**擦除脸** `erasure` = 店的**只有删除类动词**的收窄视图,喂 E21 在线删除
|
|
17
|
+
* (`boot/session-faces.ts`)与孤儿 blob GC(`boot/reapers.ts`)。旋钮从不管擦除:关掉捕获的部署照样要能删掉已捕获的文件字节。
|
|
18
|
+
* · 「捕不捕」只在 {@link fileHistoryCaptureOf} 判一次(纯函数,输入 = 店在不在 + 旋钮)。两个消费点喂它同一组输入:
|
|
19
|
+
* core 的部署捕获位({@link coreFileHistoryCaptureSeat},`boot/runner-deps.ts`)与能力词(`http/routes/capabilities.ts`)——
|
|
20
|
+
* core 收到的与能力面说的不可能各说各话。捕获位 `"off"` ⟺ 店在 ∧ 旋钮关;**没有店的部署形不下发**该位:core 对
|
|
21
|
+
* 「off 但没店」拒 prepare(`config.invalid_file_history_capture`,「nothing to switch off」),而旋钮的意思「不捕获」在没店的
|
|
22
|
+
* 部署上本来就成立 —— 旋钮照样无条件生效,只是那一形无事可关(选这一臂而不是拒启的理由见 CHANGELOG 7.104.0 S-724 条:
|
|
23
|
+
* 拒启会把「形合法、意思成立」的配置判成错,且会与 auto 后端的降级臂耦合)。
|
|
24
|
+
* ⚠️ 关掉 ≠ 删掉:翻旋钮**不**做任何批量删除;已捕获的历史照旧被 restore / rewind / sync 读用,按既有的会话删除 /
|
|
25
|
+
* 留存规则清理。要让已存历史也不可用,部署今天的出路只有逐会话 `DELETE /v1/sessions/:id` 与留存清扫(见 USAGE)。
|
|
21
26
|
*
|
|
22
|
-
* ##
|
|
23
|
-
* cli 要的是「`/config`
|
|
24
|
-
* (
|
|
25
|
-
* `on-
|
|
26
|
-
*
|
|
27
|
+
* ## 能力词为什么是 `off` / `on-optional` 两词
|
|
28
|
+
* cli 要的是「`/config` 那个开关该不该出现」。三词闭集的设计(S-589)是 `off` / `on-always` / `on-optional`;S-589 时第三词
|
|
29
|
+
* 结构上不可达(没有单 run 不捕获位)。S-724 起请求词 `fileHistory: "disabled"` 受理 ⇒ 捕获中的部署恒可按 run 关掉 ⇒
|
|
30
|
+
* `on-optional` 成真,而 `on-always`(「捕获中且关不掉」)**结构上不可达** —— 预先声明一个到不了的词等于给客户端一个
|
|
31
|
+
* 永远等不到的分支,所以删词不留(7.97.0–7.103.x 的 worker 仍会发它,消费端读法在契约附录 B)。
|
|
27
32
|
*/
|
|
33
|
+
import type { RunnerDeps } from "@sema-agent/core";
|
|
28
34
|
import type { ServiceFileHistoryStore, StoreBackend } from "./plugins/store-backend.js";
|
|
29
35
|
/** 能力词闭集的单点属主(`Record` 锁步:联合加词而这里不补 ⇒ 编译红;词表数组由它派生,不手抄)。 */
|
|
30
36
|
declare const FILE_HISTORY_CAPTURE_WORD_SET: {
|
|
31
37
|
readonly off: true;
|
|
32
|
-
readonly "on-
|
|
38
|
+
readonly "on-optional": true;
|
|
33
39
|
};
|
|
34
40
|
/** `GET /v1/capabilities` 的 `fileHistoryCapture` 词。 */
|
|
35
41
|
export type FileHistoryCapture = keyof typeof FILE_HISTORY_CAPTURE_WORD_SET;
|
|
@@ -41,25 +47,26 @@ export declare const FILE_HISTORY_CAPTURE_WORDS: readonly FileHistoryCapture[];
|
|
|
41
47
|
export type FileHistoryErasureFace = Pick<ServiceFileHistoryStore, "reap" | "deleteBySession" | "sweepOrphanBlobs">;
|
|
42
48
|
/** 装配结果:两张脸。 */
|
|
43
49
|
export interface FileHistoryAssembly {
|
|
44
|
-
/**
|
|
50
|
+
/** 店:在场 ⟺ 后端在(与旋钮无关)。 */
|
|
45
51
|
readonly store: ServiceFileHistoryStore | undefined;
|
|
46
|
-
/** 擦除脸:在场 ⟺ 后端在(
|
|
52
|
+
/** 擦除脸:在场 ⟺ 后端在(与旋钮无关;是 {@link store} 的删除类动词视图)。 */
|
|
47
53
|
readonly erasure: FileHistoryErasureFace | undefined;
|
|
48
54
|
}
|
|
55
|
+
/** 装配:「这台部署有没有文件历史店」的唯一判定。旋钮不在这里 —— 它只关捕获,不摘店(S-724)。 */
|
|
56
|
+
export declare function assembleFileHistory(backend: StoreBackend | undefined): FileHistoryAssembly;
|
|
49
57
|
/**
|
|
50
|
-
*
|
|
58
|
+
* 「捕不捕」的**唯一**判定(纯函数):能力词。`store` = 装配出来的店(在不在),`knob` = 部署旋钮 `FILE_HISTORY_CAPTURE`
|
|
59
|
+
* (`config.fileHistoryCapture`)。从**装配结果**派生,不照旋钮字面回声:后端本来就没有这只店的部署同样是 `off`。
|
|
51
60
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
61
|
+
* 与 `rewindFiles` 分叉(S-589 当时写下的分叉条件,自本版成真):本词答「这台部署捕不捕、能不能按 run 关掉」,
|
|
62
|
+
* `rewindFiles` 答「这台部署能不能还原」—— 旋钮关的部署本词 `off` 而 `rewindFiles` 仍为真。
|
|
54
63
|
*/
|
|
55
|
-
export declare function
|
|
64
|
+
export declare function fileHistoryCaptureOf(store: object | undefined, knob: boolean): FileHistoryCapture;
|
|
56
65
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* 将来有人把两位当成一回事):core 收了单会话不捕获位之后,本词在捕获脸在场时会变成 `on-optional`,而
|
|
61
|
-
* `rewindFiles` 仍是 true —— 它回答的是「这台部署能不能还原」,本词回答的是「捕获关不关得掉」。
|
|
66
|
+
* core 的部署捕获位 `RunnerDeps.fileHistoryCapture`,由 {@link fileHistoryCaptureOf} 派生(同一组输入、同一只判定)。
|
|
67
|
+
* 形照 `execution-lane-caps.ts` 的穷举 switch(无 default 臂):能力词加一个而这里不加臂 ⇒ 编译红 —— 加臂的那一刻就是
|
|
68
|
+
* 决定「这一词下 core 的捕获位怎么递」的地方。没有店 ⇒ 恒 `undefined`(core 对「off 但没店」拒 prepare)。
|
|
62
69
|
*/
|
|
63
|
-
export declare function
|
|
70
|
+
export declare function coreFileHistoryCaptureSeat(store: object | undefined, knob: boolean): RunnerDeps["fileHistoryCapture"];
|
|
64
71
|
export {};
|
|
65
72
|
//# sourceMappingURL=file-history-capture.d.ts.map
|
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
const FILE_HISTORY_CAPTURE_WORD_SET = {
|
|
2
2
|
off: true,
|
|
3
|
-
"on-
|
|
3
|
+
"on-optional": true,
|
|
4
4
|
};
|
|
5
5
|
export const FILE_HISTORY_CAPTURE_WORDS = Object.keys(FILE_HISTORY_CAPTURE_WORD_SET);
|
|
6
|
-
export function assembleFileHistory(backend
|
|
6
|
+
export function assembleFileHistory(backend) {
|
|
7
7
|
if (backend === undefined)
|
|
8
8
|
return { store: undefined, erasure: undefined };
|
|
9
9
|
const physical = backend.fileHistory();
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
return { store: physical, erasure: physical };
|
|
11
|
+
}
|
|
12
|
+
export function fileHistoryCaptureOf(store, knob) {
|
|
13
|
+
return store !== undefined && knob ? "on-optional" : "off";
|
|
14
|
+
}
|
|
15
|
+
export function coreFileHistoryCaptureSeat(store, knob) {
|
|
16
|
+
if (store === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
const word = fileHistoryCaptureOf(store, knob);
|
|
19
|
+
switch (word) {
|
|
12
20
|
case "off":
|
|
13
|
-
return
|
|
14
|
-
case "on-
|
|
15
|
-
return
|
|
21
|
+
return "off";
|
|
22
|
+
case "on-optional":
|
|
23
|
+
return undefined;
|
|
16
24
|
}
|
|
17
25
|
}
|
|
18
|
-
export function fileHistoryCaptureOf(captureFace) {
|
|
19
|
-
return captureFace === undefined ? "off" : "on-always";
|
|
20
|
-
}
|
|
21
26
|
//# sourceMappingURL=file-history-capture.js.map
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* 语言服务器子进程的 env = `scrubServerSecrets`(process.env) ∪ 部署方点名放回的键(S-699,见下节;缺省空名单)——
|
|
13
13
|
* 剥除与模型可驱动的 host shell(`inheritEnv:"scrub"`)
|
|
14
14
|
* 和 project-memory 的 git 子进程**同一只函数**:目录 `type:"secret"` 键按名剥 ∘ core 形状规则按形剥。只交 core 的默认
|
|
15
|
-
* 会漏掉形状规则认不出的本仓凭据键(
|
|
15
|
+
* 会漏掉形状规则认不出的本仓凭据键(当时是 `OTEL_EXPORTER_OTLP_HEADERS`,core 7.33.7 #1180 起 core 按名剥它;今天只剩名单型旋钮三只),那正是 S-684 当初的病形。
|
|
16
16
|
* 剥掉了什么经既有观测面留痕(site = `server.lsp.spawn-env`,每 site×key 一次披露 + 逐次计数)—— core 那侧
|
|
17
17
|
* 刻意不报(无收集器,KL),本仓这一格补上。
|
|
18
18
|
*
|
package/dist/http/admission.d.ts
CHANGED
|
@@ -29,11 +29,18 @@ export declare const MAX_OUTPUT_SCHEMA_CHARS = 32768;
|
|
|
29
29
|
* · `skill.description must be a string of at most 1024 characters`(描述长度帽);
|
|
30
30
|
* · `skill.name must be a non-empty string of at most 64 characters` 里的 **长度**那一半。
|
|
31
31
|
*
|
|
32
|
-
* 退役的判据是**上游两侧都没有同形门**:CC
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* 退役的判据是**上游两侧都没有同形门**:CC 的技能清单渲染**从不**因为条数多或描述长而拒掉一条技能 —— 超预算时每一条
|
|
33
|
+
* 只有两种结局:全文,或退成 name-only(`- name`);bundled 技能恒为全文,其余按优先级在剩余预算内贪心升回全文
|
|
34
|
+
* (cli280.js:217940-217942 `JBt`;预算 = `max(1, floor((窗口 ?? 200000) × bytesPerToken(缺省 4)× fraction))`,
|
|
35
|
+
* env `SLASH_COMMAND_TOOL_CHAR_BUDGET` 优先,cli280.js:215920 `nUe`)。单条描述超过 `skillListingMaxDescChars`(默认 1536)
|
|
36
|
+
* 截成前 1535 字符加「…」,这一截与预算无关、恒生效(cli280.js:217940 `NFr`)。旧引的 `cli259.js:23195` 是 `skillListingBudgetFraction` 的 settings describe 文案,
|
|
37
|
+
* 不是算法本体。CC 的技能名校验(2.1.259 `Ir()` / 2.1.280 `Rs()`,cli280.js:236381;校验的是 Agent SDK `skills` 选项里的
|
|
38
|
+
* 技能名)判非空、无未配对代理、无首尾空白、无括号逗号控制字符、不是 `'*'`、无通配后缀、不以 `/` 开头、无连续反斜杠、
|
|
39
|
+
* 不以反斜杠结尾 —— **没有长度这一条**;CC 的名字长度界在别的面上且都不是 64:forked skill 作用域持久化 256
|
|
40
|
+
* (cli280.js:214671 `BV`)、transcript 载入(resume)时 `skill_listing.names` 每条 ≤ 512 / 至多 4096 条,不合格整条附件丢弃
|
|
41
|
+
* (cli280.js:217865)。
|
|
35
42
|
* 引擎侧同理:唯一整拒的是 1 MiB 的单技能加载门。于是那三条帽是「装了 11 个技能就 400」这种
|
|
36
|
-
* 纯本仓面的伤——收益是零(
|
|
43
|
+
* 纯本仓面的伤——收益是零(预算由渲染侧兜),代价是用户装不进自己的技能库。
|
|
37
44
|
*
|
|
38
45
|
* **补偿**(三问的第三问):`settings.skillListingBudgetFraction` / `settings.skillListingMaxDescChars`
|
|
39
46
|
* 两个旋钮直达引擎的渲染预算(per-turn 成本的真属主)。
|
package/dist/http/admission.js
CHANGED
|
@@ -4,7 +4,7 @@ import { modelUnavailableMessage } from "../model-entry-refusal.js";
|
|
|
4
4
|
import { HttpError } from "../security.js";
|
|
5
5
|
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_PERMISSION_RULES, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS, SETTINGS_PERMISSION_MODE_WORDS, isSettingsPermissionMode, parseTaskSettings, skillListingBudgetFractionOk, skillListingMaxDescOk, taskSettingsKeyIssue } from "../task-settings.js";
|
|
6
6
|
import { taskBodyKeyIssue } from "../request-key-closure.js";
|
|
7
|
-
import { singleMemberOffFieldIssue } from "../request-value-closure.js";
|
|
7
|
+
import { FILE_HISTORY_DISABLED_NO_RESTORE, fileHistoryRestoreConflict, singleMemberOffFieldIssue } from "../request-value-closure.js";
|
|
8
8
|
import { selfOrchestrationDenial } from "../task-workflow.js";
|
|
9
9
|
import { centerEntitlementSourceWired } from "../runtime-caps-resolver.js";
|
|
10
10
|
import { parseHooksConfig } from "../hooks/hook-runner.js";
|
|
@@ -333,6 +333,10 @@ export function createAdmission(ctx) {
|
|
|
333
333
|
return null;
|
|
334
334
|
}
|
|
335
335
|
}
|
|
336
|
+
if (fileHistoryRestoreConflict(body)) {
|
|
337
|
+
sendError(res, 400, "request.field_conflict", FILE_HISTORY_DISABLED_NO_RESTORE);
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
336
340
|
if (body.requireExistingSession !== undefined && typeof body.requireExistingSession !== "boolean") {
|
|
337
341
|
sendError(res, 400, "request.field_invalid", "requireExistingSession must be a boolean");
|
|
338
342
|
return null;
|
package/dist/http/dispatch.js
CHANGED
|
@@ -6,7 +6,7 @@ import { ROUTE_METHOD_ORDER } from "./route-ctx.js";
|
|
|
6
6
|
import { PRE_CREDENTIAL_ROUTES, POST_CREDENTIAL_ROUTES, routeLabel, isBillableSubmitPath, isCredentialGatedRewrite, isMethodDispatchedSubmitPath, lookupRoute, isProbePath } from "./route-table.js";
|
|
7
7
|
import { sendJson, sendError, isSseResponse } from "./send.js";
|
|
8
8
|
import { readinessOf, healthReadinessKeys, sendReadyz, billableRefusalOf, sendBillableRefusal } from "./readiness.js";
|
|
9
|
-
import { authorized, systemFor, gatedPrincipal, sendPrincipalRefusal } from "./principal-gate.js";
|
|
9
|
+
import { authorized, systemFor, gatedPrincipal, sendPrincipalRefusal, sendUnauthorized } from "./principal-gate.js";
|
|
10
10
|
const PROCESS_STARTED_AT_MS = Date.now() - Math.round(process.uptime() * 1000);
|
|
11
11
|
export function hasServiceAuth(config) {
|
|
12
12
|
return Boolean(config.authToken) || Object.keys(config.authTokens ?? {}).length > 0;
|
|
@@ -175,7 +175,7 @@ export function createDispatch(ctx) {
|
|
|
175
175
|
const devOpen = !anyT && !mt && !deps.config.requirePrincipal;
|
|
176
176
|
const ok = devOpen || (anyT && systemFor(req, deps.config) !== undefined) || (!!mt && authorized(req, mt));
|
|
177
177
|
if (!ok) {
|
|
178
|
-
|
|
178
|
+
sendUnauthorized(res, "operator");
|
|
179
179
|
return;
|
|
180
180
|
}
|
|
181
181
|
if (url === "/metrics/summary") {
|
|
@@ -215,7 +215,7 @@ export function createDispatch(ctx) {
|
|
|
215
215
|
source = "sso";
|
|
216
216
|
}
|
|
217
217
|
else {
|
|
218
|
-
|
|
218
|
+
sendUnauthorized(res, "credential");
|
|
219
219
|
return;
|
|
220
220
|
}
|
|
221
221
|
}
|
|
@@ -44,6 +44,28 @@ export declare function principalTokenPresentedButUnverified(req: IncomingMessag
|
|
|
44
44
|
* 调用链上的**证明三件套**,与本门(身份面)是两道门。
|
|
45
45
|
*/
|
|
46
46
|
export declare function sendPrincipalRefusal(res: ServerResponse, req: IncomingMessage, config: ServiceConfig): void;
|
|
47
|
+
/**
|
|
48
|
+
* 🔴 **`401 auth.unauthorized` 的唯一铸点**(S-850,7.104.0;test [ref] QA 7a C15)。
|
|
49
|
+
*
|
|
50
|
+
* 病:十六处路由(9 个文件)各自手抄同一个 401 —— 码 `auth.unauthorized`、message 就一个词 `unauthorized`。照 sema-up 部署、壳接上
|
|
51
|
+
* 引擎后,第一句就撞上它,不说要哪份凭据、放哪、去哪拿。
|
|
52
|
+
*
|
|
53
|
+
* 规则:码不变;message 按**这道门收什么**分三面(闭集 {@link UnauthorizedFace},穷举 switch —— 加一面不接句子 = 编译红),
|
|
54
|
+
* 每面一句模块级常量(冻结门按常量解引用逐字钉)。三句都**不含任何值**(令牌 / 头名 / 路径实参一概不回显),且**不区分**
|
|
55
|
+
* 「没带」与「带错」—— 同一道门上两者逐字同体,区分就是探测面。
|
|
56
|
+
* · `credential` —— 全局凭据门(`dispatch.ts`):部署配了 service token,请求没带或带的不认(registry 登录令牌验不过同属此面);
|
|
57
|
+
* · `operator` —— 只给运维凭据开的口(`/metrics*` 与 `GET /v1/tasks/source-summary`):service token 开得了,指标口另认只读的
|
|
58
|
+
* `METRICS_TOKEN`(它在全局门之前判);
|
|
59
|
+
* · `identity` —— 过了凭据门(或这台部署没配 service token)、但这一口要一个**验过的调用方身份**:属主寻址的读 / 写面、规则面、
|
|
60
|
+
* 记忆面。在配了 service token 的部署上,「带 service token 却没身份」只会落在只认身份的口(规则 / 记忆 / 会话策略)上 ——
|
|
61
|
+
* 这时再叫调用方「去带 service token」是假出路,所以这一面的句子只讲身份。
|
|
62
|
+
* `auth.principal_required` / `principal_unverified`(上面 {@link sendPrincipalRefusal})是另一只码、另一只铸点,不在此列。
|
|
63
|
+
*/
|
|
64
|
+
export type UnauthorizedFace = "credential" | "operator" | "identity";
|
|
65
|
+
export declare const UNAUTHORIZED_CREDENTIAL = "unauthorized \u2014 the request carries no credential this deployment accepts: send the deployment's service token as a Bearer token in the Authorization header (its operator set it as SERVICE_AUTH_TOKEN or SERVICE_AUTH_TOKENS; a sema-up deployment keeps SERVICE_AUTH_TOKEN in ~/.sema-up/<project>/.env on the machine that ran sema-up)";
|
|
66
|
+
export declare const UNAUTHORIZED_OPERATOR = "unauthorized \u2014 this endpoint is for the deployment's operators and the request carries no operator credential: send the deployment's service token as a Bearer token in the Authorization header (the metrics endpoints also accept the read-only METRICS_TOKEN)";
|
|
67
|
+
export declare const UNAUTHORIZED_IDENTITY = "unauthorized \u2014 this endpoint acts for a named caller and the request carries no caller identity this deployment verifies: identify the caller the way this deployment does (a registry sign-in token as the Bearer token, a signed principal token on a direct-door deployment, or the principal header set by a trusted front end)";
|
|
68
|
+
export declare function sendUnauthorized(res: ServerResponse, face: UnauthorizedFace): void;
|
|
47
69
|
/**
|
|
48
70
|
* 🔴 **「这个调用方读得到这一行 run 吗」的唯一谓词**(S-297 抽取;此前只活在 `server.ts runOwnerOk` 的
|
|
49
71
|
* 函数体里,而 `runOwnerOk` 把**判断**与**404 怎么发**焊在一起 —— 于是任何需要同一条判断但不想 404 的
|
|
@@ -65,6 +65,26 @@ export function sendPrincipalRefusal(res, req, config) {
|
|
|
65
65
|
}
|
|
66
66
|
sendError(res, 401, "auth.principal_required", `missing principal header '${config.principalHeader}'`);
|
|
67
67
|
}
|
|
68
|
+
export const UNAUTHORIZED_CREDENTIAL = "unauthorized — the request carries no credential this deployment accepts: send the deployment's service token as a Bearer token in the Authorization header (its operator set it as SERVICE_AUTH_TOKEN or SERVICE_AUTH_TOKENS; a sema-up deployment keeps SERVICE_AUTH_TOKEN in ~/.sema-up/<project>/.env on the machine that ran sema-up)";
|
|
69
|
+
export const UNAUTHORIZED_OPERATOR = "unauthorized — this endpoint is for the deployment's operators and the request carries no operator credential: send the deployment's service token as a Bearer token in the Authorization header (the metrics endpoints also accept the read-only METRICS_TOKEN)";
|
|
70
|
+
export const UNAUTHORIZED_IDENTITY = "unauthorized — this endpoint acts for a named caller and the request carries no caller identity this deployment verifies: identify the caller the way this deployment does (a registry sign-in token as the Bearer token, a signed principal token on a direct-door deployment, or the principal header set by a trusted front end)";
|
|
71
|
+
export function sendUnauthorized(res, face) {
|
|
72
|
+
switch (face) {
|
|
73
|
+
case "credential":
|
|
74
|
+
sendError(res, 401, "auth.unauthorized", UNAUTHORIZED_CREDENTIAL);
|
|
75
|
+
return;
|
|
76
|
+
case "operator":
|
|
77
|
+
sendError(res, 401, "auth.unauthorized", UNAUTHORIZED_OPERATOR);
|
|
78
|
+
return;
|
|
79
|
+
case "identity":
|
|
80
|
+
sendError(res, 401, "auth.unauthorized", UNAUTHORIZED_IDENTITY);
|
|
81
|
+
return;
|
|
82
|
+
default: {
|
|
83
|
+
const unknownFace = face;
|
|
84
|
+
throw new Error(`sendUnauthorized: unknown face ${String(unknownFace)}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
68
88
|
export function runOwnerReadable(principal, owner, config) {
|
|
69
89
|
if (owner === null)
|
|
70
90
|
return true;
|
package/dist/http/resume-legs.js
CHANGED
|
@@ -2,6 +2,9 @@ import { redactErrorMessage, redactHead } from "../observability/run-terminal.js
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { uuidv7, resumeWithVerification, CheckpointError, defaultTaskRegistry, taskNotificationDedupKey, validatePendingSteer, GLOBAL_USAGE_KEY, PAUSE_REGISTRY } from "@sema-agent/core";
|
|
4
4
|
import { decideParkedAgent, denyWorkflowParksUnreadable, findParkOrigin, planWorkflowParkedDecide } from "../parked-decide.js";
|
|
5
|
+
const sessionFaceUnavailable = async (sessionId) => {
|
|
6
|
+
throw new Error(`parked decide cannot verify which occupant session ${sessionId} belongs to: this deployment's session store has no sessionFaceOf read (every built-in store has one) — refusing rather than resuming unverified`);
|
|
7
|
+
};
|
|
5
8
|
import { HttpError, decodeCheckpointScope, CHECKPOINT_PUBLIC_SCOPE } from "../security.js";
|
|
6
9
|
import { evictIfConflict, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
|
|
7
10
|
import { failureCodeOf, nestedPauseOf, sealResultForExit, outwardPlane } from "../terminal.js";
|
|
@@ -36,6 +39,7 @@ import { buildActiveRunConflict, resumeEntryForGate } from "./active-run-conflic
|
|
|
36
39
|
import { commitPlacement, devicePlacementErrorBody } from "./device-placement.js";
|
|
37
40
|
import { ASK_USER_QUESTION_TOOL_NAME } from "../approval-content-kind.js";
|
|
38
41
|
import { readResumeCtx, resumeCtxCorruptAnswer } from "./resume-ctx-read.js";
|
|
42
|
+
import { httpErrorCode } from "./send.js";
|
|
39
43
|
function isCheckpointReopenedFailure(r) {
|
|
40
44
|
const code = failureCodeOf(r.terminal);
|
|
41
45
|
return code === "resume.env_failed" || code === "resume.tool_unavailable" || code === "resume.tool_contract_mismatch";
|
|
@@ -258,7 +262,7 @@ export function createResumeLegs(ctx) {
|
|
|
258
262
|
checkpointStore: cs,
|
|
259
263
|
registry: defaultTaskRegistry,
|
|
260
264
|
reviveSurface: parkedSurface,
|
|
261
|
-
|
|
265
|
+
sessionFaceOf: deps.sessionStorage?.sessionFaceOf ? deps.sessionStorage.sessionFaceOf.bind(deps.sessionStorage) : sessionFaceUnavailable,
|
|
262
266
|
...(deps.parkedKnownAgentTypes ? { knownAgentTypes: deps.parkedKnownAgentTypes } : {}),
|
|
263
267
|
...(deps.parkedReviveInheritedGate ? { rebuildInheritedGate: deps.parkedReviveInheritedGate } : {}),
|
|
264
268
|
...(deps.logger ? { warn: parkWarn } : {}),
|
|
@@ -341,7 +345,7 @@ export function createResumeLegs(ctx) {
|
|
|
341
345
|
ok: false,
|
|
342
346
|
answer: {
|
|
343
347
|
status: 409,
|
|
344
|
-
body: { error: `resume blocked by an admission policy change: ${e.message}`, errorCode: "resume_blocked_by_policy",
|
|
348
|
+
body: { error: `resume blocked by an admission policy change: ${e.message}`, errorCode: "resume_blocked_by_policy", blockedBy: httpErrorCode(e.status, e.code), ...(e.extra ?? {}) },
|
|
345
349
|
},
|
|
346
350
|
};
|
|
347
351
|
}
|
|
@@ -908,7 +912,7 @@ export function createResumeLegs(ctx) {
|
|
|
908
912
|
settleFleet("failed");
|
|
909
913
|
}
|
|
910
914
|
else if (retriable) {
|
|
911
|
-
await (outcome.gate === "plan_review" ? deps.runStore.setNeedsReview(taskId) : deps.runStore.setSuspended(taskId)).catch(() => undefined);
|
|
915
|
+
await (outcome.gate === "plan_review" ? deps.runStore.setNeedsReview(taskId, null) : deps.runStore.setSuspended(taskId, null)).catch(() => undefined);
|
|
912
916
|
settleFleet(outcome.gate === "plan_review" ? "needs_review" : "suspended");
|
|
913
917
|
}
|
|
914
918
|
else {
|
|
@@ -973,9 +977,9 @@ export function createResumeLegs(ctx) {
|
|
|
973
977
|
const resumePlane = outwardPlane(sealed.terminal);
|
|
974
978
|
if (taskId && deps.runStore) {
|
|
975
979
|
if (resumePlane.status === "suspended" || checkpointReopened)
|
|
976
|
-
await deps.runStore.setSuspended(taskId);
|
|
980
|
+
await deps.runStore.setSuspended(taskId, sealed.terminal.kind === "paused" ? sealed : null);
|
|
977
981
|
else if (resumePlane.status === "needs_review")
|
|
978
|
-
await deps.runStore.setNeedsReview(taskId);
|
|
982
|
+
await deps.runStore.setNeedsReview(taskId, sealed);
|
|
979
983
|
else
|
|
980
984
|
await deps.runStore.setTerminal(taskId, resumePlane.status, sealed, resumePlane.errorMessage ?? null);
|
|
981
985
|
}
|
|
@@ -25,10 +25,17 @@
|
|
|
25
25
|
* · **能力位同源**:`capabilities.agentRoster` 与这里的 501 谓词是同一个 `deps.backgroundAgentStore`。
|
|
26
26
|
*/
|
|
27
27
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
28
|
+
import type { BackgroundAgentRowSummary } from "@sema-agent/core";
|
|
28
29
|
import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
|
|
29
30
|
/** 名册窗的扫描上限(与 fleet 的终态窗同姿势:读面要有界)。窗满时回体带 `truncated:true`,
|
|
30
31
|
* `total` 于是诚实地是「窗内行数」而不是租户全量 —— 一个悄悄封顶的 total 比没有 total 更坏。 */
|
|
31
32
|
export declare const ROSTER_SCAN_LIMIT = 500;
|
|
33
|
+
/**
|
|
34
|
+
* 后台子代在 wire 上的 **id 投影**(唯一一只):名册行的 `handle` 与 `DELETE /v1/sessions/:id` 的 409
|
|
35
|
+
* `conflict.session_children_alive` 体里 `remaining[]` 的每一项都经它出门(S-790,裁定 2026-09-28)—— 两个面上
|
|
36
|
+
* 「同一只子代」必须是同一个串,消费方拿删除答复里的 id 才对得上名册里的行、才能去它运行的地方停它。
|
|
37
|
+
*/
|
|
38
|
+
export declare function rosterAgentId(row: Pick<BackgroundAgentRowSummary, "handle">): string;
|
|
32
39
|
export declare function handleAgentRoster(req: IncomingMessage, res: ServerResponse, match: RouteMatch<AgentRosterRouteId>, ctx: RouteCtx): Promise<void>;
|
|
33
40
|
export declare const AGENT_ROSTER_ROUTES: readonly [{
|
|
34
41
|
readonly id: "agents-roster";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { sendJson, sendError } from "../send.js";
|
|
2
|
+
import { sendUnauthorized } from "../principal-gate.js";
|
|
2
3
|
import { assertNever } from "../route-ctx.js";
|
|
3
4
|
export const ROSTER_SCAN_LIMIT = 500;
|
|
4
5
|
const ROSTER_STATUSES = ["running", "parked", "completed", "failed", "killed"];
|
|
@@ -18,9 +19,12 @@ const ROSTER_KEY_DISPOSITION = {
|
|
|
18
19
|
usage: "strip",
|
|
19
20
|
};
|
|
20
21
|
void ROSTER_KEY_DISPOSITION;
|
|
22
|
+
export function rosterAgentId(row) {
|
|
23
|
+
return row.handle;
|
|
24
|
+
}
|
|
21
25
|
function projectRow(r) {
|
|
22
26
|
return {
|
|
23
|
-
handle: r
|
|
27
|
+
handle: rosterAgentId(r),
|
|
24
28
|
...(r.name !== undefined ? { name: r.name } : {}),
|
|
25
29
|
...(r.agentType !== undefined ? { agentType: r.agentType } : {}),
|
|
26
30
|
status: r.status,
|
|
@@ -56,7 +60,7 @@ export async function handleAgentRoster(req, res, match, ctx) {
|
|
|
56
60
|
case "agents-roster": {
|
|
57
61
|
const { fleetWide, gateOwner } = sessionOwnerScope(req);
|
|
58
62
|
if (!fleetWide && gateOwner === null) {
|
|
59
|
-
|
|
63
|
+
sendUnauthorized(res, "identity");
|
|
60
64
|
return;
|
|
61
65
|
}
|
|
62
66
|
const store = deps.backgroundAgentStore;
|
|
@@ -3,31 +3,59 @@ import type { QuestionAnswer } from "@sema-agent/core";
|
|
|
3
3
|
import type { CheckpointStoreFull } from "../../plugins/store-backend.js";
|
|
4
4
|
import { type ToolApprovalCoordinator } from "../../tool-approval.js";
|
|
5
5
|
import { type GovernancePosture } from "../../runtime-governance.js";
|
|
6
|
-
import { type RouteCtx, type RouteMatch, type RouteIdsOf } from "../route-ctx.js";
|
|
6
|
+
import { type RouteCtx, type RouteDecl, type RouteMatch, type RouteIdsOf } from "../route-ctx.js";
|
|
7
7
|
import { type KeyClosureIssue } from "../../request-key-closure.js";
|
|
8
|
+
/** 族的成员 = 路由表行 id(改名 / 删行 ⇒ 下表的键当场编译红)。 */
|
|
9
|
+
export type DecisionDoorId = Extract<ApprovalsAssistantRouteId, "approval-decide" | "assistant-task-plan-review">;
|
|
10
|
+
/** 一道门的静态描述 —— 只写**这道门自己**的东西;路径、路径段、别门的键一概不在这里写(从来源读)。 */
|
|
11
|
+
export interface DecisionDoor {
|
|
12
|
+
/** 体的受理集 = 本门的 `*_BODY_DECLARED_KEYS`(那张表的双向编译期门照旧咬着它的类型)。 */
|
|
13
|
+
readonly accepted: readonly string[];
|
|
14
|
+
/** `unknown` 桶标签里的门名(「not a <这里> key — check the spelling」)。 */
|
|
15
|
+
readonly requestNoun: string;
|
|
16
|
+
/** 拒体引子里「这道门上拼错一个键最贵的后果」那半句(人读;每门一句)。 */
|
|
17
|
+
readonly costliestTypo: string;
|
|
18
|
+
}
|
|
19
|
+
export type DecisionDoorFamily = {
|
|
20
|
+
readonly [K in DecisionDoorId]: DecisionDoor;
|
|
21
|
+
};
|
|
22
|
+
/** 「名字是真的、只是不在这个体上」的三族(闭集;加第四族 = 下面 `switch` 编译红)。 */
|
|
23
|
+
type NeighborFamily = "sibling_field" | "path_segment" | "submit_body";
|
|
24
|
+
/** 一个邻键:它属哪一族 + 它在哪儿是真的(wire 路径,可多条)。 */
|
|
25
|
+
interface NeighborKey {
|
|
26
|
+
readonly family: NeighborFamily;
|
|
27
|
+
readonly where: readonly string[];
|
|
28
|
+
}
|
|
29
|
+
/** 派生结果里一道门的全部判据(受理集 + 邻键表 + 两句人读)。 */
|
|
30
|
+
export interface DerivedDecisionDoor {
|
|
31
|
+
readonly accepted: ReadonlySet<string>;
|
|
32
|
+
readonly neighbors: ReadonlyMap<string, NeighborKey>;
|
|
33
|
+
readonly requestNoun: string;
|
|
34
|
+
readonly costliestTypo: string;
|
|
35
|
+
}
|
|
36
|
+
export type DecisionDoorKeyTable = {
|
|
37
|
+
readonly [K in DecisionDoorId]: DerivedDecisionDoor;
|
|
38
|
+
};
|
|
8
39
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* 🔴 **重放不额外过门**,也不需要:本腿的幂等回放(`decideIdempotentReplay`)重放的是**同一次请求的同一份
|
|
15
|
-
* 体**,它与首决走同一条路由、同一道门;而 checkpoint 的 resume 重建腿根本不读这个体(它读盘上的 body,
|
|
16
|
-
* 走 `resolveSpec` 的宽容层)⇒ 4xx 砖不到任何存量 parked 任务。这与 tasks 面「RESUME 重放不过门」是同一条律
|
|
17
|
-
* 的两种兑现方式:**门只站在人**(或客户端)**现写的那份字节**上。
|
|
40
|
+
* 回决门族的键闭集判据表 —— **唯一**的派生口(纯函数:同一份族描述 + 同一张路由表 ⇒ 同一张表)。
|
|
41
|
+
* 生产只在本模块尾调一次(路由表声明在文件尾,见 {@link DECISION_DOOR_KEY_TABLE});测试可以喂一份构造的族
|
|
42
|
+
* (「给兄弟门受理集加一个假键 ⇒ 本门的 unsupported 集自动多这一键」就是这么钉的)。
|
|
43
|
+
* 🔴 族两两不交是**前提**不是愿望:同一个键若既是某门的体字段又是某条 URL 的路径段,它的去处就有两个答案
|
|
44
|
+
* ⇒ 抛(模块加载期 = 启动即炸,不是请求期 500)。
|
|
18
45
|
*/
|
|
19
|
-
export declare function
|
|
46
|
+
export declare function deriveDecisionDoorKeyTable(family: DecisionDoorFamily, routes: readonly RouteDecl[]): DecisionDoorKeyTable;
|
|
20
47
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* {
|
|
24
|
-
*
|
|
48
|
+
* 回决门族**一道门**的顶层键闭集裁决:受理集之外的键 ⇒ 拒体(调用方 400 `request.body_shape`);全部受理 ⇒ `null`。
|
|
49
|
+
* 判据边界与两个孪生门(`taskBodyKeyIssue` / `taskSettingsKeyIssue`)逐字同一条:**只判键名**(值形校验归各自
|
|
50
|
+
* 既有的门,本门零抢话);体本身非对象 ⇒ 本门不判(留给「body must be { decision … }」那道根形门,免得同一个体
|
|
51
|
+
* 被两句话抢答)。
|
|
25
52
|
*
|
|
26
|
-
* 🔴
|
|
27
|
-
*
|
|
28
|
-
*
|
|
53
|
+
* 🔴 **重放不额外过门**,也不需要:`/decide` 的幂等回放(`decideIdempotentReplay`)重放的是**同一次请求的同一份
|
|
54
|
+
* 体**、走同一条路由同一道门;plan_review 的反重放是一次性 nonce(S-280),守的也是同一份现写的字节;而续跑重建
|
|
55
|
+
* 读的是盘上的体、走 `resolveSpec` 的宽容层 ⇒ 4xx 砖不到任何存量挂起的任务。**门只站在人(或客户端)现写的
|
|
56
|
+
* 那份字节上**(与 tasks 面「RESUME 重放不过门」同一条律)。
|
|
29
57
|
*/
|
|
30
|
-
export declare function
|
|
58
|
+
export declare function decisionBodyKeyIssue(door: DecisionDoorId, raw: unknown, table?: DecisionDoorKeyTable): KeyClosureIssue | null;
|
|
31
59
|
/**
|
|
32
60
|
* S-342 —— 「空串的理由 = 没有理由 = 这是一次**裸拒**」。这条语义只适用于**本仓自铸**的那一个域。
|
|
33
61
|
*
|
|
@@ -147,4 +175,5 @@ export declare const APPROVALS_ASSISTANT_ROUTES: readonly [{
|
|
|
147
175
|
}];
|
|
148
176
|
/** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
|
|
149
177
|
export type ApprovalsAssistantRouteId = RouteIdsOf<typeof APPROVALS_ASSISTANT_ROUTES>;
|
|
178
|
+
export {};
|
|
150
179
|
//# sourceMappingURL=approvals-assistant.d.ts.map
|