@sema-agent/server 7.133.0 → 7.134.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 +87 -0
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/USAGE.md +129 -11
- package/deploy/sema-up/README.md +16 -0
- package/deploy/sema-up/chart/Chart.yaml +1 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +15 -0
- package/deploy/sema-up/chart/templates/_helpers.tpl +129 -0
- package/deploy/sema-up/chart/templates/sealed-key.yaml +24 -0
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +14 -0
- package/deploy/sema-up/chart/templates/server.yaml +14 -0
- package/deploy/sema-up/chart/values-full-single.yaml +5 -1
- package/deploy/sema-up/chart/values.yaml +34 -0
- package/dist/approval-ask-audit-store.d.ts +1 -1
- package/dist/approval-card.d.ts +4 -4
- package/dist/approval-card.js +1 -2
- package/dist/approval-content-kind.d.ts +5 -2
- package/dist/approval-content-kind.js +16 -3
- package/dist/approval-gate-currency.d.ts +72 -0
- package/dist/approval-gate-currency.js +94 -0
- package/dist/approval.d.ts +6 -0
- package/dist/approval.js +31 -14
- package/dist/boot/coordinators.d.ts +6 -1
- package/dist/boot/coordinators.js +6 -3
- package/dist/boot/leader.d.ts +2 -0
- package/dist/boot/leader.js +2 -1
- package/dist/boot/read-deny-anchor-audit.js +2 -2
- package/dist/boot/runner-deps.d.ts +18 -5
- package/dist/boot/runner-deps.js +9 -0
- package/dist/boot/stage-06-runners.d.ts +4 -3
- package/dist/boot/stage-06-runners.js +15 -2
- package/dist/boot/stage-07-capability-layer.d.ts +2 -1
- package/dist/boot/stage-08-reapers.d.ts +2 -1
- package/dist/boot/stage-09-leader.d.ts +2 -1
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +2 -1
- package/dist/boot/stage-10-http-server.js +2 -1
- package/dist/boot/stores.d.ts +8 -0
- package/dist/boot/stores.js +8 -12
- package/dist/boot/web-admin.d.ts +6 -0
- package/dist/boot/web-admin.js +11 -2
- package/dist/budget.js +5 -1
- package/dist/caller-text-echo.d.ts +2 -0
- package/dist/caller-text-echo.js +2 -1
- package/dist/canonical-json.d.ts +2 -41
- package/dist/canonical-json.js +2 -13
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/mcp-probe.js +2 -2
- package/dist/capability-facts.d.ts +2 -0
- package/dist/capability-facts.js +4 -4
- package/dist/checkpoint-ctx-seal-m28.d.ts +42 -0
- package/dist/checkpoint-ctx-seal-m28.js +19 -0
- package/dist/field-conflict.d.ts +2 -1
- package/dist/field-conflict.js +1 -0
- package/dist/file-history-disclosure.d.ts +34 -5
- package/dist/file-history-disclosure.js +2 -2
- package/dist/fleet/fleet-bus.d.ts +4 -0
- package/dist/fleet/fleet-bus.js +1 -0
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/http/admission.js +8 -0
- package/dist/http/approval-sweeps.js +3 -1
- package/dist/http/checkpoint-ctx.d.ts +157 -2
- package/dist/http/checkpoint-ctx.js +158 -2
- package/dist/http/dispatch.js +3 -3
- package/dist/http/durable-run-leg.js +3 -2
- package/dist/http/idempotency.d.ts +2 -2
- package/dist/http/idempotency.js +2 -2
- package/dist/http/principal-gate.d.ts +32 -6
- package/dist/http/principal-gate.js +27 -4
- package/dist/http/refusal-body.d.ts +12 -0
- package/dist/http/refusal-body.js +11 -0
- package/dist/http/request-guards.d.ts +0 -9
- package/dist/http/request-guards.js +2 -16
- package/dist/http/resume-ctx-read.d.ts +48 -1
- package/dist/http/resume-ctx-read.js +26 -3
- package/dist/http/resume-legs.d.ts +2 -1
- package/dist/http/resume-legs.js +37 -22
- package/dist/http/route-ctx.d.ts +0 -9
- package/dist/http/routes/agents-roster.js +2 -3
- package/dist/http/routes/approvals-assistant.js +43 -9
- package/dist/http/routes/capabilities.js +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -3
- package/dist/http/routes/diagnostics.js +1 -1
- package/dist/http/routes/fleet.js +4 -4
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/rules.js +4 -4
- package/dist/http/routes/runs.js +30 -9
- package/dist/http/routes/session-sync.d.ts +3 -2
- package/dist/http/routes/session-sync.js +38 -20
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions-list.js +3 -4
- package/dist/http/routes/sessions.js +4 -4
- package/dist/http/routes/tasks.js +21 -16
- package/dist/http/routes/trace-usage.js +7 -9
- package/dist/http/run-declared-families.d.ts +16 -0
- package/dist/http/run-declared-families.js +12 -0
- package/dist/http/server.d.ts +6 -0
- package/dist/http/server.js +5 -2
- package/dist/http/submit-ctx.d.ts +4 -3
- package/dist/http/submit-ctx.js +19 -3
- package/dist/http/wire-types.d.ts +7 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/leader/wire.d.ts +4 -0
- package/dist/leader/wire.js +8 -7
- package/dist/main.js +1 -1
- package/dist/memory-origin-fence.js +2 -2
- package/dist/model-compat.d.ts +1 -1
- package/dist/observability/prompt-manifest.d.ts +19 -8
- package/dist/observability/prompt-manifest.js +9 -8
- package/dist/observability/tool-trace.js +8 -9
- package/dist/orchestration/workflow-park-decision.d.ts +1 -1
- package/dist/orchestration/workflow-park-decision.js +1 -0
- package/dist/parked-decide.d.ts +4 -5
- package/dist/parked-decide.js +3 -1
- package/dist/permission-rule-vocab.d.ts +1 -23
- package/dist/permission-rule-vocab.js +0 -13
- package/dist/plugins/approval-payload-fingerprint.d.ts +14 -6
- package/dist/plugins/approval-payload-fingerprint.js +1 -1
- package/dist/plugins/caching-session-store.d.ts +1 -1
- package/dist/plugins/caching-session-store.js +4 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +22 -1
- package/dist/plugins/checkpoint-store-sql.js +45 -1
- package/dist/plugins/local-checkpoint-store.d.ts +13 -2
- package/dist/plugins/local-checkpoint-store.js +26 -0
- package/dist/plugins/local-session-store.d.ts +12 -9
- package/dist/plugins/local-session-store.js +23 -14
- package/dist/plugins/local-session-window.d.ts +12 -6
- package/dist/plugins/local-session-window.js +1 -17
- package/dist/plugins/one-time-migrations.d.ts +11 -0
- package/dist/plugins/one-time-migrations.js +12 -0
- package/dist/plugins/permission-rule-store-file.js +10 -6
- package/dist/plugins/permission-rule-store-sql.js +8 -8
- package/dist/plugins/pg-session-storage.d.ts +1 -1
- package/dist/plugins/pg-session-storage.js +39 -17
- package/dist/plugins/remote-env-device.d.ts +14 -7
- package/dist/plugins/remote-env-device.js +23 -15
- package/dist/plugins/session-ownership.d.ts +11 -0
- package/dist/plugins/session-ownership.js +11 -0
- package/dist/plugins/session-placement.d.ts +12 -14
- package/dist/plugins/session-placement.js +2 -15
- package/dist/plugins/tidb-session-store.d.ts +1 -1
- package/dist/plugins/tidb-session-store.js +39 -17
- package/dist/request-tool-axes.d.ts +12 -0
- package/dist/request-tool-axes.js +20 -0
- package/dist/run-local.js +1 -0
- package/dist/runs.d.ts +2 -1
- package/dist/runs.js +4 -3
- package/dist/sealed-key.d.ts +32 -0
- package/dist/sealed-key.js +55 -1
- package/dist/security.d.ts +9 -7
- package/dist/session-error-wire.d.ts +12 -5
- package/dist/session-error-wire.js +4 -1
- package/dist/session-sync-kernel.d.ts +37 -10
- package/dist/session-sync-kernel.js +10 -0
- package/dist/task-a2a.js +4 -3
- package/dist/task-mcp.d.ts +2 -2
- package/dist/task-mcp.js +20 -8
- package/dist/task-settings.d.ts +5 -1
- package/dist/task-settings.js +1 -1
- package/dist/tool-approval.d.ts +151 -52
- package/dist/tool-approval.js +113 -20
- package/dist/tool-rule-names.d.ts +6 -0
- package/dist/trace/core-keyset-guard.d.ts +4 -3
- package/dist/trace/exit-seat.d.ts +13 -1
- package/dist/trace/exit-seat.js +55 -1
- package/dist/trace/ledger-events.d.ts +3 -1
- package/dist/trace/project.d.ts +18 -1
- package/dist/trace/project.js +16 -4
- package/dist/trace/redact.d.ts +6 -3
- package/dist/trace/redact.js +191 -23
- package/dist/trace/self-minted-wire.d.ts +4 -0
- package/dist/trace/self-minted-wire.js +1 -0
- package/dist/trace/wire-projection-faces.d.ts +6 -0
- package/dist/trace/wire-projection-faces.js +6 -0
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.js +2 -3
- package/dist/web-admin-client/types.d.ts +1 -1
- package/package.json +2 -2
- package/scripts/drain-check-local.mjs +34 -2
- package/scripts/drain-check.mysql.sql +20 -2
- package/scripts/drain-check.pg.sql +20 -2
- package/dist/mcp-tool-faces.d.ts +0 -69
- package/dist/mcp-tool-faces.js +0 -104
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* symbols WITHOUT touching the orchestration module — session-sync.ts value-imports StoreBackend and type-imports
|
|
7
7
|
* security's OwnerAwareSessionStore, so importing it from security.ts formed the repo's only true type cycle
|
|
8
8
|
* (security ↔ session-sync; lens2 §B/§E). This file imports nothing from the repo but TYPES from leaves (core types, and
|
|
9
|
-
* the session-face type from `session-incarnation.ts`, itself a leaf) — a leaf, same shape as session-sync-content.ts.
|
|
9
|
+
* the session-face type from `session-incarnation.ts`, itself a leaf) — a leaf, same shape as session-sync-content.ts. Its one VALUE import is
|
|
10
|
+
* core's `SessionError` class (the closed-set store error the three adapters already throw). session-sync.ts re-exports everything here, so existing import sites
|
|
10
11
|
* keep working unchanged.
|
|
11
12
|
*/
|
|
12
|
-
import type
|
|
13
|
+
import { SessionError, type SessionTreeEntry } from "@sema-agent/core";
|
|
13
14
|
import type { SessionFace } from "./session-incarnation.js";
|
|
14
15
|
/**
|
|
15
16
|
* P1d-β (PUSH streaming, sema-internal server/docs/DESIGN-session-sync.md §15 + the persistence synthesis) — a TWO-PHASE STAGED import
|
|
@@ -23,8 +24,9 @@ import type { SessionFace } from "./session-incarnation.js";
|
|
|
23
24
|
* Invariants the implementations uphold:
|
|
24
25
|
* - The staging rows live under `stagingId = ${realSessionId}#stg-${token}` (the `#` is illegal in a real uuidv7, so
|
|
25
26
|
* it can NEVER collide with a real session id) and have NO `session_meta` row → they are INVISIBLE to wake / ownerOf
|
|
26
|
-
* / listSessions (the existing orphan invariant) until `commit`
|
|
27
|
-
* commit point
|
|
27
|
+
* / listSessions (the existing orphan invariant) until `commit`'s transaction COMMITs the real session_meta row together
|
|
28
|
+
* with the swap (the sole commit point; a fresh push claims that row first inside the same txn, invisible to everyone else
|
|
29
|
+
* until COMMIT). A crash before commit leaves only collectable orphan staging rows, never a half-session.
|
|
28
30
|
* - `appendBatch` is IDEMPOTENT (INSERT … ON DUPLICATE KEY / ON CONFLICT DO NOTHING on the `(session_id, entry_id)`
|
|
29
31
|
* uk), so a re-streamed batch (a resumed Phase B) is a no-op.
|
|
30
32
|
* - `commit` re-classifies the relation IN-TXN against the dst's CURRENT entry ids (closing the Phase-A plan→commit
|
|
@@ -38,23 +40,48 @@ export interface StagingHandle {
|
|
|
38
40
|
/** Append a bounded batch of entries to the staging id, re-seq'd dense from a running counter held in the handle.
|
|
39
41
|
* Idempotent (ON DUPLICATE KEY / ON CONFLICT DO NOTHING) — a re-streamed batch is a no-op. One txn per batch. */
|
|
40
42
|
appendBatch(entries: SessionTreeEntry[]): Promise<void>;
|
|
41
|
-
/** ATOMICALLY swap the staged rows into the real session id (ONE txn):
|
|
42
|
-
*
|
|
43
|
-
*
|
|
43
|
+
/** ATOMICALLY swap the staged rows into the real session id (ONE txn): owner CAS ({@link StagedCommitOptions.expectedOwner},
|
|
44
|
+
* before anything about the row is classified or reported, so a refused commit reveals nothing about a row it did not judge — no
|
|
45
|
+
* placement, no relation, no entry ids) → in-txn re-classify
|
|
46
|
+
* (TOCTOU close) → purge dst → row-key rename staging→real → session_meta settled; COMMIT is the commit point. `owner` is re-stamped (§9).
|
|
47
|
+
* Throws {@link StagedCommitOwnerChangedError} when the owner cell is not the one the import's gate judged, and
|
|
48
|
+
* {@link SyncConflictError} on an unconsented fork/stale flip — both after rolling back. Returns the committed relation **and the settled session
|
|
44
49
|
* face** — the occupant (owner + incarnation) the commit left in the real session id, read INSIDE the same txn (the local
|
|
45
50
|
* store: the same hold of the session's write lock), never re-read afterwards. The post-commit replays conditioned on the
|
|
46
51
|
* occupant (the session row's two bits) write on exactly this face — a session deleted and re-created after the commit,
|
|
47
52
|
* even by the same owner, is a different occupant and is not written. A row without an incarnation is refused at that read
|
|
48
53
|
* (`sessionFaceFromRow`, the same law as every face read). */
|
|
49
|
-
commit(owner: string | null, opts
|
|
50
|
-
resolution?: "overwrite-dst";
|
|
51
|
-
}): Promise<{
|
|
54
|
+
commit(owner: string | null, opts: StagedCommitOptions): Promise<{
|
|
52
55
|
relation: SyncRelation["relation"];
|
|
53
56
|
face: SessionFace;
|
|
54
57
|
}>;
|
|
55
58
|
/** Drop the staging rows (Phase B aborted before commit) — DELETE the staging-id session_event rows. Idempotent. */
|
|
56
59
|
abort(): Promise<void>;
|
|
57
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* What a staged commit is conditioned on (S-1938 — one gate predicate, two phases). `expectedOwner` = the owner cell the import's gate
|
|
63
|
+
* judged when it opened the staging, in the `ownerOf` reading: `undefined` = no such session (a fresh push), `null` = ownerless, a string =
|
|
64
|
+
* that owner. The route judges `mayTouch` on that cell at Phase A and again on the session's current cell right before Phase B commits; the
|
|
65
|
+
* commit itself lands only on that same cell — each store compares it inside the commit txn (local: inside the session's write lock) and, when
|
|
66
|
+
* the cell differs, rolls back and throws {@link StagedCommitOwnerChangedError} without writing a byte. The compare is on the owner cell, not
|
|
67
|
+
* on the incarnation: the gate's predicate reads only the owner (plus placement, which the commit gate refuses on its own), so a same-owner
|
|
68
|
+
* re-create is judged the same and is not a reason to refuse.
|
|
69
|
+
*/
|
|
70
|
+
export interface StagedCommitOptions {
|
|
71
|
+
readonly expectedOwner: string | null | undefined;
|
|
72
|
+
readonly resolution?: "overwrite-dst";
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The staged commit's owner CAS did not hold: the session's owner cell at commit time is not the one the import's gate judged (another replica
|
|
76
|
+
* deleted / re-created / claimed / created the session while the import streamed). Code = core's closed-set `conflict` (wire: the import face's
|
|
77
|
+
* row in `session-error-wire.ts`, 409); thrown after the commit txn rolled back — the destination is untouched. The three stores throw this one
|
|
78
|
+
* class (one sentence, minted here).
|
|
79
|
+
*/
|
|
80
|
+
export declare class StagedCommitOwnerChangedError extends SessionError {
|
|
81
|
+
constructor(sessionId: string);
|
|
82
|
+
}
|
|
83
|
+
/** The owner-cell comparison of {@link StagedCommitOptions.expectedOwner} against a store's own `ownerOf`-shaped read (`undefined` = no row). */
|
|
84
|
+
export declare function ownerCellIs(expectedOwner: string | null | undefined, actual: string | null | undefined): boolean;
|
|
58
85
|
/** A session store that can stage a streamed import (P1d-β). The 3 backends implement it; the in-memory dev store
|
|
59
86
|
* does not → the method is optional (the route 501s when absent, parity with the other §0.5 optional seams). */
|
|
60
87
|
export type ImportStagingStore = {
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import { SessionError } from "@sema-agent/core";
|
|
2
|
+
export class StagedCommitOwnerChangedError extends SessionError {
|
|
3
|
+
constructor(sessionId) {
|
|
4
|
+
super("conflict", `session ${sessionId}: owner changed while the import streamed — the staged commit was rolled back`);
|
|
5
|
+
this.name = "StagedCommitOwnerChangedError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export function ownerCellIs(expectedOwner, actual) {
|
|
9
|
+
return expectedOwner === undefined ? actual === undefined : actual !== undefined && (actual ?? null) === expectedOwner;
|
|
10
|
+
}
|
|
1
11
|
export function stagingIdFor(realSessionId, token) {
|
|
2
12
|
return `${realSessionId}#stg-${token}`;
|
|
3
13
|
}
|
package/dist/task-a2a.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveComplianceDenies } from "@sema-agent/core";
|
|
2
2
|
import { isUsableWireName } from "@sema-agent/settings-schema";
|
|
3
3
|
import { HttpError } from "./security.js";
|
|
4
4
|
import { readPlainStringRecord } from "./plain-string-record.js";
|
|
5
|
+
import { readRequestToolAxes } from "./request-tool-axes.js";
|
|
5
6
|
import { echoKeyName } from "./request-key-closure.js";
|
|
6
7
|
import { GOVERNANCE_HTTP_STATUS } from "./governance-http-status.js";
|
|
7
8
|
export const MAX_REQUEST_A2A_PEERS = 32;
|
|
@@ -45,12 +46,12 @@ function normalizeA2aPeer(e, leg, storedToolAxes) {
|
|
|
45
46
|
if (Array.isArray(o.allowSkills))
|
|
46
47
|
spec.allowSkills = o.allowSkills;
|
|
47
48
|
if (o.toolAxes !== undefined) {
|
|
48
|
-
const axes =
|
|
49
|
+
const axes = readRequestToolAxes(o.toolAxes, "a2a");
|
|
49
50
|
if (!axes.ok && storedToolAxes !== undefined) {
|
|
50
51
|
const before = storedToolAxes.a2a(o.toolAxes);
|
|
51
52
|
if (before === null)
|
|
52
53
|
return dropped;
|
|
53
|
-
const again =
|
|
54
|
+
const again = readRequestToolAxes(before, "a2a");
|
|
54
55
|
if (!again.ok)
|
|
55
56
|
return dropped;
|
|
56
57
|
if (again.toolAxes !== undefined)
|
package/dist/task-mcp.d.ts
CHANGED
|
@@ -158,10 +158,10 @@ export interface RequestMcpRefusal {
|
|
|
158
158
|
* ② **键名**:条目层、或 `transport` 按 `kind` 那一形的键闭集之外的键 ⇒ `400 request.body_shape`,拒体带 `unknownKeys`(拼错的键被悄悄不读,同样读起来像生效了)。
|
|
159
159
|
* ③ **身份头的键组合**(`principalHeader` 与 `assertionHeader` 同设 / `assertionHeader` 非串)⇒ {@link requestEntryIdentityRefusal}(同设那一句对契约包解析期原句逐字)。
|
|
160
160
|
* ④ **受理键的值** ⇒ 逐键问判官表 {@link ENTRY_KEY_JUDGES},`400 request.field_invalid`,拒句点名 `mcpServers[i].<键路径>`、server 名与**该键判官的原因句**({@link entryFieldRefusal})。
|
|
161
|
-
* 判官**就是读器用的那一只**,不是第二份:`toolAxes` = 引擎根导出的 `
|
|
161
|
+
* 判官**就是读器用的那一只**,不是第二份:`toolAxes` = {@link readRequestToolAxes}(引擎根导出的 `readToolAxes` + 请求腿不收 `boundaryFlow`);`toolFaces` = 引擎根导出的 `readToolFaces`;
|
|
162
|
+
* `startupTimeoutMs` = 引擎根导出的 `mcpStartupTimeoutMs`(坏值抛 `config.mcp_startup_timeout_invalid`,本门把那一句作原因句);`contentOrigin` = `isContentOriginWord`;`source` = `isMcpSourceLabel`;
|
|
162
163
|
* `name` = `isUsableWireName`;`allowTools` / `transport.args` = {@link isStringArray};`transport.env` / `headers` = 契约包读器 {@link readRequestMcpTransport} 的纯数据记录读法;`elicitation` = {@link isElicitationFlag};
|
|
163
164
|
* `transport` 的硬字段(`kind` / `command` / `url`)与软字段(`args` / `env` / `headers` / `principalHeader`)= {@link readRequestMcpTransport}(读器投影 transport 的那一只函数,门读它报的 `lost`;读器在契约包,本仓只据 `lost[0]` 拒、据 `lost` 在重放腿逐处点名)。
|
|
164
|
-
* 判官只在引擎里、包根没给读口的键(今天只有 `startupTimeoutMs`)不在这里判,原样过境、由引擎在拨号时按 server 拒(不写第二份判据;到货读口即换)。
|
|
165
165
|
* 于是判官受理的条目,读器({@link normalizeServer})交回的就是申报本身(只差判官自己的合法归一:空容器 / `skillScopeEligible:false` 折成缺席、`toolAxes` 取引擎的拷贝)——
|
|
166
166
|
* 探测口拨的那一份因此就是提交腿会挂的那一份,不另比一遍(另比就是第二只回答者)。唯一按门分的后果是「声明了 `assertionHeader`」这一个事实(引擎尚无座,
|
|
167
167
|
* 承载、不发送):新提交照挂 + 运维 info,探测口整条拒 —— 判据是同一只谓词 `specWithholdsMcpAssertionHeader`(开关 `MCP_ASSERTION_HEADER_SENT` 假时「声明了」即「被扣下」),不是第二份判法(见 `readMcpProbeBody`)。
|
package/dist/task-mcp.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { mcpInjectionDroppedNotice,
|
|
1
|
+
import { mcpInjectionDroppedNotice, mcpStartupTimeoutMs, readToolFaces, resolveComplianceDenies } from "@sema-agent/core";
|
|
2
2
|
import { MCP_CONTENT_ORIGINS, describeBadContentOrigin, describeBadWireName, describeNotStringArray, describeValueShape, isContentOriginWord, isStringArray, isUsableWireName, readRequestMcpTransport, stripControlAndBidi, } from "@sema-agent/settings-schema";
|
|
3
3
|
import { HttpError, createCodedHttpError } from "./security.js";
|
|
4
4
|
import { GOVERNANCE_HTTP_STATUS } from "./governance-http-status.js";
|
|
5
5
|
import { MCP_SOURCE_LABEL_MAX, isMcpSourceLabel, readMcpSourceLabel } from "./mcp-source-label.js";
|
|
6
6
|
import { describeMcpAssertionHeaderNotAString, describeMcpIdentityHeadersConflict, mcpIdentityHeadersIssue, noteMcpAssertionHeaderIgnored, noteMcpAssertionHeaderNotSent, specWithholdsMcpAssertionHeader, toCoreMcpServerSpec } from "./mcp-assertion-header.js";
|
|
7
7
|
import { MAX_KEY_ECHO, echoKeyName, keyClosureClause, scanKeyClosure } from "./request-key-closure.js";
|
|
8
|
-
import {
|
|
8
|
+
import { readRequestToolAxes } from "./request-tool-axes.js";
|
|
9
9
|
export const MAX_REQUEST_MCP_SERVERS = 32;
|
|
10
10
|
export const MAX_REQUEST_MCP_DROP_NAMES = MAX_REQUEST_MCP_SERVERS;
|
|
11
11
|
export const MCP_SERVER_ENTRY_KEYS = ["allowTools", "contentOrigin", "elicitation", "name", "source", "startupTimeoutMs", "toolAxes", "toolFaces", "transport"];
|
|
@@ -48,11 +48,11 @@ function normalizeServer(e, noteBadOrigin, storedToolAxes) {
|
|
|
48
48
|
}
|
|
49
49
|
if (isElicitationFlag(o.elicitation))
|
|
50
50
|
spec.elicitation = o.elicitation;
|
|
51
|
-
const axes =
|
|
51
|
+
const axes = readRequestToolAxes(o.toolAxes, "mcp");
|
|
52
52
|
let judged = axes;
|
|
53
53
|
let toolAxesReread;
|
|
54
54
|
if (!axes.ok && storedToolAxes !== undefined) {
|
|
55
|
-
const again =
|
|
55
|
+
const again = readRequestToolAxes(storedToolAxes.mcp(o.toolAxes), "mcp");
|
|
56
56
|
if (again.ok) {
|
|
57
57
|
judged = again;
|
|
58
58
|
toolAxesReread = stripControlAndBidi(axes.problem);
|
|
@@ -71,7 +71,7 @@ function normalizeServer(e, noteBadOrigin, storedToolAxes) {
|
|
|
71
71
|
Object.assign(spec, readMcpSourceLabel(spec.name, o.source));
|
|
72
72
|
if (o.startupTimeoutMs !== undefined)
|
|
73
73
|
spec.startupTimeoutMs = o.startupTimeoutMs;
|
|
74
|
-
const faces =
|
|
74
|
+
const faces = readToolFaces(o.toolFaces);
|
|
75
75
|
if (!faces.ok)
|
|
76
76
|
return bad("toolFaces");
|
|
77
77
|
if (faces.faces !== undefined)
|
|
@@ -188,13 +188,25 @@ const ENTRY_KEY_JUDGES = {
|
|
|
188
188
|
source: (o) => o.source === undefined || isMcpSourceLabel(o.source)
|
|
189
189
|
? undefined
|
|
190
190
|
: { path: "source", reason: `must be a non-empty string of at most ${MCP_SOURCE_LABEL_MAX} characters (a display label) — got ${describeValueShape(o.source)}` },
|
|
191
|
-
startupTimeoutMs: () =>
|
|
191
|
+
startupTimeoutMs: (o) => {
|
|
192
|
+
if (o.startupTimeoutMs === undefined)
|
|
193
|
+
return undefined;
|
|
194
|
+
try {
|
|
195
|
+
mcpStartupTimeoutMs({ name: typeof o.name === "string" ? o.name : "", startupTimeoutMs: o.startupTimeoutMs });
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
if (e.code !== "config.mcp_startup_timeout_invalid")
|
|
200
|
+
throw e;
|
|
201
|
+
return { path: "startupTimeoutMs", reason: e instanceof Error ? e.message : String(e) };
|
|
202
|
+
}
|
|
203
|
+
},
|
|
192
204
|
toolAxes: (o) => {
|
|
193
|
-
const read =
|
|
205
|
+
const read = readRequestToolAxes(o.toolAxes, "mcp");
|
|
194
206
|
return read.ok ? undefined : { path: "toolAxes", reason: read.problem };
|
|
195
207
|
},
|
|
196
208
|
toolFaces: (o) => {
|
|
197
|
-
const read =
|
|
209
|
+
const read = readToolFaces(o.toolFaces);
|
|
198
210
|
return read.ok ? undefined : { path: "toolFaces", reason: read.problem };
|
|
199
211
|
},
|
|
200
212
|
transport: (o) => {
|
package/dist/task-settings.d.ts
CHANGED
|
@@ -289,11 +289,15 @@ export declare function settingsPermissionBucketIssue(perms: unknown): SettingsP
|
|
|
289
289
|
* 的座位表(位置、条目律、去处都由那张表说),读入在 {@link parseTaskSettings},投影在 {@link applyTaskSettings} / {@link deriveSettingsPolicy}。这四个键不是 CC settings 键
|
|
290
290
|
* (sdk 的 `SemaSettings` 尚未声明 —— 对账门 `test/task-settings-sdk-key-parity.test.ts` 的「server 先于 sdk」登记表记着它们)。
|
|
291
291
|
*
|
|
292
|
+
* 🔴 `approvalFrameFamilies`(S-1914)= 这条 run 的客户端**声明**渲染得了哪些审批开卡帧族 —— 不进引擎,不是 CC settings 键。消费点 = 三条执行腿的审批装配
|
|
293
|
+
* (`tool-approval.ts` 的唯一读者 `declaredApprovalFrameFamilies` → `resolveApprovalLeg`),判官 `approvalFrameFamiliesIssue`(fresh 受理 400 与宽容读者同一只)。
|
|
294
|
+
* `parseTaskSettings` 刻意不读它(它管的是落到 `TaskSpec` 的设置)。sdk 型面待声明(对账门的「server 先于 sdk」登记表记着)。
|
|
295
|
+
*
|
|
292
296
|
* 🔴 两个 `skillListing*`([ref],7.77.0)= **CC settings 同名键**,值域抄 CC 的 schema 声明
|
|
293
297
|
* (`skillListingBudgetFraction` 是 `gt(0).lte(1)`、`skillListingMaxDescChars` 是 `int().positive()`);
|
|
294
298
|
* 消费点 = 本文件的 parse + `boot/resolve-spec.ts` 的 `TaskSpec` 透传。语义(预算怎么花、描述怎么裁、
|
|
295
299
|
* 溢出怎么退成 name-only)整个归引擎,本仓一个字节都不渲染,也**不复刻默认值**——缺席即整键不写。 */
|
|
296
|
-
export declare const TASK_SETTINGS_KEYS: readonly ["complianceAdditionalDenies", "env", "hooks", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
|
|
300
|
+
export declare const TASK_SETTINGS_KEYS: readonly ["approvalFrameFamilies", "complianceAdditionalDenies", "env", "hooks", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
|
|
297
301
|
/** 本 service 消费的 `settings.permissions` 子键 —— 闭集(值形校验另有既有门,本门只判键名)。 */
|
|
298
302
|
export declare const TASK_SETTINGS_PERMISSION_KEYS: readonly ["allow", "ask", "defaultMode", "deny", "disableAutoMode", "neverAuto"];
|
|
299
303
|
/** CC/SDK 合约里合法、本 service **不接线**的顶层键。词表外的 CC 键落 `unknown` —— 两列都是 400,标签只影响
|
package/dist/task-settings.js
CHANGED
|
@@ -154,7 +154,7 @@ export function settingsPermissionBucketIssue(perms) {
|
|
|
154
154
|
function acceptedRuleList(v) {
|
|
155
155
|
return Array.isArray(v) && v.length > 0 ? v.slice() : undefined;
|
|
156
156
|
}
|
|
157
|
-
export const TASK_SETTINGS_KEYS = ["complianceAdditionalDenies", "env", "hooks", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
|
|
157
|
+
export const TASK_SETTINGS_KEYS = ["approvalFrameFamilies", "complianceAdditionalDenies", "env", "hooks", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
|
|
158
158
|
export const TASK_SETTINGS_PERMISSION_KEYS = ["allow", "ask", "defaultMode", "deny", "disableAutoMode", "neverAuto"];
|
|
159
159
|
export const TASK_SETTINGS_UNSUPPORTED_KEYS = ["apiKeyHelper", "crossSessionInbound", "dialogExpiry", "sandbox", "statusLine"];
|
|
160
160
|
export const TASK_SETTINGS_UNSUPPORTED_PERMISSION_KEYS = ["additionalDirectories", "disableBypassPermissionsMode"];
|
package/dist/tool-approval.d.ts
CHANGED
|
@@ -60,6 +60,7 @@ import { type ApprovalDecidedBy, type HostSettledBy, type RetiredDecisionKeyFact
|
|
|
60
60
|
import type { IncomingMessage } from "node:http";
|
|
61
61
|
import { type AskClass, type AskRequest, type AskOutcome, type PermissionResult, type CheckpointGate, type PendingAction, type PersistedRuleMandate, type ReadRootGrantCandidate, type RuleOffer, type AutoModeFallbackCause, type BoundGrantAbsence, type BoundSkillGrants, type ToolApprovalCard } from "@sema-agent/core";
|
|
62
62
|
import { type CardRulePersisted, type RuleConsentLane } from "./rules-consent.js";
|
|
63
|
+
import type { GateCurrencyStore } from "./approval-gate-currency.js";
|
|
63
64
|
import { type AllowRuleShadowProjection, type DenialLimitFallback, type RuleOffersAbsence, type ApprovalRequestFrame, type ApprovalRevokeFrame, type ApprovalCardLegFrame, type ApprovalCompleteFrame, type RuleEvidenceProjection } from "./approval-card.js";
|
|
64
65
|
import { type ApprovalAskStore, type AskRow } from "./plugins/approval-ask-store-sql.js";
|
|
65
66
|
import type { ApprovalAskAuditSink } from "./approval-ask-audit-store.js";
|
|
@@ -114,9 +115,9 @@ export type NotPendingCause = AskRowEndingCause | typeof NOT_PENDING_UNKNOWN_CAU
|
|
|
114
115
|
/**
|
|
115
116
|
* `GET /v1/tool-approvals/:approvalId` 应答体 `state` 的**闭集**(一只 ask 此刻在哪一态;读法与应答口逐条同源):
|
|
116
117
|
* · `pending` —— 本副本上有在等它的活登记(应答口此刻收得下);
|
|
117
|
-
* · `decided` —— 行 `DECIDED`(有人 / 有策略决过;回执三键同晚答回放体);
|
|
118
|
-
* · `parked` —— 行 `PARKING
|
|
119
|
-
* · `void` —— 行 `DENIED` / `VOID`(没有人决过它,答案无处兑现;`cause` 同晚答 404)。
|
|
118
|
+
* · `decided` —— 行 `DECIDED`(有人 / 有策略决过;回执三键同晚答回放体),**或**行 `PARKED` 而它的门已被决(S-1915:checkpoint 已结算,见 {@link ParkedGateReading});
|
|
119
|
+
* · `parked` —— 行 `PARKING`,或行 `PARKED` 且门仍是该会话当前待决的审批门(转后台候批;应答口走迟到受理,耐久回决口照收);
|
|
120
|
+
* · `void` —— 行 `DENIED` / `VOID`(没有人决过它,答案无处兑现;`cause` 同晚答 404),**或**行 `PARKED` 而它的门已过期 / 被取代(含停驻 run 被取消;此时 `cause` 缺席)。
|
|
120
121
|
*/
|
|
121
122
|
export declare const TOOL_APPROVAL_READ_STATES: readonly ["pending", "decided", "parked", "void"];
|
|
122
123
|
export type ToolApprovalReadState = (typeof TOOL_APPROVAL_READ_STATES)[number];
|
|
@@ -226,6 +227,37 @@ export type ParkedAskRedeem = (req: ParkedAskRedeemRequest) => Promise<{
|
|
|
226
227
|
status: number;
|
|
227
228
|
body: Record<string, unknown>;
|
|
228
229
|
}>;
|
|
230
|
+
/**
|
|
231
|
+
* 按问读口对一张 `PARKED` 卡的**门读法**(S-1915;判官的唯一属主 = `approval-gate-currency.ts`,协调器本身不认识 checkpoint 的形):
|
|
232
|
+
* · `open` —— 这张卡的门仍是该会话当前待决的审批门(应答口收得下)⇒ 读口说 `parked`;
|
|
233
|
+
* · `decided` —— 门已被决:方向 / 时刻 / 理由取自 checkpoint 上的结算赢家 ⇒ 读口说 `decided`;
|
|
234
|
+
* · `void` —— 门已过期 / 被取代(含停驻 run 被取消:取消先把门 expire)⇒ 读口说 `void`(没有人决过它,答案无处兑现);
|
|
235
|
+
* · `unknown` —— 说不出可信的话:`row_absent` = checkpoint 行没了;`decision_unreadable` = 门已结算却读不出可用的决议(行的 status 词或结算种类不在闭集表里 / 结算记录过不了 core 的
|
|
236
|
+
* 门记录筛 / 方向或时刻读不出)⇒ 读口答 404 `tool_approval.not_pending`(坏行不包装成首决;应答口并不同判:它的 PARKED 臂只对 checkpoint 行没了答 404,已结算的门答 409 `approval_stale`),
|
|
237
|
+
* 并记一行 error:`defects` 是读不出的那几处(非空),日志带首句 + 计数。
|
|
238
|
+
*/
|
|
239
|
+
export type ParkedGateReading = {
|
|
240
|
+
readonly kind: "open";
|
|
241
|
+
} | {
|
|
242
|
+
readonly kind: "decided";
|
|
243
|
+
readonly decision: "allow" | "deny";
|
|
244
|
+
readonly decidedAtMs: number;
|
|
245
|
+
readonly decisionNote?: string;
|
|
246
|
+
} | {
|
|
247
|
+
readonly kind: "void";
|
|
248
|
+
} | {
|
|
249
|
+
readonly kind: "unknown";
|
|
250
|
+
readonly why: "row_absent";
|
|
251
|
+
} | {
|
|
252
|
+
readonly kind: "unknown";
|
|
253
|
+
readonly why: "decision_unreadable";
|
|
254
|
+
readonly defects: readonly [string, ...string[]];
|
|
255
|
+
};
|
|
256
|
+
/** 门读席:入参 = 问账行上的 gate 坐标(PARKED 行恒带 `gateToken`)。读不出(店抛错)⇒ 抛,协调器按店故障姿势处置(见 `readParkedGate`)。 */
|
|
257
|
+
export type ParkedGateRead = (row: {
|
|
258
|
+
readonly gateToken: string;
|
|
259
|
+
readonly gateBoundCallId: string | null;
|
|
260
|
+
}) => Promise<ParkedGateReading>;
|
|
229
261
|
/** 阶梯里**重发**一把 claim 的墙钟上限(ms;首把用 `DURABLE_CALL_TIMEOUT_MS`)。🔴 codex 交叉复审 C5
|
|
230
262
|
* (2026-08-06 真缺陷):只数重试次数是**不够**的 —— 次数只约束「已经返回或已经 reject 的调用」,一次
|
|
231
263
|
* **挂住不返回**的店调用(DB 连接黑洞、池饿死)会让整个退避循环连一格都走不完,`onAsk` 的 promise 于是永久
|
|
@@ -588,7 +620,7 @@ export interface ToolApprovalFrame {
|
|
|
588
620
|
* `approval_request` 帧([ref] 呈卡帧)同名同义:`expiresAtMs` = 本 ask 的绝对到期墙钟
|
|
589
621
|
* (有店=行上一次铸定的 `expiresAtMs`,与卡帧/落库同值;无店 D1=注册时刻算定的同名初值),
|
|
590
622
|
* `serverNowMs` = **帧铸造时刻**(cli [ref] 锚定语义:倒计时锚=帧到手时刻的服务器钟),
|
|
591
|
-
* `expiresInMs` = `max(0, expiresAtMs - serverNowMs)` 现算。三键同生同缺(
|
|
623
|
+
* `expiresInMs` = `max(0, expiresAtMs - serverNowMs)` 现算。三键同生同缺(没有活审批通道的腿不发帧,
|
|
592
624
|
* 发出的帧恒带);旧消费端无感,新壳借此给旧族帧补倒计时([ref] Q2 那类 77 分钟死卡的根治半场)。 */
|
|
593
625
|
expiresAtMs?: number;
|
|
594
626
|
expiresInMs?: number;
|
|
@@ -1078,16 +1110,17 @@ export type SessionGrantStore = Pick<ApprovalExemptionStore, "has" | "grant">;
|
|
|
1078
1110
|
* `StoreBackend.approvalAsk()` 的接口契约上(那条注 + `wiring-governance-operator.test.ts` 的三腿机器钉)——
|
|
1079
1111
|
* 它是**装配面**的事实,而本谓词能看见的只有 `kind`,拿 `kind` 当持久性的代理正是这次要拆掉的那层间接。
|
|
1080
1112
|
*/
|
|
1081
|
-
export type StreamApprovalGate = {
|
|
1113
|
+
export type StreamApprovalGate<TStore extends GateCurrencyStore = GateCurrencyStore> = {
|
|
1082
1114
|
active: true;
|
|
1083
1115
|
askStore: ApprovalAskStore;
|
|
1116
|
+
checkpointStore: TStore;
|
|
1084
1117
|
} | {
|
|
1085
1118
|
active: false;
|
|
1086
1119
|
reason: "no_tool_approval" | "protocol_disabled" | "no_backend" | "no_park_facility";
|
|
1087
1120
|
};
|
|
1088
1121
|
/** {@link resolveStreamApprovalGate} 的入参。`backend` 用**结构形**(不 import `StoreBackend`):本模块是
|
|
1089
1122
|
* 协调器的家,不该为一个布尔判据把整棵 store 依赖树拖进类型面。 */
|
|
1090
|
-
export interface StreamApprovalGateInput {
|
|
1123
|
+
export interface StreamApprovalGateInput<TStore extends GateCurrencyStore = GateCurrencyStore> {
|
|
1091
1124
|
toolApprovalEnabled: boolean;
|
|
1092
1125
|
/** 协议总开关。⚠️ 调用点一律写 `config.streamApproval?.enabled === true` —— **可选链的缺席臂折算
|
|
1093
1126
|
* false**(段整段缺席 ⇒ 协议不上场)。
|
|
@@ -1101,14 +1134,16 @@ export interface StreamApprovalGateInput {
|
|
|
1101
1134
|
readonly kind: "mysql" | "pg" | "local";
|
|
1102
1135
|
approvalAsk(): ApprovalAskStore;
|
|
1103
1136
|
} | undefined;
|
|
1104
|
-
/** park
|
|
1105
|
-
*
|
|
1137
|
+
/** park 设施 = checkpoint 店本身:在场(非 `undefined`)即设施在场,上场时它随 `active` 臂交出去(与 `askStore` 同形)—— 在场性与实例是一个事实,调用点不必另持一份布尔再断言实例非空。
|
|
1138
|
+
* 类型只收店的形({@link GateCurrencyStore}:协调器读它的那两个口),`backend.checkpoint` 这种工厂方法过不了 tsc —— 全进程的店只有 stage-06 铸的那一只,装配点喂的是它,不是再造一只。
|
|
1139
|
+
* 本仓真码里 `checkpointStore` 的构造条件本身就是
|
|
1140
|
+
* `backend?.checkpoint !== undefined && config.durableApproval`,所以设计 §8.4 那条析取式的
|
|
1106
1141
|
* 「safety 词表非空」一支在**装配时刻**结构上不可达(core 的 irreversible/egress 集合来自 **per-task**
|
|
1107
1142
|
* 的 `spec.tools[].irreversibility/egress`,composition root 看不到)——两条缺失形因此在本仓收敛成
|
|
1108
1143
|
* 同一个 `no_park_facility`(D-5/D-6 两钉打的是两种**输入**形,不是两个 reason)。 */
|
|
1109
|
-
|
|
1144
|
+
checkpointStore: TStore | undefined;
|
|
1110
1145
|
}
|
|
1111
|
-
export declare function resolveStreamApprovalGate(input: StreamApprovalGateInput): StreamApprovalGate
|
|
1146
|
+
export declare function resolveStreamApprovalGate<TStore extends GateCurrencyStore>(input: StreamApprovalGateInput<TStore>): StreamApprovalGate<TStore>;
|
|
1112
1147
|
/**
|
|
1113
1148
|
* 审批开卡帧的**帧族词表**(闭集)—— wire 上一只 ask 的开卡帧可能属于的族:
|
|
1114
1149
|
* · `tool_approval`:旧族(活卡帧 `tool_approval` / 消卡帧 `tool_approval_complete`);
|
|
@@ -1122,6 +1157,63 @@ export declare function resolveStreamApprovalGate(input: StreamApprovalGateInput
|
|
|
1122
1157
|
*/
|
|
1123
1158
|
export declare const APPROVAL_FRAME_FAMILIES: readonly ["tool_approval", "approval_request"];
|
|
1124
1159
|
export type ApprovalFrameFamily = (typeof APPROVAL_FRAME_FAMILIES)[number];
|
|
1160
|
+
/**
|
|
1161
|
+
* 这台 worker **呈卡用的那一个族**(活卡协调器在场时)—— 全仓唯一的映射,两个读者:能力位 `approvalFrameFamilies`(`capability-facts.ts`,
|
|
1162
|
+
* 壳读它决定声明什么)与 {@link resolveApprovalLeg}(腿判「这条 run 声明了没有」)。两处各写一份三元式 = 壳照一份声明、服务端照另一份判,
|
|
1163
|
+
* 那正是 S-1914 要封的「两边各说各话」。入参 = {@link resolveStreamApprovalGate} 的 `active`。
|
|
1164
|
+
*/
|
|
1165
|
+
export declare function presentedApprovalFrameFamily(streamApprovalOn: boolean): ApprovalFrameFamily;
|
|
1166
|
+
/** 坏声明的拒体(fresh 受理面 400 `request.field_invalid`):一句话 + 机读位,形与 S-1351 座位族 / S-904 三桶的 `bad_entry` 同。 */
|
|
1167
|
+
export interface ApprovalFrameFamiliesIssue {
|
|
1168
|
+
readonly message: string;
|
|
1169
|
+
readonly fields: {
|
|
1170
|
+
readonly field: "settings.approvalFrameFamilies";
|
|
1171
|
+
readonly reason: "bad_entry";
|
|
1172
|
+
readonly index?: number;
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* S-1914 —— 请求面声明 `settings.approvalFrameFamilies` 的**唯一判官**:这条 run 的客户端渲染得了哪些审批开卡帧族。
|
|
1177
|
+
* 合法 = 数组,每项是 {@link APPROVAL_FRAME_FAMILIES} 里的词、各出现一次(空数组合法 = 一个族都不渲染,与缺席同读)。非数组 / 词表外 / 重复 ⇒ 拒体点名坐标。
|
|
1178
|
+
* 两个读者:fresh 受理门(`http/admission.ts`,400)与宽容读者 {@link declaredApprovalFrameFamilies}(续跑重放读存体)。入参是 `body.settings`
|
|
1179
|
+
* 本身(非对象 / 缺席 ⇒ 本判官不判,留给 `settings` 自己的形门)。拒句不回显调用方的字节,只报坐标与词表。
|
|
1180
|
+
*/
|
|
1181
|
+
export declare function approvalFrameFamiliesIssue(settings: unknown): ApprovalFrameFamiliesIssue | null;
|
|
1182
|
+
/**
|
|
1183
|
+
* S-1914 —— 一条 run 声明的帧族集合(三条执行腿读的**唯一**读者;读的是这条 run 的请求体 `settings`:sync = 本次请求、bg = 受理过的提交体、
|
|
1184
|
+
* resume / wake = 持久化体 —— 声明随体重放,不另存)。缺席 ⇒ 空集。判官说不合法 ⇒ 空集:只有绕过受理门的存体走得到这一形,
|
|
1185
|
+
* 空集 = 这条腿没有活审批通道(问当场交无人值守政策口)—— 失效方向是不呈卡,不是猜一个族去呈。
|
|
1186
|
+
*/
|
|
1187
|
+
export declare function declaredApprovalFrameFamilies(settings: unknown): ReadonlySet<ApprovalFrameFamily>;
|
|
1188
|
+
/** 一条腿**没有活审批通道**的成因(见 {@link ApprovalLegAssembly.unattended});`frame_family_undeclared` 带上缺的那个族。 */
|
|
1189
|
+
export type LegUnattended = {
|
|
1190
|
+
readonly arm: "window_zero";
|
|
1191
|
+
} | {
|
|
1192
|
+
readonly arm: "frame_family_undeclared";
|
|
1193
|
+
readonly family: ApprovalFrameFamily;
|
|
1194
|
+
};
|
|
1195
|
+
/** {@link resolveApprovalLeg} 的入参(sync / bg / resume 三腿 + 开流 preamble 共用一份形)。 */
|
|
1196
|
+
export interface ApprovalLegInput {
|
|
1197
|
+
/** = `resolveStreamApprovalGate(...).active`(协议在本 worker 上场没有)。 */
|
|
1198
|
+
streamApprovalOn: boolean;
|
|
1199
|
+
/** `config.streamApproval.windowMs`(`STREAM_ASK_WINDOW_MS`)。
|
|
1200
|
+
* 🔴 [ref]:`undefined` = **这份装配没有 `streamApproval` 段**(只出现在只填被测键的 stub-harness ——
|
|
1201
|
+
* 真实 boot 无条件铸该段,见 `config.ts`)。此前三个调用点各写 `?? 0` 把这一形折成「0」,在旧语义下
|
|
1202
|
+
* 是死代码(`windowZero` 那时挂在 `streamApprovalOn` 后面,协议关就恒 false);[ref] 让 `0` 在协议关的
|
|
1203
|
+
* 腿上也真生效之后,那个折算会把一台**什么都没配**的 harness 判成「运维显式关窗」。故本字段收成
|
|
1204
|
+
* `number | undefined`,由每个调用点如实交出「有没有这个值」,不许再就地编一个。 */
|
|
1205
|
+
windowMs: number | undefined;
|
|
1206
|
+
/** `config.streamAskWindowMarginMs`(`STREAM_ASK_WINDOW_MARGIN_MS`)。 */
|
|
1207
|
+
windowMarginMs: number;
|
|
1208
|
+
/** `spec.limits.maxWalltimeMs`。缺席 = 没有 walltime 墙 ⇒ 第三源不参与(**缺席 ≠ 0**)。 */
|
|
1209
|
+
legWalltimeMs?: number;
|
|
1210
|
+
/** 本腿**开始执行的时刻**的 `performance.now()`。取值点绝不能挪到腿启动之后:误差方向必须是
|
|
1211
|
+
* 「窗偏短」(安全侧,§7.3 的误差方向论证)。 */
|
|
1212
|
+
nowMonotonicMs: number;
|
|
1213
|
+
/** S-1914:这条 run 声明的帧族(唯一读者 {@link declaredApprovalFrameFamilies},读本 run 的请求体 / 存体)。必填 —— 漏传的装配点编译红,
|
|
1214
|
+
* 不许有「没传 ⇒ 当作声明了」的默认。 */
|
|
1215
|
+
declaredFamilies: ReadonlySet<ApprovalFrameFamily>;
|
|
1216
|
+
}
|
|
1125
1217
|
/**
|
|
1126
1218
|
* [ref] 车3 刀 3b —— **一条执行腿的审批装配裁定**(sync / bg / resume **三腿共用**)。
|
|
1127
1219
|
*
|
|
@@ -1140,14 +1232,19 @@ export type ApprovalFrameFamily = (typeof APPROVAL_FRAME_FAMILIES)[number];
|
|
|
1140
1232
|
* `prepare-task.js` 的 durableMandate/contentMandate 与 durableQuestionFace 等),
|
|
1141
1233
|
* 故不在本函数内。
|
|
1142
1234
|
*
|
|
1143
|
-
* 🔴 **[ref](半接线旋钮族)
|
|
1235
|
+
* 🔴 **[ref](半接线旋钮族)**:关窗的第一源(`STREAM_ASK_WINDOW_MS=0`)自此**不再挂在
|
|
1144
1236
|
* `streamApprovalOn` 后面** —— 那是部署级旋钮,它的语义不该因为这台机器有没有 durable ask 账而改变
|
|
1145
|
-
* (`ApprovalLegAssembly.
|
|
1237
|
+
* (`ApprovalLegAssembly.unattended` 顶注写了那条病)。第三源(贴 deadline)照旧只在协议上场时参与。
|
|
1238
|
+
*
|
|
1239
|
+
* 🔴 **S-1914**:「这条腿有没有活审批通道」多一个成因 —— 这条 run 没声明本 worker 呈卡用的族。它与关窗是**同一件事**(不呈卡、问当场交政策口),
|
|
1240
|
+
* 所以进同一个字段(`unattended` 的成因词),三条腿零新分支;判据读 {@link presentedApprovalFrameFamily}(能力位读同一只)。
|
|
1241
|
+
* 判据**不**放进 {@link resolveStreamApprovalGate}:那是 worker 级谓词(协调器注入 / 能力位 / 回决 501 都没有 run),且它为假时腿退回旧族 `emit` ——
|
|
1242
|
+
* 正是「回退发 `tool_approval`」。
|
|
1146
1243
|
*
|
|
1147
|
-
*
|
|
1148
|
-
*
|
|
1149
|
-
* bg=runs.ts,resume=
|
|
1150
|
-
* resume 无装配点,不包 ALS 即等价」的缺席论证已退役,别再据它推断「少装一件也行」。
|
|
1244
|
+
* **没有活审批通道的两件事必须同时做**(调用方契约):不包 ALS **且** `spec.onAsk` = {@link ToolApprovalCoordinator.boundUnattendedAsk}(带成因)。
|
|
1245
|
+
* 只做一件另一条路仍会呈卡(ALS 腿是 `deps.onAsk` 的到达路;不包 ALS 也让这条腿不进 broker 活流集合 = 不是别的腿的卡的投递目标)。
|
|
1246
|
+
* ⚠️ [ref] 起**三腿都有** per-task `spec.onAsk` 装配点(sync=routes/tasks.ts,bg=runs.ts,resume=http/resume-legs.ts),第二件在三腿都是显式闭包 ——
|
|
1247
|
+
* 旧文「bg/resume 无装配点,不包 ALS 即等价」的缺席论证已退役,别再据它推断「少装一件也行」。
|
|
1151
1248
|
*/
|
|
1152
1249
|
export interface ApprovalLegAssembly {
|
|
1153
1250
|
/** 协议在本腿上是否上场。false ⇒ **既不包 ALS、也不接** `emitCard`/`emitRevoke`/`legKey`/
|
|
@@ -1157,16 +1254,17 @@ export interface ApprovalLegAssembly {
|
|
|
1157
1254
|
* 同 (owner, session) 活流呈活卡(sync 腿 [ref] 起本就如此;bg/resume 的这条行为差 = [ref] CHANGELOG
|
|
1158
1255
|
* 披露的行为面变更)。旧文「本腿逐字保持协议之前的行为」只对协议件成立,别再把它读成「协议关 = 零呈卡」。 */
|
|
1159
1256
|
active: boolean;
|
|
1160
|
-
/**
|
|
1161
|
-
*
|
|
1162
|
-
*
|
|
1163
|
-
*
|
|
1164
|
-
*
|
|
1165
|
-
*
|
|
1166
|
-
*
|
|
1167
|
-
*
|
|
1168
|
-
|
|
1169
|
-
|
|
1257
|
+
/** **这条腿没有活审批通道**(在场 ⇒ 不包 ALS、不是任何卡的投递目标、`spec.onAsk` 是 {@link ToolApprovalCoordinator.boundUnattendedAsk}
|
|
1258
|
+
* 席 —— 每只问当场交无人值守政策口,臂 = 本值)。缺席 ⇒ 这条腿按 {@link presentedApprovalFrameFamily} 的族呈卡。成因闭集两个,先后即判序:
|
|
1259
|
+
* · `window_zero`(§8.3)—— 运维关窗(`STREAM_ASK_WINDOW_MS=0`),或协议上场时本腿剩余 walltime 撑不住安全余量。
|
|
1260
|
+
* 🔴 [ref] 起与 `active` 解耦:`0` 是**部署级**旋钮,在两条车道上是同一个意思(旧形下关协议的部署配了 `0` 拿到的是等满 5min 的活卡)。
|
|
1261
|
+
* ⚠️ 「窗**缺席**」(`windowMs: undefined` = 这份装配根本没有 streamApproval 段,只出现在 stub-harness)**不是**关窗。
|
|
1262
|
+
* 排在声明之前:那时谁都不呈卡,缺的不是客户端的声明。
|
|
1263
|
+
* · `frame_family_undeclared`(S-1914)—— 这条 run 的请求没有声明本 worker 呈卡用的族(`settings.approvalFrameFamilies`)。判据对两个族同一条:
|
|
1264
|
+
* 不回退发另一族、不双发、没有「键缺席 ⇒ 视作旧族」的默认。
|
|
1265
|
+
* 三条腿只问「在场没有」,成因随席交给唯一铸点(遥测臂 + deny 政策的拒因句)。 */
|
|
1266
|
+
unattended?: LegUnattended;
|
|
1267
|
+
/** §7.3 本 leg 的 walltime deadline(`performance.now()` 单调基)。只在 `active ∧ 有活审批通道 ∧
|
|
1170
1268
|
* 有 walltime 墙` 时在场 —— 关着时供值会把既有 5min 活卡窗按三元公式缩短(§7.2 真行为变更)。 */
|
|
1171
1269
|
legDeadlineMonotonic?: number;
|
|
1172
1270
|
}
|
|
@@ -1196,24 +1294,14 @@ export declare function createApprovalCardEmitter(sinks: {
|
|
|
1196
1294
|
/** live SSE 口。**已死的流应当由调用方判成缺席或让它抛**,不要写一个静默 no-op —— 那正是本 finding。 */
|
|
1197
1295
|
writeLive?: (frame: ApprovalCardLegFrame) => void | Promise<void>;
|
|
1198
1296
|
}): (frame: ApprovalCardLegFrame, registration: string) => Promise<void>;
|
|
1199
|
-
export declare function resolveApprovalLeg(input:
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
* `number | undefined`,由每个调用点如实交出「有没有这个值」,不许再就地编一个。 */
|
|
1208
|
-
windowMs: number | undefined;
|
|
1209
|
-
/** `config.streamAskWindowMarginMs`(`STREAM_ASK_WINDOW_MARGIN_MS`)。 */
|
|
1210
|
-
windowMarginMs: number;
|
|
1211
|
-
/** `spec.limits.maxWalltimeMs`。缺席 = 没有 walltime 墙 ⇒ 第三源不参与(**缺席 ≠ 0**)。 */
|
|
1212
|
-
legWalltimeMs?: number;
|
|
1213
|
-
/** 本腿**开始执行的时刻**的 `performance.now()`。取值点绝不能挪到腿启动之后:误差方向必须是
|
|
1214
|
-
* 「窗偏短」(安全侧,§7.3 的误差方向论证)。 */
|
|
1215
|
-
nowMonotonicMs: number;
|
|
1216
|
-
}): ApprovalLegAssembly;
|
|
1297
|
+
export declare function resolveApprovalLeg(input: ApprovalLegInput): ApprovalLegAssembly;
|
|
1298
|
+
/**
|
|
1299
|
+
* 🔴 **开流 preamble 往本连接投卡与否的唯一判据**(S-1914 / H5)—— 两条 preamble 共用:sync 腿(`routes/tasks.ts`,入参的声明读本次请求的 `settings`)与耐久事件流
|
|
1300
|
+
* (`routes/runs.ts`,声明读这条 run 的持久化体,见 `http/run-declared-families.ts`)。preamble 也是往本连接投卡:只在本腿**有活审批通道**时重放、订消卡 ——
|
|
1301
|
+
* 协议在本 worker 上场、没关窗、这条 run 声明了本 worker 呈卡用的族;与本腿呈不呈卡是同一只装配({@link resolveApprovalLeg}),不在 preamble 一侧另写一份合取式。
|
|
1302
|
+
* 为假 ⇒ 零帧且零 ask 账读(与「协议未上场」同形)。时钟不参与这一判:`nowMonotonicMs` 只喂 `legDeadlineMonotonic`(窗长三元),不进 `unattended` 的三个成因。
|
|
1303
|
+
*/
|
|
1304
|
+
export declare function approvalPreambleReplays(input: Omit<ApprovalLegInput, "nowMonotonicMs">): boolean;
|
|
1217
1305
|
/** 一条 **DECIDED 行**能交还给引擎的全部形(`AskOutcome` 减掉 park 路由那一臂 —— 一条有决议的行
|
|
1218
1306
|
* 从定义上不是「投递面拿走了它」)。{@link ToolApprovalCoordinator.notifyExternalDecision} 收的就是它。 */
|
|
1219
1307
|
export type DecidedAskOutcome = Exclude<AskOutcome, "unavailable">;
|
|
@@ -1360,6 +1448,9 @@ export declare class ToolApprovalCoordinator {
|
|
|
1360
1448
|
* 构造之后才装配;同款 holder 先例 = main.ts 的 `getRunDenySweep`)。取到 undefined ⇒ 该臂逐字回落
|
|
1361
1449
|
* 修前 404,partial 部署安全。 */
|
|
1362
1450
|
private readonly parkedRedeem;
|
|
1451
|
+
/** S-1915:按问读口对 `PARKED` 卡的门读席(见 {@link ParkedGateRead})。缺席(没有 checkpoint 店的部署 / 只装 ask 账的嵌入)⇒ 读口只能说行说的话(`parked`)。
|
|
1452
|
+
* 不是晚绑取值口:checkpoint 店在协调器构造之前就装好了(`boot/stage-06`),与 `parkCommits` 同一只店。 */
|
|
1453
|
+
private readonly parkedGate;
|
|
1363
1454
|
/** [ref] 裁 (c):local 车道的 File ask **审计** sink(在场 ⇔ `DB_BACKEND=local` ∧ 审批面开着,装配点
|
|
1364
1455
|
* `boot/coordinators.ts`)。三个挂线点:铸造(askBroadcast register 后)/ 决议(`settle()` 公共咽喉)/
|
|
1365
1456
|
* 腿闭(`runWithContext` finally)。**纯观察面**:sink 自己吞写失败并 `recordFailOpen`,任何一次审计
|
|
@@ -1401,6 +1492,8 @@ export declare class ToolApprovalCoordinator {
|
|
|
1401
1492
|
ruleScopeRootFor?: (sessionId: string | undefined) => string | undefined;
|
|
1402
1493
|
/** [ref]:PARKED 臂赎回席的**晚绑取值口**(见 {@link ToolApprovalCoordinator.parkedRedeem})。 */
|
|
1403
1494
|
parkedRedeem?: () => ParkedAskRedeem | undefined;
|
|
1495
|
+
/** S-1915:按问读口的门读席(见 {@link ToolApprovalCoordinator.parkedGate})。 */
|
|
1496
|
+
parkedGate?: ParkedGateRead;
|
|
1404
1497
|
/** [ref]:受理回执表两道界(缺省见 {@link DEFAULT_PARK_REDEMPTION_MAX}/{@link DEFAULT_PARK_REDEMPTION_TTL_MS})。
|
|
1405
1498
|
* 不是部署旋钮(不接 env),可注入只为让「有界」这件事被判据观测得到。 */
|
|
1406
1499
|
parkRedemptionMax?: number;
|
|
@@ -1428,7 +1521,7 @@ export declare class ToolApprovalCoordinator {
|
|
|
1428
1521
|
* 五臂(设计稿 §1 的表)全部读它,`unattendedPolicy` 因此是一处施加、五处消费:
|
|
1429
1522
|
* (a) 有店窗到期 `expireAsk` 赢 CAS / (b) 无店窗到期(D1)/ (c) 有店但 `expireAsk` 报错
|
|
1430
1523
|
* —— 三条走 `windowRouteUnavailable` 标志,在 {@link shapeOutcome}(askBroadcast 内)成形;
|
|
1431
|
-
* (d)
|
|
1524
|
+
* (d) 没有活审批通道的腿(装配层短路:关窗 / 未声明呈卡族,S-1914)与无 ALS 上下文的 headless 腿 —— 走本方法/{@link ask};
|
|
1432
1525
|
* (e) 断连 `forceParkNow` / emit 全灭 / 写侧准入超限 —— 走本方法。
|
|
1433
1526
|
*
|
|
1434
1527
|
* **持久回放的两个口也归它管**(codex R1-F1 验真后收口,红先钉在
|
|
@@ -1473,10 +1566,9 @@ export declare class ToolApprovalCoordinator {
|
|
|
1473
1566
|
* 读的是政策**读**口(不落遥测)。
|
|
1474
1567
|
*/
|
|
1475
1568
|
private batchIdFor;
|
|
1476
|
-
/** {@link unattendedOutcome}
|
|
1477
|
-
* (`
|
|
1478
|
-
|
|
1479
|
-
unattendedAskOutcome(site: {
|
|
1569
|
+
/** {@link unattendedOutcome} 的**公开**口:没有活审批通道的腿({@link ApprovalLegAssembly.unattended})的席长在装配层,它必须与协调器内
|
|
1570
|
+
* 五臂读**同一个**政策值,而不是各算一遍。成因(`why`)与出处随调用方给(那条腿知道自己为什么不呈卡、是哪一只 ask)。 */
|
|
1571
|
+
unattendedAskOutcome(site: LegUnattended & {
|
|
1480
1572
|
taskId?: string;
|
|
1481
1573
|
owner?: string | null;
|
|
1482
1574
|
toolName?: string;
|
|
@@ -1685,8 +1777,9 @@ export declare class ToolApprovalCoordinator {
|
|
|
1685
1777
|
* 问已经发出去了)。 */
|
|
1686
1778
|
private durableGrantHas;
|
|
1687
1779
|
/**
|
|
1688
|
-
* 🔴 **S-877 候裁 16 ——
|
|
1689
|
-
* (sync `http/routes/tasks.ts`、bg `runs.ts`、resume `http/resume-legs.ts`)
|
|
1780
|
+
* 🔴 **S-877 候裁 16 —— 无通道席**(旧名「窗 0 席」):本席**不呈卡**,但在报「无人可答」之前先问站着的答案。装配点 = 三条腿上没有活审批通道
|
|
1781
|
+
* 的那一支({@link ApprovalLegAssembly.unattended} 在场;sync `http/routes/tasks.ts`、bg `runs.ts`、resume `http/resume-legs.ts`),成因 `why`
|
|
1782
|
+
* 随席交给唯一铸点(遥测臂 + deny 政策的拒因句;S-1914 起两个成因:关窗 / 这条 run 没声明呈卡族)。修前三处是装配层的就地闭包 `() => unattendedAskOutcome(...)`
|
|
1690
1783
|
* —— 不读授权,引擎那一问(模式写门 / 形状层)在窗 0 部署上 remember 恒无效。改形后:授权命中 ⇒ `true`;否则逐字走
|
|
1691
1784
|
* {@link unattendedAskOutcome} 这**一个**政策口(五臂一处施加,[ref] R-13 C 不变)。
|
|
1692
1785
|
* parked 赎回腿的继承条目席**不**改:那条腿上到席的问全是继承问(core 盖 `mandated`),本席依约不退役,改了也兑现不了任何东西 ——
|
|
@@ -1697,7 +1790,7 @@ export declare class ToolApprovalCoordinator {
|
|
|
1697
1790
|
owner: string | null;
|
|
1698
1791
|
taskId: string;
|
|
1699
1792
|
sessionId?: string;
|
|
1700
|
-
}) => ((req: AskRequest) => Promise<AskOutcome>);
|
|
1793
|
+
}, why: LegUnattended) => ((req: AskRequest) => Promise<AskOutcome>);
|
|
1701
1794
|
/**
|
|
1702
1795
|
* 🔴 session grant 短路前的**持久出处复核**([ref] 的 codex R1-[high] 补丁;[ref] 换判据;S-944 / SN-C1 换读口)。
|
|
1703
1796
|
*
|
|
@@ -1991,6 +2084,12 @@ export declare class ToolApprovalCoordinator {
|
|
|
1991
2084
|
private readAdopted;
|
|
1992
2085
|
/** {@link readApproval} 的读行半场。 */
|
|
1993
2086
|
private readAskRowState;
|
|
2087
|
+
/**
|
|
2088
|
+
* `PARKED` 行的门读法(S-1915):问判官「这张卡的门此刻还是不是该会话当前待决的审批门」。行是终态、不随赎回 / 取消改写,所以只有 checkpoint 知道。
|
|
2089
|
+
* 没有门读席 / 行上没有 gate token ⇒ `open`(读口只能说行说的话;应答口对同一张卡的 PARKED 臂此时也答不了受理,不另有第二个答案)。
|
|
2090
|
+
* 读不出(店故障 / 超时)⇒ 按本协调器的店故障姿势(D5:记账 + 一次 warn)退回 `open` —— 这一臂只影响**展示**,应答口对同一张卡仍按自己的判官 fail-closed。
|
|
2091
|
+
*/
|
|
2092
|
+
private readParkedGate;
|
|
1994
2093
|
/** 一条行 → 按问读口的答案(行态六态穷举:新增 `AskState` 而不在此表态 = **编译错**)。 */
|
|
1995
2094
|
private readStateOfRow;
|
|
1996
2095
|
/**
|