@sema-agent/server 7.141.0 → 7.142.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 +215 -0
- package/USAGE.md +67 -12
- package/dist/approval-ask-audit-store.d.ts +54 -38
- package/dist/approval-ask-audit-store.js +58 -39
- package/dist/approval-card.d.ts +8 -8
- package/dist/approval-crash-convergence.d.ts +17 -7
- package/dist/approval-crash-convergence.js +94 -22
- package/dist/approval-deny-reasons.d.ts +34 -30
- package/dist/approval-deny-reasons.js +21 -6
- package/dist/approval-policy-settlement.d.ts +1 -1
- package/dist/approval-reconciler.d.ts +33 -1
- package/dist/approval-reconciler.js +43 -40
- package/dist/auth-bridge.d.ts +9 -6
- package/dist/auth-bridge.js +9 -3
- package/dist/boot/adoption.js +2 -1
- package/dist/boot/budget-tracing.d.ts +16 -0
- package/dist/boot/budget-tracing.js +10 -6
- package/dist/boot/device-lane.d.ts +1 -1
- package/dist/boot/execution-env.d.ts +24 -4
- package/dist/boot/execution-env.js +26 -23
- package/dist/boot/execution-lane.js +2 -1
- package/dist/boot/exit-boundary.js +5 -4
- package/dist/boot/memory-consolidation.js +2 -1
- package/dist/boot/parked-revive-gate.js +2 -1
- package/dist/boot/permission-rules-audit.d.ts +0 -28
- package/dist/boot/permission-rules-audit.js +2 -1
- package/dist/boot/read-deny-anchor-audit.js +2 -1
- package/dist/boot/reapers.js +4 -3
- package/dist/boot/resolve-spec.js +11 -2
- package/dist/boot/retention-lane.js +5 -3
- package/dist/boot/runner-deps.js +16 -13
- package/dist/boot/runtime-caps.js +3 -2
- package/dist/boot/session-faces.js +4 -3
- package/dist/boot/shutdown.js +3 -2
- package/dist/boot/stage-04-stores.js +3 -2
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.js +8 -6
- package/dist/boot/stage-06-runners.d.ts +1 -1
- package/dist/boot/stage-06-runners.js +2 -1
- package/dist/boot/stage-07-capability-layer.d.ts +1 -1
- package/dist/boot/stage-07-capability-layer.js +2 -1
- package/dist/boot/stage-08-reapers.d.ts +1 -1
- package/dist/boot/stage-08-reapers.js +3 -2
- package/dist/boot/stage-09-leader.d.ts +1 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -1
- package/dist/boot/stage-10-http-server.js +5 -4
- package/dist/boot/stores.js +3 -2
- package/dist/boot/web-admin.js +17 -17
- package/dist/boot/webfetch-summarize-lane.js +2 -1
- package/dist/boot/workflow-orchestration.js +3 -2
- package/dist/capabilities/builtin-tools.d.ts +2 -1
- package/dist/capabilities/builtin-tools.js +2 -4
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capability-facts.d.ts +24 -0
- package/dist/capability-facts.js +23 -0
- package/dist/config-catalog.js +1 -0
- package/dist/config-types.d.ts +15 -4
- package/dist/config.d.ts +6 -0
- package/dist/config.js +17 -3
- package/dist/execution-lane-caps.d.ts +2 -1
- package/dist/fleet/fleet-bus.d.ts +6 -3
- package/dist/hooks/hook-runner.d.ts +6 -0
- package/dist/hooks/hook-runner.js +30 -1
- package/dist/http/dispatch.js +4 -2
- package/dist/http/execution-context.d.ts +2 -2
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/principal-gate.d.ts +3 -1
- package/dist/http/principal-gate.js +17 -8
- package/dist/http/resume-legs.js +5 -9
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/attachments.js +18 -0
- package/dist/http/routes/capabilities.js +5 -2
- package/dist/http/routes/memory-policy.js +26 -6
- package/dist/http/routes/runs.js +3 -3
- package/dist/http/routes/session-memory-sync.d.ts +63 -0
- package/dist/http/routes/session-memory-sync.js +157 -0
- package/dist/http/routes/sessions-list.d.ts +2 -1
- package/dist/http/routes/tasks.js +7 -5
- package/dist/http/run-read-face.d.ts +7 -0
- package/dist/http/run-read-face.js +9 -3
- package/dist/http/send.d.ts +5 -0
- package/dist/http/send.js +9 -2
- package/dist/http/server.d.ts +14 -8
- package/dist/http/server.js +2 -4
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +2 -2
- package/dist/http/sync-submit-body.d.ts +21 -4
- package/dist/http/sync-submit-body.js +13 -0
- package/dist/identity-source.d.ts +21 -2
- package/dist/identity-source.js +6 -0
- package/dist/memory-incident-sink.d.ts +19 -6
- package/dist/memory-incident-sink.js +8 -4
- package/dist/memory-sync-client.d.ts +21 -21
- package/dist/memory-sync-client.js +50 -42
- package/dist/memory-sync-identity.d.ts +24 -0
- package/dist/memory-sync-identity.js +57 -0
- package/dist/memory-sync.d.ts +7 -1
- package/dist/memory-sync.js +6 -3
- package/dist/observability/fail-open.d.ts +27 -10
- package/dist/observability/fail-open.js +20 -7
- package/dist/observability/run-terminal.d.ts +92 -34
- package/dist/observability/run-terminal.js +167 -162
- package/dist/plugins/approval-ask-store-file.d.ts +14 -4
- package/dist/plugins/approval-ask-store-file.js +48 -7
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
- package/dist/plugins/approval-ask-store-memory.js +9 -8
- package/dist/plugins/approval-ask-store-sql.d.ts +42 -10
- package/dist/plugins/approval-ask-store-sql.js +49 -19
- package/dist/plugins/file-run-store.d.ts +6 -1
- package/dist/plugins/file-run-store.js +11 -1
- package/dist/plugins/local-session-store.d.ts +36 -7
- package/dist/plugins/local-session-store.js +76 -32
- package/dist/plugins/local-task-attachment-store.d.ts +12 -2
- package/dist/plugins/local-task-attachment-store.js +67 -43
- package/dist/plugins/memory-key-guards.d.ts +3 -3
- package/dist/plugins/memory-run-store.d.ts +6 -1
- package/dist/plugins/memory-run-store.js +11 -1
- package/dist/plugins/one-time-migrations.d.ts +27 -1
- package/dist/plugins/one-time-migrations.js +57 -3
- package/dist/plugins/remote-env-e2b.d.ts +23 -4
- package/dist/plugins/remote-env-e2b.js +111 -28
- package/dist/plugins/remote-env-k8s.d.ts +31 -5
- package/dist/plugins/remote-env-k8s.js +86 -19
- package/dist/plugins/remote-env-local-docker.d.ts +15 -3
- package/dist/plugins/remote-env-local-docker.js +40 -12
- package/dist/plugins/run-store-sql.d.ts +9 -1
- package/dist/plugins/run-store-sql.js +9 -1
- package/dist/plugins/store-backend.d.ts +10 -7
- package/dist/plugins/store-backend.js +1 -0
- package/dist/plugins/store-contracts.d.ts +26 -1
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/task-attachment-store.d.ts +112 -14
- package/dist/plugins/task-attachment-store.js +221 -28
- package/dist/plugins/workspace-fill.d.ts +60 -0
- package/dist/plugins/workspace-fill.js +106 -0
- package/dist/project-bundle.d.ts +38 -0
- package/dist/project-bundle.js +609 -0
- package/dist/run-local.js +2 -8
- package/dist/runs.js +1 -2
- package/dist/sandbox-egress-policy.d.ts +4 -1
- package/dist/sandbox-egress-policy.js +3 -1
- package/dist/sandbox-git-plane.d.ts +31 -12
- package/dist/sandbox-git-plane.js +24 -4
- package/dist/security.d.ts +9 -0
- package/dist/security.js +8 -0
- package/dist/session-error-wire.d.ts +24 -0
- package/dist/session-error-wire.js +7 -0
- package/dist/task-settings.d.ts +10 -2
- package/dist/task-settings.js +15 -2
- package/dist/terminal.d.ts +39 -19
- package/dist/terminal.js +65 -22
- package/dist/tool-approval.d.ts +4 -3
- package/dist/tool-approval.js +12 -14
- package/dist/trace/core-keyset-guard.d.ts +123 -6
- package/dist/trace/engine-notice-wire.d.ts +8 -2
- package/dist/trace/engine-notice-wire.js +4 -2
- package/dist/trace/exit-seat.d.ts +2 -1
- package/dist/trace/ledger-sink.d.ts +8 -4
- package/dist/trace/ledger-sink.js +2 -2
- package/dist/trace/project.d.ts +18 -1
- package/dist/trace/project.js +16 -0
- package/dist/trace/self-minted-wire.d.ts +251 -2
- package/dist/trace/self-minted-wire.js +82 -0
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/web-admin-client/apply-effective.js +21 -1
- package/dist/web-admin-client/effective-keys.js +2 -0
- package/dist/web-admin-client/types.d.ts +4 -0
- package/package.json +1 -1
- package/wire-changes/server@7.142.0.json +639 -0
|
@@ -38,26 +38,36 @@ export interface SandboxGitPlanePremises {
|
|
|
38
38
|
/** The patch judge's refusal for a pod that carries the broker (`podSpecPatchBrokerRefusal`); `undefined` = the patch may land. */
|
|
39
39
|
readonly patchRefusal: string | undefined;
|
|
40
40
|
readonly brokerImage: string | undefined;
|
|
41
|
-
/** The routed origins (`
|
|
41
|
+
/** The routed origins — the live config's `sandboxGit` block as committed (`config.sandboxGit`); `null` = no block. */
|
|
42
42
|
readonly origins: readonly string[] | null;
|
|
43
43
|
}
|
|
44
|
+
/** The boot's readings the judge needs besides config: the secret seat answer (asked once, by stage 5) and the static patch judge's refusal. */
|
|
45
|
+
export interface SandboxGitPlaneBootReadings {
|
|
46
|
+
readonly secretSeat?: SandboxSecretSeat;
|
|
47
|
+
readonly patchRefusal?: string;
|
|
48
|
+
}
|
|
44
49
|
/** THE judge: the first premise that fails, in the written order; all hold ⇒ on with per-origin rows. Pure. */
|
|
45
50
|
export declare function judgeSandboxGitPlane(p: SandboxGitPlanePremises): SandboxGitPlane;
|
|
46
51
|
/** The secret seat is asked for the plane only when every premise before it holds (a deployment the plane is off for asks nothing for it). */
|
|
47
52
|
export declare function sandboxGitPlaneAsksSecretSeat(p: Pick<SandboxGitPlanePremises, "multiTenant" | "k8sLane" | "callerCredential" | "centerUrl">): boolean;
|
|
48
|
-
/** The premises read from config, plus the two boot readings (seat answer, patch refusal) when the boot has them. */
|
|
49
|
-
export declare function sandboxGitPlanePremisesOf(config: ServiceConfig, readings:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
/** The premises read from the live config, plus the two boot readings (seat answer, patch refusal) when the boot has them. */
|
|
54
|
+
export declare function sandboxGitPlanePremisesOf(config: ServiceConfig, readings: SandboxGitPlaneBootReadings): SandboxGitPlanePremises;
|
|
55
|
+
/** The plane's read-time seat (see the file head): {@link SandboxGitPlaneSeat.now} judges on every call; {@link SandboxGitPlaneSeat.settle} asks the boot's IO premise. */
|
|
56
|
+
export interface SandboxGitPlaneSeat {
|
|
57
|
+
/** The reading now — the judge over the boot readings settled so far and the live config. Synchronous; every reader calls it per read and keeps no copy. */
|
|
58
|
+
now(): SandboxGitPlane;
|
|
59
|
+
/** Asks the secret seat once (stage 5 awaits it), only when the four premises before it hold; a later call asks nothing. */
|
|
60
|
+
settle(): Promise<void>;
|
|
61
|
+
}
|
|
53
62
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
63
|
+
* The plane's ONE seat. `premisesOf` = the premises over the live config (the execution env hands `(r) => sandboxGitPlanePremisesOf(config, r)` — the
|
|
64
|
+
* config object every commit of the effective configuration mutates); `boot.patchRefusal` = the static patch judge's answer (a boot reading from the start);
|
|
65
|
+
* `boot.askSecretSeat` = the lane's secret-seat review (only the k8s lane has one; absent ⇒ never asked ⇒ the seat stays unverified).
|
|
57
66
|
*/
|
|
58
|
-
export declare function
|
|
59
|
-
readonly
|
|
60
|
-
|
|
67
|
+
export declare function createSandboxGitPlaneSeat(premisesOf: (readings: SandboxGitPlaneBootReadings) => SandboxGitPlanePremises, boot: {
|
|
68
|
+
readonly patchRefusal?: string | undefined;
|
|
69
|
+
readonly askSecretSeat?: (() => Promise<SandboxSecretSeat>) | undefined;
|
|
70
|
+
}): SandboxGitPlaneSeat;
|
|
61
71
|
/**
|
|
62
72
|
* Wiring point — does this worker hold the credential the console's vault admits on its resolve endpoint (the worker-assertion token)?
|
|
63
73
|
* The server has no reader for it yet ⇒ `false` (the plane stays off, `no_caller_credential`). Replaced by the plane's vault client.
|
|
@@ -76,6 +86,15 @@ export interface SandboxGitRunRows {
|
|
|
76
86
|
* `sandbox_broker_identity_unread` —— every miss reads as "not sso" ⇒ no broker (fail-closed), none of them throws into the pod's connect.
|
|
77
87
|
*/
|
|
78
88
|
export declare function runIdentitySourceOf(runId: string | undefined, runs: SandboxGitRunRows | undefined, logger?: Pick<Logger, "warn">): Promise<IdentitySource | undefined>;
|
|
89
|
+
/**
|
|
90
|
+
* The k8s run binding's broker-seat reader (the adapter asks it once per pod, `K8sRunEnvSeats.brokerSeatOf`): reads the plane ONCE per pod, before the
|
|
91
|
+
* run-row read — off ⇒ none and no row read at all; on ⇒ {@link sandboxBrokerSeatOf} over that one reading and the row's recorded identity source.
|
|
92
|
+
* `runs` is the late-bound run store (absent ⇒ no row ⇒ not `sso` ⇒ none).
|
|
93
|
+
*/
|
|
94
|
+
export declare function createSandboxBrokerSeatOf(planeNow: () => SandboxGitPlane, brokerImage: string | undefined, runs: (() => SandboxGitRunRows | undefined) | undefined, logger?: Pick<Logger, "warn">): (runId: string | undefined) => Promise<{
|
|
95
|
+
readonly image: string;
|
|
96
|
+
readonly origins: readonly string[];
|
|
97
|
+
} | undefined>;
|
|
79
98
|
/** The broker seat for one run's pod: the plane on ∧ the image set ∧ the run's recorded identity source `sso` ⇒ the image + every origin of the reading (http too); otherwise none. */
|
|
80
99
|
export declare function sandboxBrokerSeatOf(plane: SandboxGitPlane, brokerImage: string | undefined, identitySource: IdentitySource | undefined): {
|
|
81
100
|
readonly image: string;
|
|
@@ -21,7 +21,7 @@ export const SANDBOX_GIT_PLANE_OFF_TEXT = {
|
|
|
21
21
|
secret_seat_denied: `the sandbox Role does not allow secrets ${SANDBOX_SECRET_SEAT_VERBS.lease.join(" / ")} (the lease Secret) in the sandbox namespace`,
|
|
22
22
|
pod_patch_reaches_broker: "the deployment's pod-spec patch could reach the broker container (a podSecurityContext, or a field outside the broker white-list)",
|
|
23
23
|
broker_image_unset: "K8S_SANDBOX_BROKER_IMAGE is not set (the broker container's image; this engine's own image is the recommended value)",
|
|
24
|
-
no_origins:
|
|
24
|
+
no_origins: `the effective configuration routes no git origins to the plane (sandboxGit.origins): the console projects the block only on this worker's own pull (${WEB_ADMIN_ENV.worker}), and a block that fails its contract is read as no origins (log line web_admin_sandbox_git_refused)`,
|
|
25
25
|
};
|
|
26
26
|
const isHttps = (url) => url !== undefined && URL.canParse(url) && new URL(url).protocol === "https:";
|
|
27
27
|
export function judgeSandboxGitPlane(p) {
|
|
@@ -73,11 +73,23 @@ export function sandboxGitPlanePremisesOf(config, readings) {
|
|
|
73
73
|
secretSeat: readings.secretSeat,
|
|
74
74
|
patchRefusal: readings.patchRefusal,
|
|
75
75
|
brokerImage: lane?.provider === "k8s" ? lane.brokerImage : undefined,
|
|
76
|
-
origins:
|
|
76
|
+
origins: config.sandboxGit?.origins.map((e) => e.origin) ?? null,
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
|
-
export function
|
|
80
|
-
|
|
79
|
+
export function createSandboxGitPlaneSeat(premisesOf, boot) {
|
|
80
|
+
let readings = boot.patchRefusal !== undefined ? { patchRefusal: boot.patchRefusal } : {};
|
|
81
|
+
let settled = false;
|
|
82
|
+
return {
|
|
83
|
+
now: () => judgeSandboxGitPlane(premisesOf(readings)),
|
|
84
|
+
settle: async () => {
|
|
85
|
+
if (settled)
|
|
86
|
+
return;
|
|
87
|
+
settled = true;
|
|
88
|
+
if (boot.askSecretSeat === undefined || !sandboxGitPlaneAsksSecretSeat(premisesOf(readings)))
|
|
89
|
+
return;
|
|
90
|
+
readings = { ...readings, secretSeat: await boot.askSecretSeat() };
|
|
91
|
+
},
|
|
92
|
+
};
|
|
81
93
|
}
|
|
82
94
|
export function readSandboxGitCallerCredential(_config) {
|
|
83
95
|
return false;
|
|
@@ -97,6 +109,14 @@ export async function runIdentitySourceOf(runId, runs, logger) {
|
|
|
97
109
|
return undefined;
|
|
98
110
|
}
|
|
99
111
|
}
|
|
112
|
+
export function createSandboxBrokerSeatOf(planeNow, brokerImage, runs, logger) {
|
|
113
|
+
return async (runId) => {
|
|
114
|
+
const plane = planeNow();
|
|
115
|
+
if (plane.state !== "on")
|
|
116
|
+
return undefined;
|
|
117
|
+
return sandboxBrokerSeatOf(plane, brokerImage, await runIdentitySourceOf(runId, runs?.(), logger));
|
|
118
|
+
};
|
|
119
|
+
}
|
|
100
120
|
export function sandboxBrokerSeatOf(plane, brokerImage, identitySource) {
|
|
101
121
|
if (plane.state !== "on" || brokerImage === undefined || brokerImage === "" || identitySource !== "sso")
|
|
102
122
|
return undefined;
|
package/dist/security.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { TaskRequestBody } from "./http/wire-types.js";
|
|
|
6
6
|
import type { StagingHandle } from "./session-sync-kernel.js";
|
|
7
7
|
import type { ClientSessionRowWrite, ReplaySessionRowWrite, ReplaySessionRowWriteOutcome, SessionRowPatch, SessionRowWriteOutcome } from "./session-row.js";
|
|
8
8
|
import type { ExecutionPlacementSeat } from "./execution-placement.js";
|
|
9
|
+
import type { SessionUnreadable } from "./session-error-wire.js";
|
|
9
10
|
import type { ForkTarget, ForkToOutcome } from "./plugins/session-fork.js";
|
|
10
11
|
import { type OwnerlessClaim } from "./delegated-session-owner.js";
|
|
11
12
|
/** One row of the session-list projection (CC /resume picker) — the durable session abstraction's enumeration
|
|
@@ -38,6 +39,12 @@ export interface SessionListItem {
|
|
|
38
39
|
* primitive ∧ the row has an occupant token); additive key, passed through unprojected (`sessions-list.ts`). Same name and meaning as
|
|
39
40
|
* `SessionSummary.rowTag` in `plugins/store-contracts.ts`. */
|
|
40
41
|
rowTag?: string;
|
|
42
|
+
/** The session's archive cannot be read — present-iff the session head's leaf read throws (a list row is a session ROW: its facts live on the
|
|
43
|
+
* row and read fine; this says the CONTENT does not). `errorCode` = the code the session's own reads answer for it (`GET /v1/sessions/:id` /
|
|
44
|
+
* `…/head` / `…/events` / `PATCH` once admitted) — one table, `openFaceFaultWire` in `session-error-wire.ts`, the only minter `sessionUnreadableOf`.
|
|
45
|
+
* A store whose head leaf is a row column (the SQL twins) never carries it. Additive key, passed through unprojected (`sessions-list.ts`).
|
|
46
|
+
* Same name and meaning as `SessionSummary.unreadable` in `plugins/store-contracts.ts`. */
|
|
47
|
+
unreadable?: SessionUnreadable;
|
|
41
48
|
}
|
|
42
49
|
/**
|
|
43
50
|
* 会话店的行格写原语(规则与判序:`session-row.ts` 头注「写原语」)—— 按入口分两只重载:客户端写(PATCH)的结局闭集四员;行重放(2c 导入落定后的那一笔)
|
|
@@ -456,6 +463,8 @@ export declare function setSsoScope(req: IncomingMessage, scope: string): void;
|
|
|
456
463
|
export declare function ssoVerifiedScope(req: IncomingMessage): string | undefined;
|
|
457
464
|
export declare function setSsoRole(req: IncomingMessage, role: string): void;
|
|
458
465
|
export declare function ssoVerifiedRole(req: IncomingMessage): string | undefined;
|
|
466
|
+
export declare function setSsoPrincipalInc(req: IncomingMessage, principalInc: string): void;
|
|
467
|
+
export declare function ssoVerifiedPrincipalInc(req: IncomingMessage): string | undefined;
|
|
459
468
|
export declare function verifiedPrincipal(req: IncomingMessage, config: ServiceConfig): string | undefined;
|
|
460
469
|
export {};
|
|
461
470
|
//# sourceMappingURL=security.d.ts.map
|
package/dist/security.js
CHANGED
|
@@ -210,6 +210,14 @@ export function ssoVerifiedRole(req) {
|
|
|
210
210
|
const v = SSO_ROLES.get(req);
|
|
211
211
|
return v !== undefined && v.length > 0 ? v : undefined;
|
|
212
212
|
}
|
|
213
|
+
const SSO_PRINCIPAL_INCS = new WeakMap();
|
|
214
|
+
export function setSsoPrincipalInc(req, principalInc) {
|
|
215
|
+
SSO_PRINCIPAL_INCS.set(req, principalInc);
|
|
216
|
+
}
|
|
217
|
+
export function ssoVerifiedPrincipalInc(req) {
|
|
218
|
+
const v = SSO_PRINCIPAL_INCS.get(req);
|
|
219
|
+
return v !== undefined && v.length > 0 ? v : undefined;
|
|
220
|
+
}
|
|
213
221
|
export function verifiedPrincipal(req, config) {
|
|
214
222
|
const sso = ssoVerifiedPrincipal(req);
|
|
215
223
|
if (sso) {
|
|
@@ -57,6 +57,12 @@
|
|
|
57
57
|
*
|
|
58
58
|
* core 给闭集加一个词 ⇒ 下面的 `switch` 编译红,由人裁定它在两面上的答复,而不是让它静默落进某个恰好存在的臂
|
|
59
59
|
* ([ref] 词表纪律;同形先例 `session-policy-wire.ts` 的 `sessionPolicyErrorHttp`)。
|
|
60
|
+
*
|
|
61
|
+
* ## 读一只会话的存档时的故障:一张表、两个读者({@link openFaceFaultWire})
|
|
62
|
+
* open 面映射有臂 ⇒ 那一格;否则(open 面留白的词,与不是 `SessionError` 的读故障 —— I/O 级的 EACCES / EISDIR …,core 的读腿只吞 ENOENT、其余 errno 原样抛)
|
|
63
|
+
* ⇒ 通用 500 {@link INTERNAL_ERROR_WIRE}。读者:① 全局兜底(`http/server.ts`:路由里逃出来的故障就按它答 —— 会话视图 / 头探针 / 事件流 / 改名门放行之后对坏档答的码
|
|
64
|
+
* 都出自这里);② 会话列表行的 `unreadable`({@link sessionUnreadableOf}:列表行 = 会话行,头的叶子读不出 ⇒ 行带标记,码 = 打开它时读口答的那一个)。
|
|
65
|
+
* 两个读者读同一只函数,列表说的码与读口答的码不会分叉。
|
|
60
66
|
*/
|
|
61
67
|
import { SessionError } from "@sema-agent/core";
|
|
62
68
|
/** core `SessionError` 的码闭集(core 不从包根导出这个型名 ⇒ 从导出类的 `code` 键派生,不手抄)。 */
|
|
@@ -103,6 +109,24 @@ export declare const STAMPED_SESSION_ARCHIVE_SENTENCE = "this session's stored a
|
|
|
103
109
|
* 按码答复不同),其余按码({@link sessionErrorHttp})。
|
|
104
110
|
*/
|
|
105
111
|
export declare function sessionErrorWireOf(e: SessionError, face: SessionErrorFace): SessionErrorWire | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* 全局兜底的末臂:open 面映射没有臂的故障(登记过的留白词;不是 `SessionError` 的故障)⇒ 通用 500,句子固定(故障的原句只进运维日志 `request_error`,
|
|
114
|
+
* 不上 wire —— I/O 级的原句带 errno 与盘上路径)。
|
|
115
|
+
*/
|
|
116
|
+
export declare const INTERNAL_ERROR_WIRE: SessionErrorWire & {
|
|
117
|
+
readonly sentence: string;
|
|
118
|
+
};
|
|
119
|
+
/** 一只读存档的故障在 open 面上的 wire 答复(两个读者见文件头注「一张表、两个读者」):有臂 ⇒ 那一格;否则 {@link INTERNAL_ERROR_WIRE}。 */
|
|
120
|
+
export declare function openFaceFaultWire(fault: unknown): SessionErrorWire;
|
|
121
|
+
/**
|
|
122
|
+
* 会话列表行的加性键 `unreadable`:这只会话的头的叶子读不出(存档读不出)。present-iff 读不出;`errorCode` = 读口打开这只会话时答的码
|
|
123
|
+
* (`GET /v1/sessions/:id` / `…/head` / `…/events` / `PATCH` 门放行之后)。今天读得到的值:`corrupt_session`(会话头坏)· `internal.error`(I/O 级)。
|
|
124
|
+
*/
|
|
125
|
+
export interface SessionUnreadable {
|
|
126
|
+
readonly errorCode: string;
|
|
127
|
+
}
|
|
128
|
+
/** {@link SessionUnreadable} 的唯一铸点(列表行的那只标记;码经 {@link openFaceFaultWire},与全局兜底同表)。 */
|
|
129
|
+
export declare function sessionUnreadableOf(fault: unknown): SessionUnreadable;
|
|
106
130
|
export declare function sessionErrorHttp(code: SessionErrorCode, face: SessionErrorFace): SessionErrorWire | undefined;
|
|
107
131
|
export declare function isSessionErrorCode(code: string): code is SessionErrorCode;
|
|
108
132
|
//# sourceMappingURL=session-error-wire.d.ts.map
|
|
@@ -23,6 +23,13 @@ const STAMPED_SESSION_ARCHIVE = { status: 409, errorCode: "conflict", sentence:
|
|
|
23
23
|
export function sessionErrorWireOf(e, face) {
|
|
24
24
|
return e instanceof StampedSessionArchiveError ? STAMPED_SESSION_ARCHIVE : sessionErrorHttp(e.code, face);
|
|
25
25
|
}
|
|
26
|
+
export const INTERNAL_ERROR_WIRE = { status: 500, errorCode: "internal.error", sentence: "internal error" };
|
|
27
|
+
export function openFaceFaultWire(fault) {
|
|
28
|
+
return (fault instanceof SessionError ? sessionErrorWireOf(fault, "open") : undefined) ?? INTERNAL_ERROR_WIRE;
|
|
29
|
+
}
|
|
30
|
+
export function sessionUnreadableOf(fault) {
|
|
31
|
+
return { errorCode: openFaceFaultWire(fault).errorCode };
|
|
32
|
+
}
|
|
26
33
|
export function sessionErrorHttp(code, face) {
|
|
27
34
|
switch (code) {
|
|
28
35
|
case "corrupt_session":
|
package/dist/task-settings.d.ts
CHANGED
|
@@ -52,6 +52,7 @@ import { type EntryProblem } from "./entry-problems.js";
|
|
|
52
52
|
import { type RuleListEntry } from "./permission-rule-list.js";
|
|
53
53
|
import { type KeyClosureIssue } from "./request-key-closure.js";
|
|
54
54
|
import { type RunnableHooksConfig } from "./hooks/hook-runner.js";
|
|
55
|
+
import { type HttpHookAllowlistLayer } from "@sema-agent/settings-schema";
|
|
55
56
|
import type { StoredHooksRereadM20 } from "./stored-request-hooks-m20.js";
|
|
56
57
|
import type { EntryPermissionMode } from "./runtime-governance.js";
|
|
57
58
|
import { type TightenOnlySeatValues } from "./tighten-only-seats.js";
|
|
@@ -242,6 +243,9 @@ export interface ParsedTaskSettings {
|
|
|
242
243
|
* wire 位置由座位表说(三座在 `settings` 顶层,`neverAuto` 在 `settings.permissions`),解析后与位置无关。空数组 ≡ 缺席(不铸键)。
|
|
243
244
|
* 去处:三座经 {@link applyTaskSettings} 落 core 任务层席;`neverAuto` 经 {@link deriveSettingsPolicy} 落请求叶策略。 */
|
|
244
245
|
tightenOnly?: TightenOnlySeatValues;
|
|
246
|
+
/** http hook 两张名单(`settings.allowedHttpHookUrls` / `settings.httpHookAllowedEnvVars`)作为**一个设置层**:源名 {@link REQUEST_SETTINGS_SOURCE} + 两键里在场的那几把,值原样(本服务不读内容)。
|
|
247
|
+
* 两键都缺席 ⇒ 不铸键。唯一读者 = `hooks/hook-runner.ts` 的 http 腿(经 hooks 注册上下文的 `httpHookAllowlistLayers`)。 */
|
|
248
|
+
httpHookAllowlist?: HttpHookAllowlistLayer;
|
|
245
249
|
}
|
|
246
250
|
/** The result of parsing an untrusted `body.settings`: the applied subset + the names of present-but-deferred
|
|
247
251
|
* fields (so the caller can log/advertise them — no silent drop). `null` settings ⇒ `{ settings: undefined }`. */
|
|
@@ -313,8 +317,12 @@ export declare function settingsPermissionBucketIssue(perms: unknown): SettingsP
|
|
|
313
317
|
* 🔴 两个 `skillListing*`([ref],7.77.0)= **CC settings 同名键**,值域抄 CC 的 schema 声明
|
|
314
318
|
* (`skillListingBudgetFraction` 是 `gt(0).lte(1)`、`skillListingMaxDescChars` 是 `int().positive()`);
|
|
315
319
|
* 消费点 = 本文件的 parse + `boot/resolve-spec.ts` 的 `TaskSpec` 透传。语义(预算怎么花、描述怎么裁、
|
|
316
|
-
* 溢出怎么退成 name-only)整个归引擎,本仓一个字节都不渲染,也**不复刻默认值**——缺席即整键不写。
|
|
317
|
-
|
|
320
|
+
* 溢出怎么退成 name-only)整个归引擎,本仓一个字节都不渲染,也**不复刻默认值**——缺席即整键不写。
|
|
321
|
+
*
|
|
322
|
+
* 🔴 `allowedHttpHookUrls` / `httpHookAllowedEnvVars` = **CC settings 同名键**(契约包的 http hook 两张名单,键表属主 = 契约包 `HTTP_HOOK_ALLOWLIST_KEYS`):
|
|
323
|
+
* 消费点 = {@link parseTaskSettings} 把它们原样收成一层({@link requestHttpHookAllowlistLayer})→ `boot/resolve-spec.ts` 交给 hooks 注册 → `hooks/hook-runner.ts` 的 http 腿
|
|
324
|
+
* 在拨出之前问契约包判官。受理面只判键名(本门),不判值:名单的读法只有判官那一只(坏形由判官答 `malformed`、拦)。 */
|
|
325
|
+
export declare const TASK_SETTINGS_KEYS: readonly ["allowedHttpHookUrls", "approvalFrameFamilies", "complianceAdditionalDenies", "env", "hooks", "httpHookAllowedEnvVars", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
|
|
318
326
|
/** 本 service 消费的 `settings.permissions` 子键 —— 闭集(值形校验另有既有门,本门只判键名)。 */
|
|
319
327
|
export declare const TASK_SETTINGS_PERMISSION_KEYS: readonly ["allow", "ask", "defaultMode", "deny", "disableAutoMode", "neverAuto"];
|
|
320
328
|
/** CC/SDK 合约里合法、本 service **不接线**的顶层键。词表外的 CC 键落 `unknown` —— 两列都是 400,标签只影响
|
package/dist/task-settings.js
CHANGED
|
@@ -8,7 +8,7 @@ import { limitRefusal } from "./limit-refusal.js";
|
|
|
8
8
|
import { tightenGoverned } from "./governance-overrides.js";
|
|
9
9
|
import { MAX_KEY_ECHO, keyClosureClause, scanKeyClosure } from "./request-key-closure.js";
|
|
10
10
|
import { hooksCoordinate, parseHooksConfig } from "./hooks/hook-runner.js";
|
|
11
|
-
import { SETTINGS_SOURCES } from "@sema-agent/settings-schema";
|
|
11
|
+
import { HTTP_HOOK_ALLOWLIST_KEYS, SETTINGS_SOURCES } from "@sema-agent/settings-schema";
|
|
12
12
|
import { heldRuleName } from "./tool-rule-names.js";
|
|
13
13
|
import { policyAskStandingAnswerFacts, standingAnswerDurablyRecordable } from "./tool-approval.js";
|
|
14
14
|
import { createRequestNeverAutoPolicy } from "./approval.js";
|
|
@@ -160,7 +160,7 @@ export function settingsPermissionBucketIssue(perms) {
|
|
|
160
160
|
function acceptedRuleList(v) {
|
|
161
161
|
return Array.isArray(v) && v.length > 0 ? v.slice() : undefined;
|
|
162
162
|
}
|
|
163
|
-
export const TASK_SETTINGS_KEYS = ["approvalFrameFamilies", "complianceAdditionalDenies", "env", "hooks", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
|
|
163
|
+
export const TASK_SETTINGS_KEYS = ["allowedHttpHookUrls", "approvalFrameFamilies", "complianceAdditionalDenies", "env", "hooks", "httpHookAllowedEnvVars", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
|
|
164
164
|
export const TASK_SETTINGS_PERMISSION_KEYS = ["allow", "ask", "defaultMode", "deny", "disableAutoMode", "neverAuto"];
|
|
165
165
|
export const TASK_SETTINGS_UNSUPPORTED_KEYS = ["apiKeyHelper", "crossSessionInbound", "dialogExpiry", "sandbox", "statusLine"];
|
|
166
166
|
export const TASK_SETTINGS_UNSUPPORTED_PERMISSION_KEYS = ["additionalDirectories", "disableBypassPermissionsMode"];
|
|
@@ -228,6 +228,16 @@ export function hooksKeyIssue(raw, config) {
|
|
|
228
228
|
export function taskHooksHonored(config) {
|
|
229
229
|
return !DEPLOYMENT_SHAPE_PREDICATES.multiTenant.holds(config);
|
|
230
230
|
}
|
|
231
|
+
const REQUEST_SETTINGS_SOURCE = "flagSettings";
|
|
232
|
+
function requestHttpHookAllowlistLayer(s) {
|
|
233
|
+
const present = HTTP_HOOK_ALLOWLIST_KEYS.filter((key) => Object.hasOwn(s, key));
|
|
234
|
+
if (present.length === 0)
|
|
235
|
+
return undefined;
|
|
236
|
+
const layer = { source: REQUEST_SETTINGS_SOURCE };
|
|
237
|
+
for (const key of present)
|
|
238
|
+
layer[key] = s[key];
|
|
239
|
+
return layer;
|
|
240
|
+
}
|
|
231
241
|
function tightenOnlySeatDeferredName(name) {
|
|
232
242
|
return tightenOnlySeatField(name).replace(/^settings\./, "");
|
|
233
243
|
}
|
|
@@ -303,6 +313,9 @@ export function parseTaskSettings(raw, seats = {}) {
|
|
|
303
313
|
deferred.push(tightenOnlySeatDeferredName(name));
|
|
304
314
|
if (Object.keys(tightenSeats.values).length > 0)
|
|
305
315
|
out.tightenOnly = tightenSeats.values;
|
|
316
|
+
const httpHookAllowlist = requestHttpHookAllowlistLayer(s);
|
|
317
|
+
if (httpHookAllowlist !== undefined)
|
|
318
|
+
out.httpHookAllowlist = httpHookAllowlist;
|
|
306
319
|
return { settings: Object.keys(out).length > 0 ? out : undefined, deferred, ...(hooksReread !== undefined ? { hooksReread } : {}) };
|
|
307
320
|
}
|
|
308
321
|
export function acceptAppendSystemPrompt(v, warn) {
|
package/dist/terminal.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 终局因由(`TaskResult.terminal`)的 server 侧**读**面 ——
|
|
2
|
+
* 终局因由(`TaskResult.terminal`)的 server 侧**读**面 —— 一个叶子模块:依赖 `@sema-agent/core`、进程级记录器(`observability/fail-open.ts`)
|
|
3
|
+
* 与秘密脱敏器(`trace/redact.ts`,只作码判官 {@link isCodeToken} 的一条合取);本仓的结果汇点 / 出口座反过来依赖本模块,本模块不依赖它们。
|
|
3
4
|
*
|
|
4
5
|
* core 7.6.0([ref] S6 D-8,BREAKING)把 `TaskResult` 上并列的八个平面键
|
|
5
6
|
* (`status` / `errorCode` / `errorMessage` / `blockedReason` / `checkpointToken` / `checkpointId` /
|
|
@@ -57,18 +58,30 @@ export declare const STORE_ERROR_CODE = "internal.store_error";
|
|
|
57
58
|
*
|
|
58
59
|
* ⚠️ **辖域如实**:本臂触发条件是「码出了本仓的词法」,所以任何依赖铸出的域外码都落这里,不只 SQL 驱动。
|
|
59
60
|
* 今天可观察的产出方**只有**耐久店那一层(引擎自己的传输/brain 错误在 core 侧已被分类成小写码),故码名
|
|
60
|
-
* 取 `internal.store_error`;原码逐字进 message 尾注(随结果过汇点脱敏)
|
|
61
|
+
* 取 `internal.store_error`;原码逐字进 message 尾注(随结果过汇点脱敏)、是码时进运维线索({@link isCodeToken}),归因从不丢失。
|
|
61
62
|
*/
|
|
62
63
|
export declare function outwardFailureCode(code: string): string;
|
|
63
64
|
/**
|
|
64
|
-
* 一个错误的 `code`
|
|
65
|
-
*
|
|
66
|
-
*
|
|
65
|
+
* 一个错误的 `code` 只在它**是**码时才说出口 —— 一只判官,两条合取:
|
|
66
|
+
* ① **形**:短 token。驱动码 `ER_*` / SQLSTATE / `ECONNRESET` 全在这一形里;带空白、`=`、换行或超过 64 字符的就是一句话,不是码;
|
|
67
|
+
* ② **不是凭据形**:秘密脱敏器({@link redactSecrets})原样放过它。`sk-…` / `AKIA…` / `ghp_…` 这类秘密过得了 ①,而 wire 上同一只脱敏器
|
|
68
|
+
* 会把它从 message 尾注里遮掉 —— 日志行若照印,就是 wire 遮、日志露;所以「长得像码的秘密」在这里判「不是码」,日志与 wire 同读。
|
|
69
|
+
* 先判 ①:脱敏器只读 ≤ 64 个 token 字符(扫描代价有界);只问不出门(`observe: false`,与影子重扫同口 —— 不计「已脱」读数)。
|
|
70
|
+
* 读者两只,同一条判据:折码的运维线索(本文件 {@link sealResultForExit} / {@link sealStoredResult})与执行车道占位读的故障词(`boot/execution-env.ts`)。
|
|
67
71
|
*/
|
|
68
72
|
export declare function isCodeToken(code: unknown): code is string;
|
|
73
|
+
/**
|
|
74
|
+
* 一个错误**说得出口**的机器码,以日志字段 / 指标标签的形交回:`err.code` 是串且 {@link isCodeToken} 判它是码 ⇒ `{ code }`(逐字);其余(没有 / 不是串 / 空串 / 非 `Error` 抛出物 / 读取时抛 / 不是码)⇒ `{}`
|
|
75
|
+
* —— 键缺席,不补 `null` / `none` 之类的占位。「读口 + 判官 + 字段形」的**唯一**组合:引擎交来的错上的 `code` 是别人写的串,凡把它写进运维日志行或指标标签的汇点
|
|
76
|
+
* (`onError` 汇点的七条行与 `runner_config_advisory_total` 的标签、记忆引擎事故席)都经这一只,不各写一份读法、也不各设一份形判。读口 {@link errorCodeOf} 不抛。
|
|
77
|
+
* 显式标注返回类型:推断型 `{ code?: undefined } | { code: string }` 进不了指标标签 `Record<string, string>`。
|
|
78
|
+
*/
|
|
79
|
+
export declare function codeFieldOf(err: unknown): {
|
|
80
|
+
readonly code?: string;
|
|
81
|
+
};
|
|
69
82
|
/**
|
|
70
83
|
* 存量结果被**再观看**的读座(闭集):`GET /v1/runs/:id → result`(终态行与停驻行同一座)与账本 `done` 行的回放(三条耐久读腿共用)。
|
|
71
|
-
*
|
|
84
|
+
* 读入口 {@link sealStoredResult} 必须点名其一(类型级必填);`exit` 不在这里 —— 它只由写入口 {@link sealResultForExit} 给。
|
|
72
85
|
*/
|
|
73
86
|
export type StoredResultReadSeat = "run.result" | "ev.done.result";
|
|
74
87
|
/**
|
|
@@ -111,7 +124,7 @@ export declare function mapPauseSeats<T>(result: T, f: (seat: SeatObject, keys:
|
|
|
111
124
|
/**
|
|
112
125
|
* **一条结果离开引擎、进入本仓(可重放账本 / run 行 / wire)的唯一封口** —— 两件事,一个出口:
|
|
113
126
|
* ①摘掉**耐久恢复能力凭据**(`CheckpointToken`)—— 座表 {@link PAUSE_SEATS} 上的每一座;②把**域外的失败码**折成本仓的
|
|
114
|
-
* `internal.store_error`({@link outwardFailureCode},[ref])。
|
|
127
|
+
* `internal.store_error`({@link outwardFailureCode},[ref])—— 码座两只:因由的 `terminal.code` 与 cascade 每一级的 `attempts[].errorCode`(同一类码,见 {@link foldOutwardFailureCodes})。
|
|
115
128
|
*
|
|
116
129
|
* 🔴 为什么两件事合在一只函数里而不是并列两只(7.92.2 改名 `stripResumeToken` → `sealResultForExit`):
|
|
117
130
|
* 它们是**同一个出口上的同一件事** —— 「这条结果可以见人了吗」。并列两只 = 六个结算点各自要记得调两次,
|
|
@@ -129,23 +142,30 @@ export declare function mapPauseSeats<T>(result: T, f: (seat: SeatObject, keys:
|
|
|
129
142
|
* `/v1/approvals` 决策端点,凭 `checkpointId` 对账,从来不是拿 token 直连引擎。
|
|
130
143
|
*
|
|
131
144
|
* **出口全集都经这一口**:三家 run 店的行写口与账本店的 `done` 行写口在店入口调它(`observability/run-terminal.ts`),
|
|
132
|
-
* 同步 200 体 / 流 done 帧 /
|
|
133
|
-
* 升级前落盘、还带着凭据的存量行在读面上同样摘掉。两件事都**幂等**,重复过口是同形。
|
|
134
|
-
*
|
|
135
|
-
* ──
|
|
136
|
-
* `
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* ·
|
|
140
|
-
* ·
|
|
141
|
-
*
|
|
142
|
-
*
|
|
145
|
+
* 同步 200 体 / 流 done 帧 / 续跑决定腿在铸体处调它;`GET /v1/runs/:id{,/events}` 等读面在回放前过同一套封口的**读入口**
|
|
146
|
+
* {@link sealStoredResult} —— 升级前落盘、还带着凭据的存量行在读面上同样摘掉。两件事都**幂等**,重复过口是同形。
|
|
147
|
+
*
|
|
148
|
+
* ── 折码的运维线索:一句定形字段,两只入口各说一只词 ──
|
|
149
|
+
* 字段 `original=<码 | <not-a-code>> outward=<对外词> messageBytes=<n> at=<观看点>`,只有闭集词、数字与码({@link isCodeToken}):
|
|
150
|
+
* 失败句只报字节数,**永不**报原文 —— 失败句是自由文本(驱动句里可以有连接串 / 口令),结果里的那份由结果汇点脱敏,运维线索没有汇点,
|
|
151
|
+
* 所以压根不带它;码形座里装着文字的、长得像码的秘密同理只报 `<not-a-code>`。`at` 说这是哪一次观看,**一个入口一只词**(类型给定,不靠记得传):
|
|
152
|
+
* · 本函数 = 结果**第一次**离开引擎(店的写口 / 出口铸体)⇒ `at=exit`,每折一次一行 `[terminal] outward_failure_code_folded <字段>` —— 每次都是一件新的失败;
|
|
153
|
+
* · {@link sealStoredResult} = 存量行的**再观看**(读座必填)⇒ 走 fail-open 纪律(登记 tag `server.terminal.stored-result-code-folded`):
|
|
154
|
+
* 逐读计数 + 逐读探针行 + 每进程一次 warn,detail 即上面那句字段。读面每次轮询 / 每个观看者 / 每次重连都重放同一批行 ——
|
|
155
|
+
* 计数让「旧失败被读了多少次」看得见,一次 warn 让日志不随读次放大;去重归记录器一只,不另造表。
|
|
143
156
|
*
|
|
144
157
|
* 对任何输入安全(非普通对象 / 无可摘的座 / 码在词法内 ⇒ **同一引用**原样返回,不重塑)。
|
|
145
158
|
*/
|
|
146
159
|
export declare function sealResultForExit<T extends {
|
|
147
160
|
readonly terminal?: unknown;
|
|
148
|
-
}>(result: T
|
|
161
|
+
}>(result: T): T;
|
|
162
|
+
/**
|
|
163
|
+
* **存量结果被再观看时的封口** —— 与 {@link sealResultForExit} 同一套封口(摘凭据 + 折码,同判同形),只差运维线索的观看点:
|
|
164
|
+
* 读座({@link StoredResultReadSeat})**必填**,于是新开一条读腿忘了点名读座是 tsc 红,不是悄悄落成 `at=exit`、按读次放大。
|
|
165
|
+
*/
|
|
166
|
+
export declare function sealStoredResult<T extends {
|
|
167
|
+
readonly terminal?: unknown;
|
|
168
|
+
}>(result: T, seat: StoredResultReadSeat): T;
|
|
149
169
|
/**
|
|
150
170
|
* **投影出口的唯一属主**([ref])—— core 的 `terminalProjection` + 对外码归一,一次。
|
|
151
171
|
*
|
package/dist/terminal.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { terminalProjection } from "@sema-agent/core";
|
|
2
|
+
import { errorCodeOf } from "./observability/err-text.js";
|
|
2
3
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
4
|
+
import { redactSecrets } from "./trace/redact.js";
|
|
3
5
|
export function failureCodeOf(terminal) {
|
|
4
6
|
return terminal.kind === "failed" ? terminal.code : undefined;
|
|
5
7
|
}
|
|
@@ -9,9 +11,13 @@ export function outwardFailureCode(code) {
|
|
|
9
11
|
return OUTWARD_CODE_SHAPE.test(code) ? code : STORE_ERROR_CODE;
|
|
10
12
|
}
|
|
11
13
|
export function isCodeToken(code) {
|
|
12
|
-
return typeof code === "string" && CODE_TOKEN_SHAPE.test(code);
|
|
14
|
+
return typeof code === "string" && CODE_TOKEN_SHAPE.test(code) && redactSecrets(code, { observe: false }) === code;
|
|
13
15
|
}
|
|
14
16
|
const CODE_TOKEN_SHAPE = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
17
|
+
export function codeFieldOf(err) {
|
|
18
|
+
const code = errorCodeOf(err);
|
|
19
|
+
return isCodeToken(code) ? { code } : {};
|
|
20
|
+
}
|
|
15
21
|
export const PAUSE_SEATS = [
|
|
16
22
|
{ path: ["terminal"], token: "token", gate: "gate" },
|
|
17
23
|
{ path: ["terminal", "nestedPause"], token: "token", gate: "gate" },
|
|
@@ -41,36 +47,73 @@ function dropCapability(seat, keys) {
|
|
|
41
47
|
const { [keys.token]: _drop, ...rest } = seat;
|
|
42
48
|
return rest;
|
|
43
49
|
}
|
|
44
|
-
export function sealResultForExit(result
|
|
50
|
+
export function sealResultForExit(result) {
|
|
51
|
+
return seal(result, "exit");
|
|
52
|
+
}
|
|
53
|
+
export function sealStoredResult(result, seat) {
|
|
54
|
+
return seal(result, seat);
|
|
55
|
+
}
|
|
56
|
+
function seal(result, at) {
|
|
45
57
|
if (result === null || typeof result !== "object")
|
|
46
58
|
return result;
|
|
47
|
-
return
|
|
59
|
+
return foldOutwardFailureCodes(mapPauseSeats(result, dropCapability), at);
|
|
48
60
|
}
|
|
49
|
-
function
|
|
50
|
-
const
|
|
51
|
-
|
|
61
|
+
function foldOutwardFailureCodes(result, at) {
|
|
62
|
+
const terminal = foldCauseCode(result.terminal, at);
|
|
63
|
+
const priorAttempts = result.attempts;
|
|
64
|
+
const attempts = foldAttemptCodes(priorAttempts, at);
|
|
65
|
+
const terminalKept = Object.is(terminal, result.terminal);
|
|
66
|
+
const attemptsKept = Object.is(attempts, priorAttempts);
|
|
67
|
+
if (terminalKept && attemptsKept)
|
|
52
68
|
return result;
|
|
69
|
+
return { ...result, ...(terminalKept ? {} : { terminal }), ...(attemptsKept ? {} : { attempts }) };
|
|
70
|
+
}
|
|
71
|
+
function foldCauseCode(terminal, at) {
|
|
72
|
+
const t = failedCauseWithCode(terminal);
|
|
73
|
+
if (t === undefined)
|
|
74
|
+
return terminal;
|
|
53
75
|
const outward = outwardFailureCode(t.code);
|
|
54
76
|
if (outward === t.code)
|
|
55
|
-
return
|
|
56
|
-
reportFold(t.code, outward, t.message,
|
|
77
|
+
return terminal;
|
|
78
|
+
reportFold(t.code, outward, t.message, at);
|
|
57
79
|
const message = typeof t.message === "string" ? t.message : "the durable store failed";
|
|
58
|
-
return { ...
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (readFoldLatch.has(key))
|
|
80
|
+
return { ...t, code: outward, message: `${message} [original code: ${t.code}]` };
|
|
81
|
+
}
|
|
82
|
+
function foldAttemptCodes(attempts, at) {
|
|
83
|
+
if (!Array.isArray(attempts))
|
|
84
|
+
return attempts;
|
|
85
|
+
let folded;
|
|
86
|
+
attempts.forEach((attempt, i) => {
|
|
87
|
+
if (attempt === null || typeof attempt !== "object")
|
|
67
88
|
return;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
89
|
+
const code = attempt.errorCode;
|
|
90
|
+
if (typeof code !== "string")
|
|
91
|
+
return;
|
|
92
|
+
const outward = outwardFailureCode(code);
|
|
93
|
+
if (outward === code)
|
|
94
|
+
return;
|
|
95
|
+
reportFold(code, outward, undefined, at);
|
|
96
|
+
(folded ??= [...attempts])[i] = { ...attempt, errorCode: outward };
|
|
97
|
+
});
|
|
98
|
+
return folded ?? attempts;
|
|
99
|
+
}
|
|
100
|
+
const STORED_RESULT_FOLD_TAG = "server.terminal.stored-result-code-folded";
|
|
101
|
+
function reportFold(code, outward, message, at) {
|
|
72
102
|
const messageBytes = typeof message === "string" ? Buffer.byteLength(message, "utf8") : 0;
|
|
73
|
-
|
|
103
|
+
const fields = `original=${isCodeToken(code) ? code : "<not-a-code>"} outward=${outward} messageBytes=${messageBytes} at=${at}`;
|
|
104
|
+
switch (at) {
|
|
105
|
+
case "exit":
|
|
106
|
+
console.warn(`[terminal] outward_failure_code_folded ${fields}`);
|
|
107
|
+
return;
|
|
108
|
+
case "run.result":
|
|
109
|
+
case "ev.done.result":
|
|
110
|
+
recordFailOpen(STORED_RESULT_FOLD_TAG, fields);
|
|
111
|
+
return;
|
|
112
|
+
default: {
|
|
113
|
+
const unhandled = at;
|
|
114
|
+
throw new Error(`unhandled fold sighting: ${String(unhandled)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
74
117
|
}
|
|
75
118
|
function failedCauseWithCode(t) {
|
|
76
119
|
if (t === null || typeof t !== "object")
|
package/dist/tool-approval.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export declare function approvalDecidePath(sessionId: string): string;
|
|
|
34
34
|
/**
|
|
35
35
|
* [ref] 随批小件:`tool_approval.not_pending` 404 的**判别位**([ref] 排障代价实证 —— 三因合并成一句人话,现场分不出「答案没地方兑」与「打错副本了」)。
|
|
36
36
|
* DECIDED/PARKED 已从 404 分流(DECIDED ⇒ 200 回放首决,回执带 `decidedBy`,**不是**成因;PARKED ⇒ 赎回席),残余两类:
|
|
37
|
-
* · 行 `VOID` / `DENIED`(没有人决过它)—— `cause` = **行上记的那个成因词**(问账 `
|
|
37
|
+
* · 行 `VOID` / `DENIED`(没有人决过它)—— `cause` = **行上记的那个成因词**(问账 `ending_cause` 列;与决断口 410 `gone.ask` 体的 `cause`、审计 settle 行
|
|
38
38
|
* 的 `cause` 同一个词,闭集 {@link SettleCause});行上没写归因(存量行)⇒ 缺席,不替没写的行编一个词;
|
|
39
39
|
* · {@link NOT_PENDING_UNKNOWN_CAUSE} —— 无行 / 行不是调用方的 / 行仍在别处活着:换副本或换端点重试才有意义(这个词只属于 404 面,不是结局成因)。
|
|
40
40
|
* 对齐 [ref] 反枚举语义:cause 只在**已过属主门**的面上出,不扩枚举面(属主门不过 = 与未知 id 同形)。
|
|
@@ -1666,7 +1666,8 @@ export declare class ToolApprovalCoordinator {
|
|
|
1666
1666
|
* ② 在 `deadlineMs` 之前等:未决卡结清、在飞腿闭合(闭了的腿照常落 `leg_closed`)、**本次路由到 park 的卡全部结清**
|
|
1667
1667
|
* (F1:观察到 core 已处置这次调用 —— 停车行提交或承载它的腿退场 ⇒ 那张卡的审计 settle 行此刻才写,见 {@link settleParksAwaitingCommit})、
|
|
1668
1668
|
* 店里在飞的 `put` 静默;
|
|
1669
|
-
* ③
|
|
1669
|
+
* ③ 等待结束(全结清或到期)⇒ 审计落**一行**进程级 `interrupted_by_shutdown` 记录([ref]:有序收尾走完是进程的事实,不是腿的 —— 此刻还没结清的卡
|
|
1670
|
+
* 不论在不在协调器的追踪视野里,下一次 boot 都写 `shutdown_before_park`;下一次 boot 收敛照样列出它们,decided 臂的 `resumeSafe:false` 提醒不丢);
|
|
1670
1671
|
* 仍未提交的 park ⇒ settle 行**不写**(账本上仍是 pending 孤儿 ⇒ 下一次 boot 与崩溃同路收敛,证据不丢),交回调用方
|
|
1671
1672
|
* (`uncommittedParks`,运维 warn `approval_park_uncommitted_at_shutdown` 由关停序落 —— 它握 logger)。之后才观察到的照样写 settle 行
|
|
1672
1673
|
* (观察到就是结清,迟到也是)。
|
|
@@ -1674,7 +1675,7 @@ export declare class ToolApprovalCoordinator {
|
|
|
1674
1675
|
* 的 await 里 —— 零活流的后台 / 委派子代也在内)在登记那一刻按同一条路收场(`askBroadcast` 的铸造审计点之后读这一位)。等待条件
|
|
1675
1676
|
* 因此数「在飞的 ask」({@link asksInFlight},`askBroadcast` 进出计数)而不只是 `pending` —— 尚未登记的卡不在 `pending` 里。
|
|
1676
1677
|
* `deadlineMs` 是绝对时刻(`Date.now()` 域):关停序对审批面与店 quiesce 读**同一个**到期点(一个宽限窗,不是两个)。
|
|
1677
|
-
* 从不抛错(关停序不许被审计面卡住);再调一次 = 再等一次(
|
|
1678
|
+
* 从不抛错(关停序不许被审计面卡住);再调一次 = 再等一次(关停记录一世一行,零重写)。
|
|
1678
1679
|
*/
|
|
1679
1680
|
drainForShutdown(deadlineMs: number): Promise<{
|
|
1680
1681
|
uncommittedParks: Array<{
|