@sema-agent/server 7.136.0 → 7.137.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 +173 -0
- package/USAGE.md +84 -16
- package/deploy/sema-up/README.md +14 -0
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
- package/dist/approval-content-kind.d.ts +6 -0
- package/dist/approval-content-kind.js +7 -0
- package/dist/approval-gate-currency.d.ts +48 -6
- package/dist/approval-gate-currency.js +16 -9
- package/dist/approval-hmac.d.ts +13 -1
- package/dist/approval-hmac.js +1 -1
- package/dist/approval.js +3 -3
- package/dist/auth-bridge.js +2 -2
- package/dist/boot/execution-env.d.ts +24 -1
- package/dist/boot/execution-env.js +69 -6
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +15 -1
- package/dist/boot/resolve-spec.d.ts +4 -0
- package/dist/boot/resolve-spec.js +16 -17
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +3 -1
- package/dist/boot/stage-05-execution-env.js +11 -2
- package/dist/boot/stage-06-runners.d.ts +3 -1
- package/dist/boot/stage-06-runners.js +5 -1
- package/dist/boot/stage-07-capability-layer.d.ts +4 -1
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +4 -1
- package/dist/boot/stage-08-reapers.js +2 -1
- package/dist/boot/stage-09-leader.d.ts +4 -1
- package/dist/boot/stage-10-http-server.d.ts +4 -1
- package/dist/boot/stage-10-http-server.js +3 -2
- package/dist/boot/stores.d.ts +1 -1
- package/dist/broker/broker.d.ts +89 -0
- package/dist/broker/broker.js +287 -0
- package/dist/broker/constants.d.ts +39 -0
- package/dist/broker/constants.js +15 -0
- package/dist/broker/http1.d.ts +111 -0
- package/dist/broker/http1.js +256 -0
- package/dist/broker/lease-book.d.ts +87 -0
- package/dist/broker/lease-book.js +142 -0
- package/dist/broker/lease-file.d.ts +60 -0
- package/dist/broker/lease-file.js +127 -0
- package/dist/broker/main.d.ts +2 -0
- package/dist/broker/main.js +20 -0
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/engine-capabilities.js +1 -0
- package/dist/capabilities/scenarios.d.ts +23 -8
- package/dist/capabilities/scenarios.js +13 -8
- package/dist/capabilities/skills.d.ts +28 -5
- package/dist/capabilities/skills.js +8 -0
- package/dist/center-credential-renewer.js +5 -1
- package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
- package/dist/checkpoint-ctx-seal-m28.js +1 -1
- package/dist/checkpoint-identity.d.ts +3 -3
- package/dist/config-catalog.js +9 -6
- package/dist/config-types.d.ts +9 -0
- package/dist/config.d.ts +1 -1
- package/dist/config.js +34 -8
- package/dist/deployment-governance.d.ts +2 -2
- package/dist/deployment-governance.js +7 -9
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/file-history-disclosure.d.ts +4 -2
- package/dist/file-history-disclosure.js +2 -0
- package/dist/hooks/classic-stations.d.ts +11 -54
- package/dist/http/admission.js +10 -7
- package/dist/http/approval-sweeps.js +4 -3
- package/dist/http/caller-trust.d.ts +2 -1
- package/dist/http/caller-trust.js +1 -3
- package/dist/http/checkpoint-ctx.d.ts +48 -7
- package/dist/http/checkpoint-ctx.js +40 -0
- package/dist/http/dispatch.js +15 -10
- package/dist/http/durable-run-leg.d.ts +5 -1
- package/dist/http/durable-run-leg.js +1 -1
- package/dist/http/health-self-report.d.ts +2 -2
- package/dist/http/mcp-remote-text.d.ts +5 -0
- package/dist/http/mcp-remote-text.js +7 -0
- package/dist/http/principal-gate.d.ts +15 -10
- package/dist/http/principal-gate.js +48 -9
- package/dist/http/resume-legs.d.ts +2 -20
- package/dist/http/resume-legs.js +52 -38
- package/dist/http/route-ctx.d.ts +8 -2
- package/dist/http/routes/approvals-assistant.js +18 -4
- package/dist/http/routes/capabilities.js +11 -1
- package/dist/http/routes/mcp-reconnect.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +5 -7
- package/dist/http/routes/runs.js +2 -1
- package/dist/http/routes/session-sync.js +42 -6
- package/dist/http/routes/sessions.js +2 -1
- package/dist/http/routes/tasks.js +8 -6
- package/dist/http/server.d.ts +4 -0
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +6 -1
- package/dist/http/wire-types.d.ts +52 -27
- package/dist/identity-source.d.ts +65 -0
- package/dist/identity-source.js +45 -0
- package/dist/memory-store-refusal.d.ts +3 -3
- package/dist/model-entry-refusal.d.ts +2 -0
- package/dist/model-entry-refusal.js +11 -0
- package/dist/model-select.d.ts +6 -17
- package/dist/model-select.js +0 -3
- package/dist/namespaced-rule-name.d.ts +1 -1
- package/dist/observability/fail-open.d.ts +13 -1
- package/dist/observability/fail-open.js +13 -1
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/operator-ask.d.ts +2 -2
- package/dist/org-delivery-gate.js +1 -1
- package/dist/org-memory-admission.d.ts +2 -2
- package/dist/org-memory-admission.js +39 -2
- package/dist/parked-decide.js +3 -2
- package/dist/peer-directory.js +2 -0
- package/dist/permission-rule-list.d.ts +2 -2
- package/dist/plugins/checkpoint-store-sql.d.ts +19 -9
- package/dist/plugins/checkpoint-store-sql.js +6 -6
- package/dist/plugins/file-history-store-sql.js +7 -14
- package/dist/plugins/file-run-store.js +8 -2
- package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
- package/dist/plugins/k8s-exec-protocol.js +116 -28
- package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
- package/dist/plugins/k8s-pod-delete-failure.js +17 -0
- package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
- package/dist/plugins/k8s-pod-reaper.js +122 -0
- package/dist/plugins/local-checkpoint-store.d.ts +3 -6
- package/dist/plugins/local-checkpoint-store.js +3 -3
- package/dist/plugins/memory-run-store.js +2 -0
- package/dist/plugins/one-time-migrations.d.ts +28 -9
- package/dist/plugins/one-time-migrations.js +48 -5
- package/dist/plugins/permission-rule-store-file.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
- package/dist/plugins/permission-rule-store-sql.js +16 -17
- package/dist/plugins/pg-pool.js +4 -0
- package/dist/plugins/pg-session-storage.js +2 -2
- package/dist/plugins/remote-env-adb.d.ts +3 -0
- package/dist/plugins/remote-env-adb.js +1 -0
- package/dist/plugins/remote-env-device.d.ts +3 -0
- package/dist/plugins/remote-env-device.js +4 -0
- package/dist/plugins/remote-env-e2b.d.ts +3 -0
- package/dist/plugins/remote-env-e2b.js +1 -0
- package/dist/plugins/remote-env-file-error.d.ts +8 -1
- package/dist/plugins/remote-env-file-error.js +7 -1
- package/dist/plugins/remote-env-host.d.ts +7 -1
- package/dist/plugins/remote-env-host.js +7 -1
- package/dist/plugins/remote-env-k8s.d.ts +137 -10
- package/dist/plugins/remote-env-k8s.js +276 -14
- package/dist/plugins/remote-env-local-docker.d.ts +3 -0
- package/dist/plugins/remote-env-local-docker.js +1 -0
- package/dist/plugins/remote-env-ssh.d.ts +3 -0
- package/dist/plugins/remote-env-ssh.js +1 -0
- package/dist/plugins/run-store-sql.js +10 -4
- package/dist/plugins/session-ownership.d.ts +17 -3
- package/dist/plugins/session-ownership.js +19 -5
- package/dist/plugins/sql-errors.d.ts +23 -2
- package/dist/plugins/sql-errors.js +15 -0
- package/dist/plugins/store-contracts.d.ts +10 -1
- package/dist/plugins/store-fault.d.ts +2 -2
- package/dist/plugins/store-fault.js +4 -2
- package/dist/plugins/tidb-pool.js +7 -0
- package/dist/plugins/tidb-session-store.js +2 -2
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +31 -2
- package/dist/request-value-closure.js +7 -1
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +2 -2
- package/dist/rules-consent.js +7 -4
- package/dist/run-local.d.ts +2 -2
- package/dist/runs.js +2 -1
- package/dist/runtime-governance.d.ts +5 -5
- package/dist/sandbox-git-lease.d.ts +49 -0
- package/dist/sandbox-git-lease.js +60 -0
- package/dist/sandbox-git-plane.d.ts +84 -0
- package/dist/sandbox-git-plane.js +105 -0
- package/dist/security.d.ts +16 -4
- package/dist/security.js +8 -3
- package/dist/server-secret-env.d.ts +6 -0
- package/dist/server-secret-env.js +14 -0
- package/dist/session-error-wire.d.ts +14 -6
- package/dist/session-error-wire.js +2 -1
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-policy-wire.js +2 -2
- package/dist/session-sync.d.ts +1 -1
- package/dist/spec-fields.d.ts +43 -13
- package/dist/spec-fields.js +33 -12
- package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
- package/dist/stored-request-prompt-profile-m30.js +25 -0
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +2 -2
- package/dist/tool-approval.d.ts +12 -2
- package/dist/tool-approval.js +8 -3
- package/dist/tool-rule-names.d.ts +5 -0
- package/dist/trace/core-keyset-guard.d.ts +4 -4
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.js +2 -2
- package/dist/trace/ledger-sink.d.ts +4 -5
- package/dist/trace/ledger-sink.js +3 -7
- package/dist/trace/project.d.ts +37 -3
- package/dist/trace/project.js +20 -6
- package/dist/trace/redact.d.ts +18 -6
- package/dist/trace/redact.js +118 -53
- package/dist/trace/self-minted-wire.d.ts +377 -3
- package/dist/trace/self-minted-wire.js +122 -0
- package/dist/trace/wire-projection-faces.d.ts +5 -3
- package/dist/trace/wire-projection-faces.js +7 -3
- package/dist/web-admin-client/apply-effective.js +0 -1
- package/dist/web-admin-client/center-request.d.ts +20 -2
- package/dist/web-admin-client/center-request.js +40 -5
- package/dist/web-admin-client/http-client.js +6 -6
- package/dist/web-admin-client/types.d.ts +5 -5
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
- package/package.json +2 -2
- package/wire-changes/server@7.137.0.json +825 -0
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* 引擎的 `classic.*` 站 —— 本仓挂 handler 的唯一入口(core 7.52.0 起扩展点 = mods 注册,`Hooks` 记录退役)。
|
|
3
3
|
*
|
|
4
4
|
* 三件事只写在这里:
|
|
5
|
-
* ① **事件名 → { `e` 型, 答案型 }
|
|
6
|
-
*
|
|
5
|
+
* ① **事件名 → { `e` 型, 答案型 }**:读 core 的 `ClassicEventMap`(7.56.0 起包根导出;此前本文件手写十一行配对,随之删)。
|
|
6
|
+
* 每站的角色(链 / 观察)由 {@link CLASSIC_STATION_ROLE} 说,它在编译期对着 core 的答案形核:答案形是 `ObservedAnswer` 的站必须是 `observe`、其余是 `chain`。
|
|
7
7
|
* ② **handler 怎么挂**:链成员「无意见 ⇒ `next(e)`」(链 handler 返回 `undefined` 是站点违约,refuse 站上即 fail-closed 拒),
|
|
8
8
|
* 观察者「恒返回 `undefined`」(观察者的判词不采纳、被引擎报告)。两条都在 {@link hangClassic} / {@link observeClassic} 里成形,
|
|
9
9
|
* 调用方只写「这一站我答什么」或「这一站我看什么」,不再各自记得这两条。
|
|
@@ -13,55 +13,9 @@
|
|
|
13
13
|
* 站的集合 = 引擎事件表里有站的 `classic.*` 名(一条钉从 `MOD_EVENT_TABLE` 派生对拍,不手抄第二份)。
|
|
14
14
|
* 观察站(`StopFailure` / `PostCompact` / `PermissionDenied`)只许观察者:答案形是「无」,core 的迁移配方要求挂 `{ observe: true }`。
|
|
15
15
|
*/
|
|
16
|
-
import type {
|
|
17
|
-
/**
|
|
18
|
-
export
|
|
19
|
-
"classic.PreToolUse": {
|
|
20
|
-
e: PreToolUseEvent;
|
|
21
|
-
answer: PreToolUseResult;
|
|
22
|
-
};
|
|
23
|
-
"classic.PostToolUse": {
|
|
24
|
-
e: PostToolUseEvent;
|
|
25
|
-
answer: PostToolUseResult;
|
|
26
|
-
};
|
|
27
|
-
"classic.PostToolUseFailure": {
|
|
28
|
-
e: PostToolUseFailureEvent;
|
|
29
|
-
answer: PostToolUseFailureResult;
|
|
30
|
-
};
|
|
31
|
-
"classic.PostToolBatch": {
|
|
32
|
-
e: PostToolBatchEvent;
|
|
33
|
-
answer: PostToolBatchResult;
|
|
34
|
-
};
|
|
35
|
-
"classic.UserPromptSubmit": {
|
|
36
|
-
e: UserPromptSubmitEvent;
|
|
37
|
-
answer: UserPromptSubmitResult;
|
|
38
|
-
};
|
|
39
|
-
"classic.Stop": {
|
|
40
|
-
e: StopEvent;
|
|
41
|
-
answer: StopHookResult;
|
|
42
|
-
};
|
|
43
|
-
"classic.SubagentStop": {
|
|
44
|
-
e: StopEvent;
|
|
45
|
-
answer: StopHookResult;
|
|
46
|
-
};
|
|
47
|
-
"classic.StopFailure": {
|
|
48
|
-
e: StopFailureEvent;
|
|
49
|
-
answer: void;
|
|
50
|
-
};
|
|
51
|
-
"classic.PreCompact": {
|
|
52
|
-
e: PreCompactEvent;
|
|
53
|
-
answer: PreCompactResult;
|
|
54
|
-
};
|
|
55
|
-
"classic.PostCompact": {
|
|
56
|
-
e: PostCompactEvent;
|
|
57
|
-
answer: void;
|
|
58
|
-
};
|
|
59
|
-
"classic.PermissionDenied": {
|
|
60
|
-
e: PermissionDeniedPayload;
|
|
61
|
-
answer: void;
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
export type ClassicStation = keyof ClassicStationTypes;
|
|
16
|
+
import type { ClassicEventMap, ClassicEventName, EngineDollar, On } from "@sema-agent/core";
|
|
17
|
+
/** 引擎有站的 `classic.*` 名(core 的 `ClassicEventMap` 键集)。 */
|
|
18
|
+
export type ClassicStation = ClassicEventName;
|
|
65
19
|
/** 每一站能挂什么:`chain` = 链成员(会作答)与观察者都可;`observe` = 只许观察者。 */
|
|
66
20
|
export declare const CLASSIC_STATION_ROLE: {
|
|
67
21
|
readonly "classic.PreToolUse": "chain";
|
|
@@ -76,16 +30,19 @@ export declare const CLASSIC_STATION_ROLE: {
|
|
|
76
30
|
readonly "classic.PostCompact": "observe";
|
|
77
31
|
readonly "classic.PermissionDenied": "observe";
|
|
78
32
|
};
|
|
33
|
+
/** 本仓 handler 体对一站作的答:观察站不作答(`void`);链站 = core 的答案形,去掉本仓从不作的 `null` / `false`(PreToolUse 的两个「不表态」词)。 */
|
|
34
|
+
type StationAnswer<K extends ClassicStation> = (typeof CLASSIC_STATION_ROLE)[K] extends "observe" ? void : Exclude<ClassicEventMap[K]["answer"], null | false>;
|
|
79
35
|
/**
|
|
80
36
|
* 一站的 handler 体:读 `e`,答这一站的答案;`undefined` = 无意见(链成员由 {@link hangClassic} 交给 `next(e)`)。
|
|
81
37
|
* `signal` = 引擎交给这只 handler 的 `next.signal`(预算到 ∪ 派发被取消;见头注 ③)。
|
|
82
38
|
*/
|
|
83
|
-
export type ClassicAnswer<K extends ClassicStation> = (e:
|
|
39
|
+
export type ClassicAnswer<K extends ClassicStation> = (e: ClassicEventMap[K]["e"], signal: AbortSignal) => Promise<StationAnswer<K> | undefined>;
|
|
84
40
|
/**
|
|
85
41
|
* 按站的角色挂一只 handler:链站 ⇒ 链成员 `(await answer(e)) ?? next(e)`;观察站 ⇒ 观察者(体照跑、恒返回 `undefined`)。
|
|
86
|
-
* `e` 的型由
|
|
42
|
+
* `e` 的型由 core 的 `ClassicEventMap` 给(引擎交来的是本站的 `e`;见头注 ①)。
|
|
87
43
|
*/
|
|
88
44
|
export declare function hangClassic<K extends ClassicStation>(on: On<EngineDollar>, station: K, answer: ClassicAnswer<K>): void;
|
|
89
45
|
/** 挂一只观察者(任何站都可):拿分离副本、不进链、恒返回 `undefined`;`signal` 同 {@link ClassicAnswer}。 */
|
|
90
|
-
export declare function observeClassic<K extends ClassicStation>(on: On<EngineDollar>, station: K, watch: (e:
|
|
46
|
+
export declare function observeClassic<K extends ClassicStation>(on: On<EngineDollar>, station: K, watch: (e: ClassicEventMap[K]["e"], signal: AbortSignal) => void | Promise<void>): void;
|
|
47
|
+
export {};
|
|
91
48
|
//# sourceMappingURL=classic-stations.d.ts.map
|
package/dist/http/admission.js
CHANGED
|
@@ -12,7 +12,7 @@ import { FILE_HISTORY_DISABLED_NO_RESTORE, fileHistoryRestoreConflict, fileHisto
|
|
|
12
12
|
import * as facts from "../capability-facts.js";
|
|
13
13
|
import { buildHooksRefusal, buildHooksUrlCredentialRefusal, parseHooksConfig, hooksMatcherIssue, hooksUrlCredentialIssue } from "../hooks/hook-runner.js";
|
|
14
14
|
import { judgeFreshRequestWebSearch } from "../plugins/web-search.js";
|
|
15
|
-
import { normalizeApproachNotice, skillToolListOk, unknownTaskAgentModelRef, validateTaskAgents } from "../spec-fields.js";
|
|
15
|
+
import { ATTACHMENT_BOOLEAN_KEYS, ATTACHMENT_KEYS, normalizeApproachNotice, skillToolListOk, unknownTaskAgentModelRef, validateTaskAgents } from "../spec-fields.js";
|
|
16
16
|
import { allowSelectorWarningsOf, skillToolListsOverCap } from "../rule-entry-list.js";
|
|
17
17
|
import { limitFacts, limitRefusal } from "../limit-refusal.js";
|
|
18
18
|
import { fieldConflictFacts } from "../field-conflict.js";
|
|
@@ -78,6 +78,8 @@ function userSkillShapeIssue(item, index) {
|
|
|
78
78
|
}
|
|
79
79
|
if (skillCarriesRetiredManifest(s))
|
|
80
80
|
return { message: SKILL_MANIFEST_RETIRED };
|
|
81
|
+
if (s["mods"] !== undefined)
|
|
82
|
+
return { message: "skill.mods is not a request key — skill registrations are code the deployment mounts, not request data (this server does not compile skill hooks: GET /v1/capabilities answers mods.skill_seat: false)" };
|
|
81
83
|
for (const key of ["allowedTools", "disallowedTools"]) {
|
|
82
84
|
if (s[key] !== undefined && !skillToolListOk(s[key]))
|
|
83
85
|
return { message: `skill.${key} must be an array of strings (permission-rule spellings: a tool name, mcp__<server> / mcp__<server>__*, Bash(<prefix>:*), Read(//<dir>/**))` };
|
|
@@ -457,7 +459,7 @@ export function createAdmission(ctx) {
|
|
|
457
459
|
{
|
|
458
460
|
const inheritEnvIssue = inheritEnvFieldIssue(body);
|
|
459
461
|
if (inheritEnvIssue) {
|
|
460
|
-
sendError(res, 400, "request.field_invalid", inheritEnvIssue);
|
|
462
|
+
sendError(res, 400, "request.field_invalid", inheritEnvIssue.message, inheritEnvIssue.facts);
|
|
461
463
|
return null;
|
|
462
464
|
}
|
|
463
465
|
}
|
|
@@ -618,9 +620,9 @@ export function createAdmission(ctx) {
|
|
|
618
620
|
}
|
|
619
621
|
}
|
|
620
622
|
{
|
|
621
|
-
const v = body.
|
|
622
|
-
if (v !== undefined &&
|
|
623
|
-
sendError(res, 400, "request.field_invalid",
|
|
623
|
+
const v = body.promptLean;
|
|
624
|
+
if (v !== undefined && typeof v !== "boolean") {
|
|
625
|
+
sendError(res, 400, "request.field_invalid", "promptLean must be a boolean");
|
|
624
626
|
return null;
|
|
625
627
|
}
|
|
626
628
|
}
|
|
@@ -687,7 +689,8 @@ export function createAdmission(ctx) {
|
|
|
687
689
|
if (body.attachments !== undefined) {
|
|
688
690
|
const a = body.attachments;
|
|
689
691
|
if (typeof a !== "object" || a === null || Array.isArray(a)) {
|
|
690
|
-
|
|
692
|
+
const attachmentKeyList = ATTACHMENT_KEYS.map((k) => `${k}?`).join(", ");
|
|
693
|
+
sendError(res, 400, "request.field_invalid", `attachments must be an object { ${attachmentKeyList} }`);
|
|
691
694
|
return null;
|
|
692
695
|
}
|
|
693
696
|
const rec = a;
|
|
@@ -695,7 +698,7 @@ export function createAdmission(ctx) {
|
|
|
695
698
|
sendError(res, 400, "request.field_invalid", 'attachments.planModeReminder was retired in core 7.39.0 — the engine now states plan posture itself whenever the permission mode is "plan" (first request, periodic reminders, exit frame); remove the key (send permissionMode "plan" for plan posture)');
|
|
696
699
|
return null;
|
|
697
700
|
}
|
|
698
|
-
for (const k of
|
|
701
|
+
for (const k of ATTACHMENT_BOOLEAN_KEYS) {
|
|
699
702
|
if (rec[k] !== undefined && typeof rec[k] !== "boolean") {
|
|
700
703
|
sendError(res, 400, "request.field_invalid", `attachments.${k} must be a boolean`);
|
|
701
704
|
return null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { redactHead } from "../observability/run-terminal.js";
|
|
2
2
|
const errText = (e) => redactHead(e instanceof Error ? e.message : String(e), 2000);
|
|
3
|
+
import { checkpointRevOf } from "@sema-agent/core";
|
|
3
4
|
import { findParkOrigin } from "../parked-decide.js";
|
|
4
5
|
import { decodeCheckpointScope } from "../security.js";
|
|
5
6
|
import { hostDecisionFacts } from "../host-decision.js";
|
|
@@ -89,7 +90,7 @@ export function createApprovalSweeps(ctx) {
|
|
|
89
90
|
const cp = await cs.get(token);
|
|
90
91
|
if (!cp)
|
|
91
92
|
return { token, parked: false, rev: undefined };
|
|
92
|
-
const rev = cp
|
|
93
|
+
const rev = checkpointRevOf(cp);
|
|
93
94
|
if (!parkSources)
|
|
94
95
|
return { token, parked: false, rev };
|
|
95
96
|
const origin = await findParkOrigin({
|
|
@@ -120,7 +121,7 @@ export function createApprovalSweeps(ctx) {
|
|
|
120
121
|
const cp = await cs.get(cardToken);
|
|
121
122
|
if (!cp)
|
|
122
123
|
return;
|
|
123
|
-
if (cp.status !== "pending" || (cp
|
|
124
|
+
if (cp.status !== "pending" || checkpointRevOf(cp) !== observedRev) {
|
|
124
125
|
deps.logger?.warn?.("deny_sweep_policy_blocked_expire_skipped", { sessionId, skipReason: cp.status !== "pending" ? "not_pending" : "card_changed", ...blockedScalars(body) });
|
|
125
126
|
return;
|
|
126
127
|
}
|
|
@@ -177,7 +178,7 @@ export function createApprovalSweeps(ctx) {
|
|
|
177
178
|
deps.logger?.warn?.("deny_sweep_stored_body_skipped", {
|
|
178
179
|
count: replaySkipped.length,
|
|
179
180
|
skipped: replaySkipped,
|
|
180
|
-
note: "these expired approvals belong to tasks whose stored request this version does not replay (kind
|
|
181
|
+
note: "these expired approvals belong to tasks whose stored request this version does not replay (skipped[].kind names the rule that refused each one; the resume paths log the same kinds with a reason per kind), so the SLA deny cannot be delivered. They stay suspended until the terminal backstop reaps them or someone cancels them; nothing was retried",
|
|
181
182
|
});
|
|
182
183
|
}
|
|
183
184
|
}
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* · 单用户 turnkey(`requirePrincipal !== true`,本仓 posture 判别式同一只)⇒ `global`:一台机一位超管;
|
|
10
10
|
* · 多租 · 部署的 service 凭据 ⇒ `global`:部署自己的凭据是运维档(契约给「启动方自己的行为」—— `SETTINGS_SOURCE_CONTRACT.flagSettings` —— 的档),持它的前端为自己转发的内容负责;
|
|
11
|
-
* 出示的 Bearer 就是 service 凭据时,哪怕它同时也被鉴权桥验成登录令牌,也按 service 凭据读
|
|
11
|
+
* 出示的 Bearer 就是 service 凭据时,哪怕它同时也被鉴权桥验成登录令牌,也按 service 凭据读 —— 这不是本表的判:来源只有一只判定次序(`verifiedIdentitySource`,
|
|
12
|
+
* service 凭据先;凭据门的 401 / 归因 / run 行身份元组读同一次的答案),本表只按来源词分档;
|
|
12
13
|
* · 多租 · 中心登录令牌,scope 角色 ≥ 契约 `domainWriteRole("governance")` ⇒ `global`:能写 scope 治理域(autonomy / commandPolicy / approvalRequire,全组织的免问面)
|
|
13
14
|
* 的角色,放宽自己这一 run 是它的子集;门槛读契约,不手抄角色词;
|
|
14
15
|
* · 多租 · 其余(登录令牌角色低于它 / 不带 / 认不出;直连门 principal 令牌只证身份不证角色;没有验过的凭据)⇒ `project`:契约给非运维档作者的仓控来源的档,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { domainWriteRole, isAtLeast, isRole, memberAdmission, membersOf, roleAtLeast, USER_ROLES, } from "@sema-agent/settings-schema";
|
|
2
2
|
import { entryProblem, entryProblemsFacts } from "../entry-problems.js";
|
|
3
3
|
import { ssoVerifiedRole } from "../security.js";
|
|
4
|
-
import {
|
|
4
|
+
import { verifiedIdentitySource } from "./principal-gate.js";
|
|
5
5
|
export function callerTrust(req, config) {
|
|
6
6
|
if (config.requirePrincipal !== true)
|
|
7
7
|
return { trust: "global", basis: "single_user" };
|
|
@@ -10,8 +10,6 @@ export function callerTrust(req, config) {
|
|
|
10
10
|
case "service_token":
|
|
11
11
|
return { trust: "global", basis: "service_credential" };
|
|
12
12
|
case "sso": {
|
|
13
|
-
if (systemFor(req, config) !== undefined)
|
|
14
|
-
return { trust: "global", basis: "service_credential" };
|
|
15
13
|
const role = ssoVerifiedRole(req);
|
|
16
14
|
if (role === undefined || !isRole(role))
|
|
17
15
|
return { trust: "project", basis: "sign_in_role_unrecognized" };
|
|
@@ -31,8 +31,14 @@
|
|
|
31
31
|
* 不受影响:那两种行在新旧两种读法下答同一个词。出路:作为新任务重提(仍停驻的先取消),或在它们收尾之前把 `AUTONOMY` 设成 `read-only`(按段的只读围栏,
|
|
32
32
|
* 与 7.121.x 的 `AUTONOMY=plan` 同一个结局,且没有入口地板 ⇒ 不拒)。删除条件登记在 `plugins/one-time-migrations.ts` 的 M12 行。
|
|
33
33
|
*
|
|
34
|
+
* ## 第四问(7.137.0):体里的退役请求键
|
|
35
|
+
* 请求键退役之后(今天一只:`promptProfile` 换成布尔 `promptLean`,{@link RETIRED_REQUEST_KEYS}),新提交带它在键闭集门 400;存体里的旧键由一次性迁移在起服时原地改写
|
|
36
|
+
* (M30,`stored-request-prompt-profile-m30.ts`,行改写经本模块的 {@link rewriteStoredBodyKeys})。改写不到的残留行(窗内从没起过服的部署;起服扫描之后滚动升级里
|
|
37
|
+
* 旧副本才写下的行)体里仍带旧键 ⇒ 拒({@link retiredRequestKeyRefusal},码同前两只读法拒:`checkpoint.unsupported_version`)。**没有**续跑时按旧键读的第二种读法,
|
|
38
|
+
* 也**没有**「旧键无人读、静默续跑」的臂:旧键还在 = 这份体没被改写过,它当年要的形本版读不出来。
|
|
39
|
+
*
|
|
34
40
|
* ## 一只判据、五个问点(`http/stored-body-replay.ts` 的 `storedBodyReplayRefusal` / 本模块的 {@link storedBodyReplayAnswer})
|
|
35
|
-
*
|
|
41
|
+
* 「这条停驻行的持久化体在本版上能否重放」只有**一个**读法:上面四问合在 `stored-body-replay.ts` 的 `storedBodyReplayRefusal` 一处(前两问的判据在本模块、第三问读请求设置
|
|
36
42
|
* 与治理两个属主 —— 本模块的值依赖要留在 node 内建 / 引擎包根之内,随包排空脚本经编译产物读它),答复合在 {@link storedBodyReplayAnswer} 一处。
|
|
37
43
|
* 问点五个,同调同答:续跑驱动段(`resume-legs.ts` `resolveResumeSpecOrBlocked`,另四条腿的唯一判点)· plan_review 的 prepare 段(读类拒,在 nonce 消费之前)·
|
|
38
44
|
* SLA 拒扫腿的跳过臂(`approval-sweeps.ts`)· 终态 run 的 steer 铸唤醒卡之前(`routes/runs.ts`:铸卡前先证明它赎得回,证不出 ⇒ 不铸、当场同一个拒)·
|
|
@@ -110,6 +116,23 @@ export declare function permissionModeReadingStated(ctx: CheckpointCtx): boolean
|
|
|
110
116
|
* 🔴 形是给冻结门看的:`status: <三位数>, body: refusalBody({ errorCode: "<码>", message: "<整句双引号字面>"`,中间只许有空白。
|
|
111
117
|
*/
|
|
112
118
|
export declare function permissionModeReadingRefusal(): ResumeRefusalAnswer;
|
|
119
|
+
/**
|
|
120
|
+
* 第四问:**退役的请求键**闭集(新提交在键闭集门 400;存体里还带着它 = 这份体是更早的写者写下、一次性迁移没改写到的)。加一个 ⇒ 加一行,
|
|
121
|
+
* {@link retiredRequestKeyRefusal} 的 switch 编译红直到给它一句拒句。今天一只:`promptProfile`(7.137.0 起换成布尔 `promptLean`;存体由 M30 起服改写)。
|
|
122
|
+
*/
|
|
123
|
+
export declare const RETIRED_REQUEST_KEYS: readonly ["promptProfile"];
|
|
124
|
+
export type RetiredRequestKey = (typeof RETIRED_REQUEST_KEYS)[number];
|
|
125
|
+
/** 存体顶层带的第一只退役请求键(闭集序);没有 ⇒ `undefined`。只看自有键(原型上的同名不算)。 */
|
|
126
|
+
export declare function retiredRequestKeyOf(body: unknown): RetiredRequestKey | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* 第四问的拒答复 —— 码与落点同另两只读法拒(`checkpoint.unsupported_version`,409;卡仍 pending 时的 `retriable:true` 由 {@link resumeRefusalWithCard} 追加,本铸点不带)。
|
|
129
|
+
* 句子说键、它换成了什么、为什么不续跑,与两条出路(运维在一次性迁移的窗内重启本服务 ⇒ 起服改写存体、同一请求可再发;否则作为新任务重提,仍停驻的先取消)。不回显值。
|
|
130
|
+
* 🔴 形是给冻结门看的:`status: <三位数>, body: refusalBody({ errorCode: "<码>", message: <整句双引号字面 或 一只模板串>`,中间只许有空白(模板串形冻结的是骨架,`${…}` 折成一个洞)。
|
|
131
|
+
* **窗的日期不手写**:`rewriteWindowEnd` 由调用方交 —— 判据(`stored-body-replay.ts`)按「退役键 → 改写它的迁移」那张表读登记表该行的 `removableAfter`,窗只写在登记表一处;
|
|
132
|
+
* 句子写绝对日期(过了这一天读句子的人一眼看出出路已关,不靠「本版发布日」这种随版漂的说法)。
|
|
133
|
+
* 删法见登记表 M30 的 `removal`(唯一来源)。
|
|
134
|
+
*/
|
|
135
|
+
export declare function retiredRequestKeyRefusal(key: RetiredRequestKey, rewriteWindowEnd: string): ResumeRefusalAnswer;
|
|
113
136
|
/**
|
|
114
137
|
* 本行能否被按当前读法重放:体里 `allow` 在生效而行上的记号不是当前这一个词 ⇒ **不能**(`true` = 拒)。
|
|
115
138
|
* 记号缺席、是旧词(`"statement"`)或认不得(手改 / 将来的写者)⇒ 同样拒:只有当前这个词是「按当前读法写下」的证据。
|
|
@@ -127,7 +150,7 @@ export declare function allowListReadingEraOf(ctx: CheckpointCtx): AllowListRead
|
|
|
127
150
|
export declare function allowListReadingRefusal(era: AllowListReadingEra): ResumeRefusalAnswer;
|
|
128
151
|
/**
|
|
129
152
|
* 一条停驻行的持久化体在本版上**不能**重放时,是哪一条规则拒的(闭集;加一条 ⇒ {@link storedBodyReplayAnswer} 的 switch 编译红)。
|
|
130
|
-
* 答 `checkpoint.unsupported_version`
|
|
153
|
+
* 答 `checkpoint.unsupported_version` 的三臂随带这一行的写者版本(续跑上下文的 `writerVersion`,缺席 = 本版之前写下的行)—— 那一码的两版本键由它铸({@link buildCheckpointVersionKeys})。
|
|
131
154
|
*/
|
|
132
155
|
export type StoredBodyReplayRefusal = {
|
|
133
156
|
readonly kind: "allow_list_reading";
|
|
@@ -141,10 +164,16 @@ export type StoredBodyReplayRefusal = {
|
|
|
141
164
|
readonly stored: PermissionMode | undefined;
|
|
142
165
|
readonly floor: PermissionMode;
|
|
143
166
|
readonly writerVersion?: string;
|
|
167
|
+
} | {
|
|
168
|
+
readonly kind: "retired_request_key";
|
|
169
|
+
readonly key: RetiredRequestKey;
|
|
170
|
+
/** 改写这只退役键的一次性迁移窗的最后一天(UTC 日历日 YYYY-MM-DD)—— 登记表那一行的 `removableAfter`,由判据(`stored-body-replay.ts`)读来:本模块的闭包只许 node 内建 / 引擎包根,读不了登记表,日期只能由调用方交。 */
|
|
171
|
+
readonly rewriteWindowEnd: string;
|
|
172
|
+
readonly writerVersion?: string;
|
|
144
173
|
};
|
|
145
174
|
/**
|
|
146
|
-
* 🔴 **`checkpoint.unsupported_version` 的 409 体上两个版本键 —— 唯一铸法;本码每一个铸点同一条律**(core 原码镜像 = `resume-legs.ts` 的 catch
|
|
147
|
-
* = {@link storedBodyReplayAnswer} 的 allow 读法 /
|
|
175
|
+
* 🔴 **`checkpoint.unsupported_version` 的 409 体上两个版本键 —— 唯一铸法;本码每一个铸点同一条律**(core 原码镜像 = `resume-legs.ts` 的 catch 臂;本服务自铸的三只拒
|
|
176
|
+
* = {@link storedBodyReplayAnswer} 的 allow 读法 / 模式词读法 / 退役请求键三臂):续跑上下文带写者版本 ⇒ `{ writerVersion, readerVersion }`;不带(本版之前写下的行)⇒ `{}`,不编值。
|
|
148
177
|
* 两值相等也照带 —— 那是事实(不是版本问题,出路照码与报文);写者比读者新才是「回到写它的那一版去决」。`readerVersion` 由调用方传本进程的服务版本
|
|
149
178
|
* (本模块零值依赖,不自取)。纯数据。
|
|
150
179
|
*/
|
|
@@ -201,11 +230,12 @@ export declare function storedBodyReplayLogFields(refusal: StoredBodyReplayRefus
|
|
|
201
230
|
layer?: InertToolListEntry["layer"];
|
|
202
231
|
storedMode?: PermissionMode | null;
|
|
203
232
|
floor?: PermissionMode;
|
|
233
|
+
key?: RetiredRequestKey;
|
|
204
234
|
note: string;
|
|
205
235
|
};
|
|
206
236
|
/**
|
|
207
237
|
* `stored-body-replay.ts` `storedBodyReplayRefusal` 的答复:每条规则一只既有铸点(allow 记号 ⇒ {@link allowListReadingRefusal};模式词记号 ⇒ {@link permissionModeReadingRefusal};
|
|
208
|
-
*
|
|
238
|
+
* 退役请求键 ⇒ {@link retiredRequestKeyRefusal};这三只答 `checkpoint.unsupported_version`,体按那一码的键律补两版本键,`readerVersion` = 调用方传的本进程服务版本;工具名清单 ⇒ {@link resumeBlockedByPolicyAnswer},与提交腿的 400
|
|
209
239
|
* (该层的拒码:`request.field_invalid` / `config.agent.unknown_tool` / `config.tool_face_control`)同码同句 —— 体上只有这一个问题时;存量体同时还踩中 `resolveSpec` 更早的拒〔三桶超帽 / 托管姿态〕时,提交侧答更早那条、续跑侧答本句,两边都是拒,指路不同)。
|
|
210
240
|
*
|
|
211
241
|
* 🔴 `cardPending` —— **调用方交出的事实**:这一次拒的时候,有没有一张待批卡仍在等。`retriable:true` 说的恰是「卡仍 pending」,所以它只在卡在时在场(经
|
|
@@ -356,9 +386,10 @@ export type StoredCheckpointCtx = Omit<CheckpointCtx, "body"> & {
|
|
|
356
386
|
};
|
|
357
387
|
declare const sealedCheckpointCtxBrand: unique symbol;
|
|
358
388
|
/**
|
|
359
|
-
* **封口铸出的**落盘形(品牌型):店的写口(`putCtx`)
|
|
389
|
+
* **封口铸出的**落盘形(品牌型):店的写口(`putCtx`)与一次性迁移的 ctx 行改写店腿(`rewriteCtxRows`)只收它 ⇒ 「写 ctx 只有本模块的铸点一个写入面」由型面守,
|
|
360
390
|
* 不再靠逐个数调用点(把开封形 / 裸对象直接交给写口 = 当场 tsc 红)。铸点闭集(都在本模块,都经 {@link mintSealedCheckpointCtx}):
|
|
361
|
-
* {@link sealCheckpointCtx}(新的持久化写经 {@link buildCheckpointCtx} 到它;续跑腿的写回)与 {@link
|
|
391
|
+
* {@link sealCheckpointCtx}(新的持久化写经 {@link buildCheckpointCtx} 到它;续跑腿的写回)、{@link sealStoredCheckpointCtxM28} 与 {@link rewriteStoredBodyKeys}
|
|
392
|
+
* (存量行非凭据顶层体键的改写,凭据位与读法记号原样随行)。零运行期变化。
|
|
362
393
|
*/
|
|
363
394
|
export type SealedCheckpointCtx = StoredCheckpointCtx & {
|
|
364
395
|
readonly [sealedCheckpointCtxBrand]: true;
|
|
@@ -407,5 +438,15 @@ export declare function sealStoredCheckpointCtxM28(stored: object, sealer: Check
|
|
|
407
438
|
readonly ctx: SealedCheckpointCtx;
|
|
408
439
|
readonly valuesSealed: number;
|
|
409
440
|
} | undefined;
|
|
441
|
+
/**
|
|
442
|
+
* 🔴 **一次性迁移对存量行非凭据顶层体键的改写**(品牌铸点之三;今天的调用方 = M30 的行改写 `stored-request-prompt-profile-m30.ts`):在一行落盘形的体上删 `drop` 里的顶层键、
|
|
443
|
+
* 设 `set` 里的顶层键,其余一个字节不动 —— 行上的读法记号(凭据封口的 `credentialSeal` / `credentialSlotGeneration` 与另两只记号)与凭据位上的值原样随行:封好的行仍是封好的,
|
|
444
|
+
* 更早写者留下的明文行仍是明文(读口照旧拒 `plaintext`,本口不替它洗白)。`drop` / `set` 碰到凭据位的根键(闭集各行路径的第一段)或 `__proto__` ⇒ 抛(程序错,不是数据错)。
|
|
445
|
+
* 体不是对象 ⇒ 抛(店的判形保证 ctx 是对象;体坏形的行由调用方先判、不交来)。
|
|
446
|
+
*/
|
|
447
|
+
export declare function rewriteStoredBodyKeys(stored: object, edit: {
|
|
448
|
+
readonly drop: readonly string[];
|
|
449
|
+
readonly set?: Readonly<Record<string, unknown>>;
|
|
450
|
+
}): SealedCheckpointCtx;
|
|
410
451
|
export {};
|
|
411
452
|
//# sourceMappingURL=checkpoint-ctx.d.ts.map
|
|
@@ -39,6 +39,28 @@ export function permissionModeReadingRefusal() {
|
|
|
39
39
|
}),
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
+
export const RETIRED_REQUEST_KEYS = ["promptProfile"];
|
|
43
|
+
export function retiredRequestKeyOf(body) {
|
|
44
|
+
if (body === null || typeof body !== "object" || Array.isArray(body))
|
|
45
|
+
return undefined;
|
|
46
|
+
return RETIRED_REQUEST_KEYS.find((key) => Object.hasOwn(body, key));
|
|
47
|
+
}
|
|
48
|
+
export function retiredRequestKeyRefusal(key, rewriteWindowEnd) {
|
|
49
|
+
switch (key) {
|
|
50
|
+
case "promptProfile":
|
|
51
|
+
return {
|
|
52
|
+
status: 409,
|
|
53
|
+
body: refusalBody({
|
|
54
|
+
errorCode: "checkpoint.unsupported_version",
|
|
55
|
+
message: `this task was suspended by an earlier server version and its stored request still carries promptProfile, a request key this version no longer reads (it was replaced by the boolean promptLean: classic is promptLean: false, simple is leaving promptLean out), so continuing would run the task with a prompt form it was not submitted with. Nothing was decided and nothing was resumed. If whoever operates this deployment restarts this server on or before ${rewriteWindowEnd} (UTC), the stored request is rewritten in place and the same request can be sent again; otherwise submit the work again as a new task with promptLean in place of promptProfile, and if this task is still suspended cancel it first (POST /v1/runs/:id/cancel).`,
|
|
56
|
+
}),
|
|
57
|
+
};
|
|
58
|
+
default: {
|
|
59
|
+
const unhandled = key;
|
|
60
|
+
throw new Error(`retired request key ${String(unhandled)} has no refusal sentence`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
42
64
|
export function allowListReadingUnstated(ctx) {
|
|
43
65
|
if (!bodyCarriesAllowList(ctx.body))
|
|
44
66
|
return false;
|
|
@@ -96,6 +118,8 @@ export function storedBodyReplayLogFields(refusal) {
|
|
|
96
118
|
return { kind: refusal.kind, layer: refusal.entry.layer, note: "this session's stored request carries a tool-list entry the engine reads as empty (a skill disallowedTools, an agent definition's denyTools / allowTools, the request's excludeTools / allowTools, or the rules in settings.permissions.deny / ask: a retired name, a case twin, a spelling no tool name can have, a rule core's grammar cannot compile, or a ~/ path rule on a lane with no home), or a permission list that is malformed or over the entry cap in either direction; it is not replayed — the task stays suspended until someone cancels it and resubmits with the entry fixed" };
|
|
97
119
|
case "permission_mode_reading":
|
|
98
120
|
return { kind: refusal.kind, storedMode: refusal.stored ?? null, floor: refusal.floor, note: "this session's stored request was written before the operator's AUTONOMY plan floor was recorded in the task (earlier versions re-applied it on every resume); replaying it now would run under its submitted permission mode — the task stays suspended until someone cancels it, or AUTONOMY is set to read-only so it can finish with read-only hands" };
|
|
121
|
+
case "retired_request_key":
|
|
122
|
+
return { kind: refusal.kind, key: refusal.key, note: "this session's stored request still carries a request key this version retired, and the one-time rewrite at startup did not reach it (no startup of this version inside its window since the row was written); it is not replayed — the task stays suspended until someone cancels it, or a restart of this version inside the window rewrites the row" };
|
|
99
123
|
default: {
|
|
100
124
|
const unhandled = refusal;
|
|
101
125
|
throw new Error(`stored-body replay refusal ${JSON.stringify(unhandled)} has no log fields`);
|
|
@@ -117,6 +141,10 @@ function storedBodyReplayArmAnswer(refusal, readerVersion) {
|
|
|
117
141
|
const answer = permissionModeReadingRefusal();
|
|
118
142
|
return { status: answer.status, body: { ...answer.body, ...buildCheckpointVersionKeys(refusal.writerVersion, readerVersion) } };
|
|
119
143
|
}
|
|
144
|
+
case "retired_request_key": {
|
|
145
|
+
const answer = retiredRequestKeyRefusal(refusal.key, refusal.rewriteWindowEnd);
|
|
146
|
+
return { status: answer.status, body: { ...answer.body, ...buildCheckpointVersionKeys(refusal.writerVersion, readerVersion) } };
|
|
147
|
+
}
|
|
120
148
|
default: {
|
|
121
149
|
const unhandled = refusal;
|
|
122
150
|
throw new Error(`stored-body replay refusal ${JSON.stringify(unhandled)} has no answer`);
|
|
@@ -321,4 +349,16 @@ export function sealStoredCheckpointCtxM28(stored, sealer, sessionId) {
|
|
|
321
349
|
? undefined
|
|
322
350
|
: { ctx: mintSealedCheckpointCtx({ ...stored, body: sealed, credentialSeal: CHECKPOINT_CTX_SEAL_ALG, credentialSlotGeneration: generation }), valuesSealed };
|
|
323
351
|
}
|
|
352
|
+
const CREDENTIAL_SLOT_ROOTS = new Set(CHECKPOINT_CTX_CREDENTIAL_SLOTS.map((slot) => slot.path[0]));
|
|
353
|
+
export function rewriteStoredBodyKeys(stored, edit) {
|
|
354
|
+
const touched = [...edit.drop, ...Object.keys(edit.set ?? {})];
|
|
355
|
+
const forbidden = touched.filter((key) => CREDENTIAL_SLOT_ROOTS.has(key) || key === "__proto__");
|
|
356
|
+
if (forbidden.length > 0)
|
|
357
|
+
throw new Error(`rewriteStoredBodyKeys: ${forbidden.join(", ")} is a credential-slot root (or __proto__) — a one-time body-key rewrite never touches those`);
|
|
358
|
+
const body = stored.body;
|
|
359
|
+
if (!isRecord(body))
|
|
360
|
+
throw new Error("rewriteStoredBodyKeys: the stored row's body is not an object");
|
|
361
|
+
const next = Object.fromEntries([...Object.entries(body).filter(([key]) => !edit.drop.includes(key)), ...Object.entries(edit.set ?? {})]);
|
|
362
|
+
return mintSealedCheckpointCtx({ ...stored, body: next });
|
|
363
|
+
}
|
|
324
364
|
//# sourceMappingURL=checkpoint-ctx.js.map
|
package/dist/http/dispatch.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { setSsoPrincipal,
|
|
2
|
+
import { setSsoPrincipal, setSsoScope, setSsoRole, isDestructiveSessionWrite, PRINCIPAL_TOKEN_HEADER, APPROVAL_MAC_HEADER, APPROVAL_MAC_KID_HEADER } from "../security.js";
|
|
3
3
|
import { publicStoreProbeError } from "../store-live-probe.js";
|
|
4
4
|
import { looksLikeJwt } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
5
5
|
import { admitBridgeIdentity } from "../auth-bridge.js";
|
|
@@ -9,7 +9,7 @@ import { sendJson, sendError, isSseResponse, endSseWithError } from "./send.js";
|
|
|
9
9
|
import { admissionRefusalOf, readinessOf, healthReadinessKeys, sendReadyz, sendBillableRefusal, billableRefusalOf, billableRefusalFrame } from "./readiness.js";
|
|
10
10
|
import { ENGINE_SIGNAL_KEY, buildEngineSignal, fleetMembershipKeys, healthConfigLedgerKeys, healthDrainingKeys, mintEngineSignal, restartActionable } from "../fleet/engine-signal.js";
|
|
11
11
|
import { buildHealthSelfReport } from "./health-self-report.js";
|
|
12
|
-
import { actsForDeployment, authorized, systemFor, gatedPrincipal, sendPrincipalRefusal, sendUnauthorized, traceTokenPresented, verifiedIdentitySource, hasServiceAuth, devOpenDeployment } from "./principal-gate.js";
|
|
12
|
+
import { actsForDeployment, admitRunIdentity, authorized, systemFor, gatedPrincipal, sendPrincipalRefusal, sendUnauthorized, traceTokenPresented, verifiedIdentitySource, hasServiceAuth, devOpenDeployment } from "./principal-gate.js";
|
|
13
13
|
const PROCESS_STARTED_AT_MS = Date.now() - Math.round(process.uptime() * 1000);
|
|
14
14
|
export function readCredentialOk(req, config) {
|
|
15
15
|
if (!hasServiceAuth(config))
|
|
@@ -228,22 +228,27 @@ export function createDispatch(ctx) {
|
|
|
228
228
|
return;
|
|
229
229
|
if (await dispatchSegment(PUBLIC_ROUTES, req, res, url, search, method, allow.pre, ctx))
|
|
230
230
|
return;
|
|
231
|
+
const identitySource = verifiedIdentitySource(req, deps.config);
|
|
232
|
+
admitRunIdentity(req, identitySource);
|
|
231
233
|
let source = null;
|
|
232
234
|
const anyServiceAuth = hasServiceAuth(deps.config);
|
|
233
235
|
if (anyServiceAuth) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
236
|
+
switch (identitySource) {
|
|
237
|
+
case "service_token":
|
|
238
|
+
source = systemFor(req, deps.config) ?? null;
|
|
239
|
+
break;
|
|
240
|
+
case "sso":
|
|
237
241
|
source = "sso";
|
|
238
|
-
|
|
239
|
-
|
|
242
|
+
break;
|
|
243
|
+
case "direct_door_jwt":
|
|
244
|
+
case undefined:
|
|
240
245
|
sendUnauthorized(res, "credential");
|
|
241
246
|
return;
|
|
247
|
+
default: {
|
|
248
|
+
const unknownSource = identitySource;
|
|
249
|
+
throw new Error(`credential gate: identity source ${String(unknownSource)} has no gate row`);
|
|
242
250
|
}
|
|
243
251
|
}
|
|
244
|
-
else {
|
|
245
|
-
source = sys;
|
|
246
|
-
}
|
|
247
252
|
}
|
|
248
253
|
reqState.source = source;
|
|
249
254
|
if (!anyServiceAuth &&
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RunStore } from "../plugins/store-backend.js";
|
|
2
2
|
import type { RunIdemBinding, RunRecord } from "../plugins/store-contracts.js";
|
|
3
3
|
import type { PreparedTaskSubmission, RouteCtxBase, WorkflowDecisionDelivery } from "./route-ctx.js";
|
|
4
|
+
import type { RunIdentity } from "../identity-source.js";
|
|
4
5
|
export interface DurableRunLegArgs {
|
|
5
6
|
/** `prepareSpec` 的产物,或内部车道按同一条 `resolveSpec` 装配出来的等价形。 */
|
|
6
7
|
prepared: PreparedTaskSubmission;
|
|
@@ -18,8 +19,11 @@ export interface DurableRunLegArgs {
|
|
|
18
19
|
* 提交门传 `gatedPrincipal(req) ?? null` = 提交者本人;`/decide` 的第三条车道传**卡属主**(= 原提交者,
|
|
19
20
|
* 不是代为按下按钮的 operator —— 与 resume 腿那条成文口径逐字同源)。`null` = 匿名/开放部署。 */
|
|
20
21
|
elicitOwner: string | null;
|
|
21
|
-
/** run 行元数据的 `source` 列(调用方的系统身份;内部车道传自己的车道名)
|
|
22
|
+
/** run 行元数据的 `source` 列(调用方的系统身份;内部车道传自己的车道名)。只作归因。 */
|
|
22
23
|
source: string | null;
|
|
24
|
+
/** run 行身份元组(`RunRecord.identity`)—— 调用方经 `runIdentityOf(req, config, 属主)` 读凭据门铸下的那一份交进来(本腿不碰请求);
|
|
25
|
+
* 属主 = `prepared.auth.principal`(本腿建行写进 `owner` 的同一个值)。`/decide` 第三条车道同一只读法:按下按钮的不是卡属主 ⇒ 空。 */
|
|
26
|
+
identity: RunIdentity | null;
|
|
23
27
|
/** `/decide` 第三条车道的 fresh 投递腿交来的那一件决定投递(带着的决定 + 起跑之前落行的那一笔;语义与 TRUSTED 纪律的属主 =
|
|
24
28
|
* `route-ctx.ts` 的 {@link WorkflowDecisionDelivery} 顶注)。缺席 ⇒ 只摊本会话已受理的决定行。 */
|
|
25
29
|
workflowDecision?: WorkflowDecisionDelivery;
|
|
@@ -19,7 +19,7 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
|
|
|
19
19
|
const sessionId = args.prepared.spec.sessionId ?? uuidv7();
|
|
20
20
|
const taskId = args.clientTaskId ?? uuidv7();
|
|
21
21
|
const trustedInternals = await heldWorkflowParkDecisions(deps, sessionId, args.workflowDecision?.carried);
|
|
22
|
-
const created = await runStore.createRun(taskId, sessionId, args.prepared.auth?.principal ?? null, deps.instanceId ?? "default", { ...runMeta(args.prepared, args.source), idem: args.idem ?? null });
|
|
22
|
+
const created = await runStore.createRun(taskId, sessionId, args.prepared.auth?.principal ?? null, deps.instanceId ?? "default", { ...runMeta(args.prepared, args.source), identity: args.identity, idem: args.idem ?? null });
|
|
23
23
|
if (created.ok)
|
|
24
24
|
clearTurnActivity(taskId);
|
|
25
25
|
if (created.ok)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* ## 规则(一条):自报位是**投影**,不是第二份规则
|
|
6
6
|
* 每一位读的是**门本身读的那只谓词 / 那只取值口**,本模块不判任何东西:
|
|
7
|
-
* · `auth.kinds` = `IDENTITY_SOURCES`(`verifiedIdentitySource`
|
|
7
|
+
* · `auth.kinds` = `IDENTITY_SOURCES`(`identity-source.ts`;`verifiedIdentitySource` 的回值闭集,序 = 判定次序)里,这台部署的装配**能**回的那几个词 —— 取唯一读法 `wiredIdentitySources`
|
|
8
8
|
* (`principal-gate.ts`;「这台部署有没有身份边界」的门 `devOpenDeployment` 读同一只):
|
|
9
9
|
* `service_token` ⇐ 全局门武装(`systemFor` 只在配了 service token 时回得出值)· `sso` ⇐ 鉴权桥装了(入口预认证只在
|
|
10
10
|
* `webAdminJwtVerifier` 在场时挂身份)· `direct_door_jwt` ⇐ 直连门开着(`verifiedIdentitySource` 那一臂的第一个合取项);
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* 三键**恒在场**;缺席 = 老 worker(≤7.120.x)。`center.issuer` / `center.webBaseUrl` 缺席即缺席(没有鉴权桥 / 没配旋钮),不拼不猜。
|
|
34
34
|
*/
|
|
35
35
|
import type { FlatServiceDeps } from "./server.js";
|
|
36
|
-
import {
|
|
36
|
+
import type { IdentitySource } from "../identity-source.js";
|
|
37
37
|
/** `auth.identityAttach` 的词表。 */
|
|
38
38
|
export declare const IDENTITY_ATTACH_WORDS: readonly ["available", "unavailable"];
|
|
39
39
|
export type IdentityAttach = (typeof IDENTITY_ATTACH_WORDS)[number];
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** 远端可控自由文本在这几面上的长度上界(字符)。 */
|
|
2
|
+
export declare const MCP_REMOTE_TEXT_MAX = 200;
|
|
3
|
+
/** 引擎铸过的 MCP 远端失败句 → 本服务出口的形:引擎没抹的段过本仓脱敏器、引擎的记号原样,截到 {@link MCP_REMOTE_TEXT_MAX}。 */
|
|
4
|
+
export declare function redactMcpRemoteText(text: string): string;
|
|
5
|
+
//# sourceMappingURL=mcp-remote-text.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { redactSecrets } from "../trace/redact.js";
|
|
2
|
+
const ENGINE_REDACTION_MARK = "[redacted]";
|
|
3
|
+
export const MCP_REMOTE_TEXT_MAX = 200;
|
|
4
|
+
export function redactMcpRemoteText(text) {
|
|
5
|
+
return text.split(ENGINE_REDACTION_MARK).map((part) => redactSecrets(part)).join(ENGINE_REDACTION_MARK).slice(0, MCP_REMOTE_TEXT_MAX);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=mcp-remote-text.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import type { ServiceConfig } from "../config-types.js";
|
|
3
3
|
import { type DurableOwnerKey, type Principal, type SessionCaller, type SessionOwnerCell } from "../security.js";
|
|
4
|
+
import { type IdentitySource, type RunIdentity } from "../identity-source.js";
|
|
4
5
|
/** Coerce a possibly-repeated request header to a single string (or undefined). */
|
|
5
6
|
export declare function headerStr(v: string | string[] | undefined): string | undefined;
|
|
6
7
|
export declare function safeEqual(a: string, b: string): boolean;
|
|
@@ -18,19 +19,23 @@ export declare function traceTokenPresented(req: IncomingMessage, config: Servic
|
|
|
18
19
|
/** loopback 三形(IPv4 / IPv6 / IPv4-mapped)—— 一处定义,diagnostics 门与任何「本机自用」判据同吃。缺席 ⇒ false(fail-closed)。 */
|
|
19
20
|
export declare function isLoopbackAddress(addr: string | undefined): boolean;
|
|
20
21
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* 🔴 **这次请求验过的身份来源 —— 全仓唯一的判定次序**:按 {@link IDENTITY_SOURCES} 的序逐词问,答第一个验得过的词(次序与它为什么是这个次序见
|
|
23
|
+
* `identity-source.ts` 头注律 1:出示了 service 凭据就是 `service_token`)。缺席 = 只有自述头或什么都没有。
|
|
24
|
+
* 读者:凭据门(`dispatch.ts`:401、归因 `source` 与 run 行身份元组读同一次的答案)、调用方信任档(`caller-trust.ts`)、门前读路谓词、
|
|
25
|
+
* off-box 诊断门(只判有无:名单只在有可验身份时才是身份,零凭据下它只是谁都能写的字符串)。
|
|
24
26
|
*/
|
|
25
|
-
export declare
|
|
26
|
-
export type IdentitySource = (typeof IDENTITY_SOURCES)[number];
|
|
27
|
+
export declare function verifiedIdentitySource(req: IncomingMessage, config: ServiceConfig): IdentitySource | undefined;
|
|
27
28
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* 缺席 = 只有自述头或什么都没有。一处判定(7.63.0 codex [high]+[medium]):off-box 读 operator 面的门用它——名单只在
|
|
31
|
-
* 有可验身份时才是身份,零凭据下它只是谁都能写的字符串;而 JWT 身份不依赖静态 token 的在场。
|
|
29
|
+
* 🔴 **run 行身份元组的唯一铸点**(律见 `identity-source.ts` 头注;唯一调用点 = `dispatch.ts` 的凭据门,机器门钉着)。`source` = 凭据门那一次
|
|
30
|
+
* {@link verifiedIdentitySource} 的答案 —— 这里不再判来源,只把来源连同它的 scope / 化身号定成元组,挂在这个请求上;建 run 行的点经 {@link runIdentityOf} 读它。
|
|
32
31
|
*/
|
|
33
|
-
export declare function
|
|
32
|
+
export declare function admitRunIdentity(req: IncomingMessage, source: IdentitySource | undefined): void;
|
|
33
|
+
/**
|
|
34
|
+
* 建 run 行时写进行里的身份元组 —— 每个 `createRun` 调用点(或替它建行的腿的调用方)读这一只,原样写入(机器门:`src/http/**` 每处 `createRun` 都写明 `identity`)。
|
|
35
|
+
* 只在**受理请求验过的主体就是这一行的属主**时交出凭据门铸下的元组(提交面恒成立:行的属主就是这个主体);别人替属主按下的那一跑(唤醒、决定投递铸的
|
|
36
|
+
* 新一跑)⇒ 空 —— 属主本人的凭据没有在这次受理里出示过(律 3)。没有请求 / 请求没过凭据门 ⇒ 空。
|
|
37
|
+
*/
|
|
38
|
+
export declare function runIdentityOf(req: IncomingMessage | undefined, config: ServiceConfig, owner: string | null): RunIdentity | null;
|
|
34
39
|
export declare function gatedPrincipal(req: IncomingMessage, config: ServiceConfig): Principal | undefined;
|
|
35
40
|
/**
|
|
36
41
|
* 「**出示了凭证但验不过** ≠ **没出示**」在 **direct-door principal JWT** 面上的形 —— 本仓
|