@sema-agent/server 7.145.0 → 7.146.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 +492 -0
- package/USAGE.md +218 -47
- package/deploy/facts/CHANGES.json +25 -0
- package/deploy/facts/README.md +57 -0
- package/deploy/facts/lanes.json +54 -0
- package/deploy/facts/lanes.schema.json +57 -0
- package/deploy/facts/sandbox-rbac.json +113 -0
- package/deploy/facts/sandbox-rbac.schema.json +74 -0
- package/deploy/facts/sealed-key.json +9 -0
- package/deploy/facts/sealed-key.schema.json +52 -0
- package/deploy/sema-up/chart/templates/_helpers.tpl +19 -0
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +47 -7
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +4 -0
- package/deploy/sema-up/chart/templates/server.yaml +4 -0
- package/deploy/sema-up/chart/values.yaml +7 -0
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +30 -14
- package/dist/adoption/import-limits.d.ts +6 -0
- package/dist/adoption/import-limits.js +7 -0
- package/dist/adoption/import.d.ts +62 -0
- package/dist/adoption/import.js +107 -0
- package/dist/adoption/wire.d.ts +52 -0
- package/dist/adoption/wire.js +9 -0
- package/dist/approval-ask-machine.d.ts +2 -2
- package/dist/approval-card.d.ts +3 -12
- package/dist/approval-card.js +0 -4
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/approval-gate-currency.js +1 -1
- package/dist/approval-reconciler.d.ts +1 -1
- package/dist/boot/deployment-mods.d.ts +11 -1
- package/dist/boot/deployment-mods.js +15 -2
- package/dist/boot/device-lane.js +2 -0
- package/dist/boot/engine-lease.d.ts +193 -21
- package/dist/boot/engine-lease.js +146 -20
- package/dist/boot/execution-env.d.ts +4 -0
- package/dist/boot/execution-env.js +12 -0
- package/dist/boot/exit-boundary.d.ts +50 -0
- package/dist/boot/exit-boundary.js +32 -0
- package/dist/boot/exit-last.d.ts +20 -4
- package/dist/boot/exit-last.js +21 -4
- package/dist/boot/governance-seams.js +1 -0
- package/dist/boot/memory-consolidation.d.ts +29 -17
- package/dist/boot/memory-consolidation.js +26 -15
- package/dist/boot/reapers.d.ts +12 -4
- package/dist/boot/reapers.js +39 -15
- package/dist/boot/resolve-spec.d.ts +2 -0
- package/dist/boot/resolve-spec.js +38 -15
- package/dist/boot/runner-deps.js +1 -1
- package/dist/boot/session-faces.js +3 -1
- package/dist/boot/shutdown.js +27 -9
- package/dist/boot/stage-01-config.js +1 -1
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +3 -0
- package/dist/boot/stage-05-execution-env.js +3 -3
- package/dist/boot/stage-06-runners.d.ts +3 -0
- package/dist/boot/stage-06-runners.js +11 -10
- package/dist/boot/stage-07-capability-layer.d.ts +3 -0
- package/dist/boot/stage-07-capability-layer.js +7 -2
- package/dist/boot/stage-08-reapers.d.ts +3 -0
- package/dist/boot/stage-08-reapers.js +3 -3
- package/dist/boot/stage-09-leader.d.ts +3 -0
- package/dist/boot/stage-10-http-server.d.ts +3 -0
- package/dist/boot/stage-10-http-server.js +3 -2
- package/dist/boot/stores.d.ts +1 -0
- package/dist/boot/stores.js +35 -19
- package/dist/boot/workflow-orchestration.d.ts +1 -1
- package/dist/boot/workflow-orchestration.js +2 -1
- package/dist/capabilities/composite-members.d.ts +3 -1
- package/dist/capabilities/composite-members.js +3 -1
- package/dist/capabilities/scenarios.d.ts +2 -2
- package/dist/capability-facts.d.ts +18 -1
- package/dist/capability-facts.js +25 -7
- package/dist/checkpoint-ctx-seal-m28.d.ts +1 -1
- package/dist/config-catalog.js +13 -4
- package/dist/config-provider.d.ts +2 -2
- package/dist/config-types.d.ts +43 -4
- package/dist/config.d.ts +6 -1
- package/dist/config.js +67 -11
- package/dist/deleted-session-ids.d.ts +19 -0
- package/dist/deleted-session-ids.js +26 -0
- package/dist/deploy-facts.d.ts +155 -0
- package/dist/deploy-facts.js +94 -0
- package/dist/device-ws-hub.d.ts +48 -3
- package/dist/device-ws-hub.js +48 -2
- package/dist/error-code-family.d.ts +35 -0
- package/dist/error-code-family.js +26 -0
- package/dist/fleet/drain-entry.d.ts +39 -3
- package/dist/fleet/drain-entry.js +6 -0
- package/dist/fleet/fleet-bus.d.ts +2 -1
- package/dist/hooks/branch-transcript.d.ts +1 -1
- package/dist/hooks/branch-transcript.js +1 -0
- package/dist/hooks/classic-stations.d.ts +25 -8
- package/dist/hooks/classic-stations.js +29 -3
- package/dist/hooks/hook-runner.d.ts +33 -16
- package/dist/hooks/hook-runner.js +166 -73
- package/dist/hooks/hook-source.d.ts +14 -2
- package/dist/hooks/hook-source.js +8 -0
- package/dist/http/active-run-conflict.d.ts +1 -1
- package/dist/http/active-run-conflict.js +1 -1
- package/dist/http/admission.js +11 -1
- package/dist/http/checkpoint-ctx.d.ts +59 -71
- package/dist/http/checkpoint-ctx.js +34 -40
- package/dist/http/dispatch.js +18 -2
- package/dist/http/durable-run-leg.d.ts +5 -0
- package/dist/http/durable-run-leg.js +1 -0
- package/dist/http/execution-context.d.ts +4 -0
- package/dist/http/execution-context.js +9 -0
- package/dist/http/health-self-report.d.ts +6 -2
- package/dist/http/health-self-report.js +3 -2
- package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
- package/dist/http/memory-compliance-failure-reply.js +2 -15
- package/dist/http/memory-control-plane-corrupt-reply.d.ts +25 -0
- package/dist/http/memory-control-plane-corrupt-reply.js +19 -0
- package/dist/http/principal-gate.d.ts +35 -12
- package/dist/http/principal-gate.js +16 -12
- package/dist/http/request-guards.d.ts +1 -1
- package/dist/http/request-guards.js +5 -5
- package/dist/http/resume-legs.d.ts +22 -0
- package/dist/http/resume-legs.js +26 -17
- package/dist/http/route-ctx.d.ts +11 -10
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/adoption.d.ts +11 -0
- package/dist/http/routes/adoption.js +80 -6
- package/dist/http/routes/agents-roster.js +1 -0
- package/dist/http/routes/approvals-assistant.js +2 -1
- package/dist/http/routes/background.js +1 -0
- package/dist/http/routes/capabilities.js +4 -1
- package/dist/http/routes/diagnostics.d.ts +9 -14
- package/dist/http/routes/diagnostics.js +10 -20
- package/dist/http/routes/engine.d.ts +31 -0
- package/dist/http/routes/engine.js +81 -0
- package/dist/http/routes/memory-bundle.js +3 -0
- package/dist/http/routes/memory-consolidation.js +3 -5
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/memory-origin.js +2 -4
- package/dist/http/routes/memory-policy.js +5 -1
- package/dist/http/routes/observability.js +3 -6
- package/dist/http/routes/runs.js +7 -3
- package/dist/http/routes/session-sync.js +12 -25
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions.js +4 -0
- package/dist/http/routes/tasks.js +23 -5
- package/dist/http/routes/trace-usage.js +1 -1
- package/dist/http/run-concurrency-cap.d.ts +86 -0
- package/dist/http/run-concurrency-cap.js +54 -0
- package/dist/http/server.d.ts +15 -12
- package/dist/http/server.js +18 -3
- package/dist/http/stored-body-replay.d.ts +9 -6
- package/dist/http/stored-body-replay.js +19 -5
- package/dist/http/wire-types.d.ts +8 -3
- package/dist/json-body-limit.d.ts +8 -0
- package/dist/json-body-limit.js +2 -0
- package/dist/leader/diffout.d.ts +1 -1
- package/dist/local-data-root.d.ts +2 -2
- package/dist/memory-operator-faces.d.ts +9 -4
- package/dist/memory-scope-stores.d.ts +9 -7
- package/dist/memory-scope-stores.js +3 -14
- package/dist/memory-scope.d.ts +36 -0
- package/dist/memory-scope.js +32 -1
- package/dist/net-address.d.ts +53 -0
- package/dist/net-address.js +111 -0
- package/dist/observability/fail-open.d.ts +14 -10
- package/dist/observability/fail-open.js +14 -10
- package/dist/orchestration/workflow-notify-journal.js +2 -3
- package/dist/org-delivery-gate.js +1 -1
- package/dist/peer-directory.d.ts +6 -12
- package/dist/peer-directory.js +5 -1
- package/dist/plugins/caching-session-store.d.ts +3 -1
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-store-face.d.ts +426 -0
- package/dist/plugins/checkpoint-store-face.js +152 -0
- package/dist/plugins/file-run-store.d.ts +0 -6
- package/dist/plugins/file-run-store.js +1 -12
- package/dist/plugins/fork-routing-session-store.d.ts +6 -2
- package/dist/plugins/fork-routing-session-store.js +2 -2
- package/dist/plugins/imported-layer-root-retirement.d.ts +10 -8
- package/dist/plugins/local-checkpoint-store.d.ts +2 -2
- package/dist/plugins/local-checkpoint-store.js +2 -1
- package/dist/plugins/local-session-store.d.ts +28 -5
- package/dist/plugins/local-session-store.js +32 -8
- package/dist/plugins/memory-engine-pg.d.ts +29 -6
- package/dist/plugins/memory-engine-pg.js +96 -214
- package/dist/plugins/memory-engine-sql-apply.d.ts +121 -0
- package/dist/plugins/memory-engine-sql-apply.js +358 -0
- package/dist/plugins/memory-engine-tidb.d.ts +35 -9
- package/dist/plugins/memory-engine-tidb.js +97 -227
- package/dist/plugins/memory-key-guards.d.ts +1 -1
- package/dist/plugins/memory-origin-law.d.ts +3 -3
- package/dist/plugins/one-time-migrations.d.ts +32 -56
- package/dist/plugins/one-time-migrations.js +25 -117
- package/dist/plugins/permission-rule-store-file.d.ts +17 -3
- package/dist/plugins/permission-rule-store-file.js +8 -1
- package/dist/plugins/pg-pool.js +1 -1
- package/dist/plugins/pg-session-storage.d.ts +17 -4
- package/dist/plugins/pg-session-storage.js +8 -4
- package/dist/plugins/run-store-sql.js +2 -2
- package/dist/plugins/schema-ensure-legs.js +2 -2
- package/dist/plugins/session-fork.d.ts +31 -9
- package/dist/plugins/session-fork.js +3 -2
- package/dist/plugins/session-placement.d.ts +4 -2
- package/dist/plugins/sql-json-column.d.ts +5 -1
- package/dist/plugins/store-backend.d.ts +11 -8
- package/dist/plugins/store-backend.js +8 -7
- package/dist/plugins/store-contracts.d.ts +1 -11
- package/dist/plugins/store-contracts.js +0 -9
- package/dist/plugins/store-sweep.d.ts +63 -0
- package/dist/plugins/store-sweep.js +48 -0
- package/dist/plugins/tidb-pool.js +1 -1
- package/dist/plugins/tidb-session-store.d.ts +16 -3
- package/dist/plugins/tidb-session-store.js +8 -4
- package/dist/prompt-screen.d.ts +6 -9
- package/dist/prompt-screen.js +2 -5
- package/dist/rules-consent.d.ts +20 -27
- package/dist/rules-consent.js +82 -6
- package/dist/run-cancel-context.d.ts +4 -1
- package/dist/run-cancel-context.js +2 -1
- package/dist/runs.js +1 -1
- package/dist/sandbox-egress-policy.js +6 -11
- package/dist/sandbox-rbac-rules.d.ts +88 -0
- package/dist/sandbox-rbac-rules.js +80 -0
- package/dist/sealed-key-layout.d.ts +17 -0
- package/dist/sealed-key-layout.js +4 -0
- package/dist/sealed-key.js +5 -5
- package/dist/security.d.ts +38 -6
- package/dist/security.js +9 -2
- package/dist/session-notify-door.d.ts +5 -3
- package/dist/session-resident-background.d.ts +57 -0
- package/dist/session-resident-background.js +20 -0
- package/dist/session-sync-kernel.d.ts +53 -14
- package/dist/session-sync-kernel.js +70 -1
- package/dist/session-sync.d.ts +5 -3
- package/dist/session-workspace.d.ts +1 -0
- package/dist/session-workspace.js +1 -0
- package/dist/sighup-idle.d.ts +9 -7
- package/dist/sighup-idle.js +4 -5
- package/dist/spec-fields.d.ts +16 -0
- package/dist/spec-fields.js +7 -0
- package/dist/stored-request-env-credentials-m38.d.ts +3 -0
- package/dist/stored-request-env-credentials-m38.js +8 -0
- package/dist/stored-request-prompt-profile-m30.d.ts +1 -1
- package/dist/{plugins → stores-sql}/checkpoint-store-sql.d.ts +13 -427
- package/dist/{plugins → stores-sql}/checkpoint-store-sql.js +33 -194
- package/dist/{plugins → stores-sql}/mailbox-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/mailbox-store-sql.js +4 -4
- package/dist/{plugins → stores-sql}/roster-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/roster-store-sql.js +1 -1
- package/dist/{plugins → stores-sql}/tool-result-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/tool-result-store-sql.js +6 -6
- package/dist/{plugins → stores-sql}/workflow-journal-store-sql.d.ts +7 -12
- package/dist/{plugins → stores-sql}/workflow-journal-store-sql.js +3 -8
- package/dist/task-settings.d.ts +59 -4
- package/dist/task-settings.js +50 -4
- package/dist/tool-approval.d.ts +2 -2
- package/dist/trace/core-keyset-guard.d.ts +8 -7
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.d.ts +6 -0
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/project.js +9 -0
- package/dist/trace/redact.d.ts +13 -2
- package/dist/trace/redact.js +305 -24
- package/dist/trace/self-minted-wire.d.ts +326 -8
- package/dist/trace/self-minted-wire.js +125 -7
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/usage-analytics.d.ts +4 -0
- package/package.json +12 -4
- package/scripts/drain-check-local.mjs +8 -29
- package/scripts/drain-check.mysql.sql +3 -24
- package/scripts/drain-check.pg.sql +3 -24
- package/skills/update-config.md +1 -1
- package/wire-changes/server@7.146.0.json +3221 -0
- package/dist/approval-batch-census.d.ts +0 -27
- package/dist/approval-batch-census.js +0 -27
package/dist/device-ws-hub.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
+
import { isIP } from "node:net";
|
|
3
4
|
import { WebSocketServer } from "ws";
|
|
4
5
|
import { DEVICE_DRAINING_CODE, DEVICE_ERROR_CODES, DEVICE_ERROR_STATUS, assertDeviceIdShape, } from "./device-store.js";
|
|
5
6
|
import { MAX_TIMER_DELAY_MS } from "./config-types.js";
|
|
7
|
+
import { canonicalAddress, createTrustedProxySet } from "./net-address.js";
|
|
6
8
|
import { DEVICE_HELLO_ACK_BOUNDS, DEVICE_MIN_PROTOCOL_VERSION, DEVICE_WS_LIMITS, DEVICE_WS_PATH, DEVICE_PROTOCOL_VERSION, asInstructionKind, buildHelloSignaturePayload, deviceHelloAckNumberFlaw, formatGeneration, mintChallengeNonce, mintInstructionId, parseDeviceFrameText, verifyHelloSignature, } from "./device-ws-protocol.js";
|
|
7
9
|
const OUTCOME_UNKNOWN_SENTENCE = "outcome unknown — the command may have already executed on the device; verify its effect before re-running";
|
|
8
10
|
const SETTLED_MEMO_MAX = 512;
|
|
@@ -63,6 +65,41 @@ function assertHelloAckNumbersInBounds(values) {
|
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
}
|
|
68
|
+
export const DEVICE_PREAUTH_KEY_SOURCES = ["socket", "xff", "fallback"];
|
|
69
|
+
export const DEVICE_FORWARDED_UNUSABLE_REASONS = ["absent", "malformed", "all_trusted", "too_many_headers"];
|
|
70
|
+
export const DEVICE_PREAUTH_FORWARDED_HEADER_LINES_MAX = 100;
|
|
71
|
+
export function readPreAuthRemoteKey(peer, readForwardedFor, trusted) {
|
|
72
|
+
const raw = peer ?? "unknown";
|
|
73
|
+
const socketKey = canonicalAddress(raw) ?? raw;
|
|
74
|
+
if (!trusted.has(socketKey))
|
|
75
|
+
return { key: socketKey, source: "socket" };
|
|
76
|
+
const reading = readForwardedFor();
|
|
77
|
+
if (!reading.complete)
|
|
78
|
+
return { key: socketKey, source: "fallback", reason: "too_many_headers" };
|
|
79
|
+
const header = typeof reading.header === "string" ? reading.header : reading.header?.join(",");
|
|
80
|
+
if (header === undefined || header.replace(/[ \t]/g, "") === "")
|
|
81
|
+
return { key: socketKey, source: "fallback", reason: "absent" };
|
|
82
|
+
const hops = header.split(",");
|
|
83
|
+
for (let i = hops.length - 1; i >= 0; i--) {
|
|
84
|
+
const hop = forwardedHopAddress(hops[i]);
|
|
85
|
+
if (hop === undefined)
|
|
86
|
+
return { key: socketKey, source: "fallback", reason: "malformed" };
|
|
87
|
+
if (!trusted.has(hop))
|
|
88
|
+
return { key: hop, source: "xff" };
|
|
89
|
+
}
|
|
90
|
+
return { key: socketKey, source: "fallback", reason: "all_trusted" };
|
|
91
|
+
}
|
|
92
|
+
function forwardedHopAddress(hop) {
|
|
93
|
+
const t = hop.replace(/^[ \t]+|[ \t]+$/g, "");
|
|
94
|
+
if (t.startsWith("[")) {
|
|
95
|
+
const m = /^\[([^\]]*)\](?::\d{1,5})?$/.exec(t);
|
|
96
|
+
return m === null || isIP(m[1]) !== 6 ? undefined : canonicalAddress(m[1]);
|
|
97
|
+
}
|
|
98
|
+
const withPort = /^([^:]+):\d{1,5}$/.exec(t);
|
|
99
|
+
if (withPort !== null)
|
|
100
|
+
return isIP(withPort[1]) === 4 ? withPort[1] : undefined;
|
|
101
|
+
return canonicalAddress(t);
|
|
102
|
+
}
|
|
66
103
|
export function createDeviceWsHub(options) {
|
|
67
104
|
const { store, enrollment, replicaId, logger, metrics } = options;
|
|
68
105
|
const heartbeatIntervalMs = options.heartbeatIntervalMs ?? 15_000;
|
|
@@ -81,6 +118,7 @@ export function createDeviceWsHub(options) {
|
|
|
81
118
|
const chunkBufferMaxBytes = options.chunkBufferMaxBytes ?? 2 * 1024 * 1024;
|
|
82
119
|
const maxPreAuthConnections = options.maxPreAuthConnections ?? 64;
|
|
83
120
|
const maxPreAuthConnectionsPerRemote = options.maxPreAuthConnectionsPerRemote ?? 8;
|
|
121
|
+
const trustedProxies = createTrustedProxySet(options.trustedProxyCidrs ?? []);
|
|
84
122
|
const wss = new WebSocketServer({ noServer: true, maxPayload: DEVICE_WS_LIMITS.maxFrameBytes });
|
|
85
123
|
const conns = new Map();
|
|
86
124
|
const allConns = new Set();
|
|
@@ -757,6 +795,10 @@ export function createDeviceWsHub(options) {
|
|
|
757
795
|
}, DEVICE_WS_LIMITS.handshakeDeadlineMs);
|
|
758
796
|
sendServerFrame(conn, { t: "challenge", nonce: conn.nonce, protocolVersion: DEVICE_PROTOCOL_VERSION });
|
|
759
797
|
}
|
|
798
|
+
function forwardedHeaderLineCeiling(server) {
|
|
799
|
+
const cap = server.maxHeadersCount;
|
|
800
|
+
return typeof cap === "number" && cap > 0 ? Math.min(DEVICE_PREAUTH_FORWARDED_HEADER_LINES_MAX, cap) : DEVICE_PREAUTH_FORWARDED_HEADER_LINES_MAX;
|
|
801
|
+
}
|
|
760
802
|
function attach(server) {
|
|
761
803
|
server.on("upgrade", (req, socket, head) => {
|
|
762
804
|
const path = (req.url ?? "/").split("?")[0];
|
|
@@ -764,7 +806,11 @@ export function createDeviceWsHub(options) {
|
|
|
764
806
|
return refuseUpgrade(socket, 404);
|
|
765
807
|
if (draining || closed)
|
|
766
808
|
return refuseUpgrade(socket, 503);
|
|
767
|
-
const
|
|
809
|
+
const keyed = readPreAuthRemoteKey(req.socket.remoteAddress, () => ({ header: req.headers["x-forwarded-for"], complete: req.rawHeaders.length / 2 < forwardedHeaderLineCeiling(server) }), trustedProxies);
|
|
810
|
+
const remoteKey = keyed.key;
|
|
811
|
+
metrics?.inc("device_preauth_key_total", { source: keyed.source });
|
|
812
|
+
if (keyed.source === "fallback")
|
|
813
|
+
logger?.warn("device_preauth_forwarded_unusable", { reason: keyed.reason });
|
|
768
814
|
const perRemoteCount = preAuthByRemote.get(remoteKey) ?? 0;
|
|
769
815
|
const globalHit = preAuthCount >= maxPreAuthConnections;
|
|
770
816
|
const perRemoteHit = maxPreAuthConnectionsPerRemote !== "off" && perRemoteCount >= maxPreAuthConnectionsPerRemote;
|
|
@@ -772,7 +818,7 @@ export function createDeviceWsHub(options) {
|
|
|
772
818
|
const arm = globalHit ? (perRemoteHit ? "both" : "global") : "per_remote";
|
|
773
819
|
metrics?.inc("device_preauth_refused_total", { arm });
|
|
774
820
|
logger?.warn("device_preauth_cap_hit", {
|
|
775
|
-
remoteKey, arm, preAuthCount, cap: maxPreAuthConnections, perRemoteCount, perRemoteCap: maxPreAuthConnectionsPerRemote,
|
|
821
|
+
remoteKey, keySource: keyed.source, arm, preAuthCount, cap: maxPreAuthConnections, perRemoteCount, perRemoteCap: maxPreAuthConnectionsPerRemote,
|
|
776
822
|
});
|
|
777
823
|
return refuseUpgrade(socket, 503);
|
|
778
824
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 错误码目录的**码族**列(契约附录 A.1g;目录每一行的第二格)的闭集与读法 —— 纯数据 + 穷举 switch,无 import。
|
|
3
|
+
*
|
|
4
|
+
* ## 它是什么
|
|
5
|
+
* 目录(附录 A.1 / A.1b / A.1h)的每个码恰属一族 —— 族写在目录行的**第二格**(紧跟在码后面)。族回答的是消费端要做的那一个决定:**同一个请求原样再发一次,有没有意义**,以及出路在哪一侧。
|
|
6
|
+
* 它与码名前缀的「前缀族」(`auth.` / `request.` / … ,粗分支面,按**码的出身**分)是两根轴:前缀说这个码从哪来,码族说拿到它之后重发有没有用。
|
|
7
|
+
*
|
|
8
|
+
* · `transient` —— 原样重发(不改请求、不改部署、不改数据,谁也不必动一手)等一等**可能成功**:限流 / 配额窗 / 排空 / 店暂时不可用 / 竞态窗 / 在飞的活干完了自会放行(会话上的活跃 run、在跑的 bake)/ 未判为确定性的服务端故障。
|
|
9
|
+
* · `deterministic` —— 原样重发**改不了判**:要变的是请求本身、凭据与权限、部署声明与旋钮、存储里坏掉的字节,或一个已被别人结清的状态。
|
|
10
|
+
* · `store_judged` —— 重试类**逐次由店判**:体键 `permanence`(`transient` / `terminal`)在场 ⇔ 店判了;缺席 = 店没判(按 `transient` 读)。
|
|
11
|
+
* · `pending_decision` —— 恢复面 / 决断面的拒,拒在认领之前:体 `retriable: true` 在场 ⇒ 一张待批卡仍 pending(行重新 park);键缺席不推出「卡已不在」(服务端自铸的拒如 `wake.gate_pending` 不带该键)。
|
|
12
|
+
* 它说「卡还在」,**不说**「原样重发会成功」。
|
|
13
|
+
* · `retention_expired` —— 被寻址的留存物曾在、已被保留策略收走(TTL / 容量 / 淘汰),不会回来;出路是新开,不是重试。
|
|
14
|
+
*
|
|
15
|
+
* ## 单一真源
|
|
16
|
+
* 码 → 族的归属**只住在契约文档的目录行上**(消费端读的就是那份文档与它的 wire 变更清单);本文件只定**词表**与每个词的**状态律**,
|
|
17
|
+
* 词表与文档 A.1g 的词表行由 `test/error-code-family.test.ts` 双向钉(多一个词 / 少一个词 / 写错一个词都红),目录每个码有族、族与状态对得上、`pending_decision` 与
|
|
18
|
+
* 服务端真能盖 `retriable:true` 的码集对得上也在那里钉。没有第二张「码 → 族」的表。
|
|
19
|
+
*/
|
|
20
|
+
/** 码族闭集(词表顺序 = 契约 A.1g 的行序)。 */
|
|
21
|
+
export declare const ERROR_CODE_FAMILIES: readonly ["transient", "deterministic", "store_judged", "pending_decision", "retention_expired"];
|
|
22
|
+
export type ErrorCodeFamily = (typeof ERROR_CODE_FAMILIES)[number];
|
|
23
|
+
/** 一个串是不是码族词(`includes` 判,不吃原型链上的名字)。 */
|
|
24
|
+
export declare function isErrorCodeFamily(word: string): word is ErrorCodeFamily;
|
|
25
|
+
/**
|
|
26
|
+
* 一个状态码落在这一族是否说得通 —— 每个词一条状态律(穷举 switch:加词而没来这里定它的状态律 ⇒ tsc 红)。
|
|
27
|
+
*
|
|
28
|
+
* · `transient` —— 等待 / 可用性类状态:408 · 425 · 429,服务端侧 500 · 502 · 503 · 504,以及「等一个明说的窗再发」的 409(忙 / 竞态);501 不是(换部署形态不是等);
|
|
29
|
+
* · `deterministic` —— 除了等待类三态(408 · 425 · 429:这三个状态本身就在说「等一等再来」)之外的任何状态;
|
|
30
|
+
* · `store_judged` —— 店答的状态:422(店的重放失败)与服务端侧 500 · 502 · 503 · 504;
|
|
31
|
+
* · `pending_decision` —— 只在 4xx(拒在认领之前,调用方可以带着更新后的坐标再决),且不是等待类三态;
|
|
32
|
+
* · `retention_expired` —— 只在 4xx(被寻址的东西不在了),且不是等待类三态。
|
|
33
|
+
*/
|
|
34
|
+
export declare function familyAdmitsStatus(family: ErrorCodeFamily, status: number): boolean;
|
|
35
|
+
//# sourceMappingURL=error-code-family.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const ERROR_CODE_FAMILIES = ["transient", "deterministic", "store_judged", "pending_decision", "retention_expired"];
|
|
2
|
+
export function isErrorCodeFamily(word) {
|
|
3
|
+
return ERROR_CODE_FAMILIES.includes(word);
|
|
4
|
+
}
|
|
5
|
+
export function familyAdmitsStatus(family, status) {
|
|
6
|
+
const waiting = status === 408 || status === 425 || status === 429;
|
|
7
|
+
const clientSide = status >= 400 && status < 500 && !waiting;
|
|
8
|
+
const serverSide = status === 500 || status === 502 || status === 503 || status === 504;
|
|
9
|
+
switch (family) {
|
|
10
|
+
case "transient":
|
|
11
|
+
return waiting || serverSide || status === 409;
|
|
12
|
+
case "deterministic":
|
|
13
|
+
return status >= 400 && status < 600 && !waiting;
|
|
14
|
+
case "store_judged":
|
|
15
|
+
return serverSide || status === 422;
|
|
16
|
+
case "pending_decision":
|
|
17
|
+
return clientSide;
|
|
18
|
+
case "retention_expired":
|
|
19
|
+
return clientSide;
|
|
20
|
+
default: {
|
|
21
|
+
const unhandled = family;
|
|
22
|
+
throw new Error(`unhandled error-code family: ${String(unhandled)}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=error-code-family.js.map
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* 纯类型 + 纯函数叶(值 import 只有 scope 读法叶与 run 行判词叶)。
|
|
31
31
|
*/
|
|
32
32
|
import type { DrainReceipt, DrainRequest, EngineDraining } from "@sema-agent/settings-schema/fleet";
|
|
33
|
-
import type { EngineLeaseFacts } from "../boot/engine-lease.js";
|
|
33
|
+
import type { EngineHandoverDoor, EngineLeaseFacts } from "../boot/engine-lease.js";
|
|
34
34
|
/**
|
|
35
35
|
* 引擎自铸的动作号 —— **一张表**(排空事实与回执里不来自请求体的全部拼法;请求体的 `actionId` 由调用方自取):
|
|
36
36
|
* · {@link ENGINE_DRAIN_CAUSES}:没有请求承载的排空成因(停止信号 / SIGHUP 升级 / 附着租约自退);
|
|
@@ -68,6 +68,29 @@ export interface DrainDeadlineRefusal {
|
|
|
68
68
|
}
|
|
69
69
|
/** 入口的答:契约回执(进入 / 已在排空),或期限越界的拒(只在调用方声明了上界时出现)。按 `state` 穷举。 */
|
|
70
70
|
export type DrainEntryAnswer = DrainReceipt | DrainDeadlineRefusal;
|
|
71
|
+
/**
|
|
72
|
+
* 在飞数的**分项**(`GET /health` 的 `inflightBreakdown`)—— 同一份分项派生**两个事实**,不是两份计数:
|
|
73
|
+
* · **总数**(可观测事实,{@link inflightTotal} = `/health.inflight`):这台引擎此刻托管着多少活 = `http + leader + sessionResident`;
|
|
74
|
+
* · **阻断集**(判据,{@link blockingInflightOf} = `drainState.blockingInflight`):还有没有**必须等完**的活 = `http + leader`。
|
|
75
|
+
* 排空等的、孤儿窗 / 附着租约自退 / SIGHUP 空闲窗判「可以停了吗」读它。`sessionResident` 不在其内。
|
|
76
|
+
* 三项:
|
|
77
|
+
* · `http` —— 本副本的 HTTP 腿:在飞 run 集(`inflightRuns ∪ steerableRuns`)+ 没有登记身份的同步计费腿(`/v1/tasks` 的同步腿)+ admission 期的非探针请求;
|
|
78
|
+
* · `leader` —— leader 口的后台 run(`leaderEndpoint.inflight()`);
|
|
79
|
+
* · `sessionResident` —— 会话驻留的后台 shell / monitor(活过起它的那一条腿;`session-resident-background.ts` 的读法:不含 retain 行)。
|
|
80
|
+
* 它只进总数、不进阻断集:一只不退出的驻留 shell(`while true`)若阻断,崩溃孤儿引擎就永生、排空每次等满窗;窗到 / 排空完成即由退出边界的后台收割收掉
|
|
81
|
+
* (retain 行按 retain 既有语义,活过受控退)。
|
|
82
|
+
* 三项在同一次读里求出,`/health` 的 `inflight` 与 `inflightBreakdown` 因此永远自洽。只有这两个函数各写一次这两个和,没有第三张计法表。
|
|
83
|
+
* 阻断集只需要前两项:判停路径(排空 1 s 拍 / 租约拍 / SIGHUP 拍)**不求值** `sessionResident`(那是对进程级注册表的一次逐会话走查,只在 `/health` 与崩溃遗言里付)。
|
|
84
|
+
*/
|
|
85
|
+
export interface InflightBreakdown {
|
|
86
|
+
readonly http: number;
|
|
87
|
+
readonly leader: number;
|
|
88
|
+
readonly sessionResident: number;
|
|
89
|
+
}
|
|
90
|
+
/** 阻断集 = 必须等完的活 = `drainState.blockingInflight()`(`sessionResident` 不在内)。 */
|
|
91
|
+
export declare function blockingInflightOf(b: Pick<InflightBreakdown, "http" | "leader">): number;
|
|
92
|
+
/** 总数 = 引擎此刻托管的全部活 = `/health.inflight`(= 阻断集 + 会话驻留项)。 */
|
|
93
|
+
export declare function inflightTotal(b: InflightBreakdown): number;
|
|
71
94
|
/**
|
|
72
95
|
* 引擎与 HTTP 面共享的排空盒子(装配层建一只,`createHttpServer` 与收尾链各自往上装读口 / 入口)。
|
|
73
96
|
* · `draining` / `since` / `actionId` / `deadlineAt` —— **只由** {@link enter} 整组写入(排空事实 = 契约 `EngineDraining`);
|
|
@@ -75,8 +98,8 @@ export type DrainEntryAnswer = DrainReceipt | DrainDeadlineRefusal;
|
|
|
75
98
|
* · `yieldInflight` —— 让出本副本在飞的可抢占腿(`createHttpServer` 装);
|
|
76
99
|
* · `endSubscriptions` —— 关掉本副本打开着的只读订阅流(发一帧 `draining` 终帧再关;`createHttpServer` 装,[ref])。订阅流不是在飞的工作
|
|
77
100
|
* (不进 `inflight`),排空不等它;收尾链在排空进入那一拍与硬停起点各调一次(排空期间新开的订阅在硬停那一拍收);
|
|
78
|
-
* ·
|
|
79
|
-
* admission 期的普通请求),只读订阅只进 `attachedStreams`([ref])。
|
|
101
|
+
* · 其余五只是既有的读口(在飞数 / 活性时钟两只〔最近一次 / 首次〕/ 附着流两只),语义见 `http/server.ts`。在飞数只数**工作**(run 腿 / 计费提交 / HITL 应答 /
|
|
102
|
+
* admission 期的普通请求),只读订阅只进 `attachedStreams`([ref]);在飞数分两个读口 —— 总数 `inflight`(事实)与阻断集 `blockingInflight`(判据),见 {@link InflightBreakdown}。
|
|
80
103
|
*/
|
|
81
104
|
export interface DrainState {
|
|
82
105
|
draining: boolean;
|
|
@@ -86,12 +109,25 @@ export interface DrainState {
|
|
|
86
109
|
enter?: (entry: DrainEntry) => DrainEntryAnswer;
|
|
87
110
|
yieldInflight?: () => void;
|
|
88
111
|
endSubscriptions?: () => void;
|
|
112
|
+
/** **总数**读口(`createHttpServer` 装):引擎此刻托管的全部活 = `inflightTotal(inflightBreakdown())`。可观测事实 —— 只有 `/health` 与崩溃遗言读它;
|
|
113
|
+
* **没有任何「停不停」的判据读它**(判据读 {@link DrainState.blockingInflight})。 */
|
|
89
114
|
inflight?: () => number;
|
|
115
|
+
/** 分项读口(`createHttpServer` 装,与 `inflight` / `blockingInflight` 同一次装、同一份求值)。`/health` 读它一次,同时出 `inflight` 与 `inflightBreakdown` 两键。 */
|
|
116
|
+
inflightBreakdown?: () => InflightBreakdown;
|
|
117
|
+
/** **阻断集**读口(`createHttpServer` 装):还有没有必须等完的活 = `blockingInflightOf(inflightBreakdown())` = `http + leader`。排空等的(`boot/shutdown.ts` 的排空驱动表)、
|
|
118
|
+
* 孤儿窗 / 附着租约自退(`boot/engine-lease.ts`)、SIGHUP 空闲窗(`sighup-idle.ts`)的「可以停了吗」都读它;会话驻留的后台 shell / monitor 不在内。 */
|
|
119
|
+
blockingInflight?: () => number;
|
|
90
120
|
lastActivityAt?: () => number;
|
|
121
|
+
/** [ref]:首个**非探针**请求的单调时刻(`null` = 还没有过)。与 `lastActivityAt` 同一个写者(`http/server.ts` 的活性打点)、写一次就定;
|
|
122
|
+
* 读者 = 租约腿的候首触态(`boot/engine-lease.ts`)与 `/health.engineLease.firstAttach.attachedAt`。 */
|
|
123
|
+
firstActivityAt?: () => number | null;
|
|
91
124
|
attachedStreams?: () => number;
|
|
92
125
|
lastAttachEndedAt?: () => number;
|
|
93
126
|
/** [ref]:附着租约窗的读口(`/health.engineLease`)。收尾链在**装了租约腿**时才装它,读口现读租约腿的把手 —— 腿还在观察才有读数,未装配的盒子里根本没有这只口。 */
|
|
94
127
|
engineLease?: () => EngineLeaseFacts | undefined;
|
|
128
|
+
/** [ref]:附着租约的交接门(`POST /v1/engine/handover` · `POST /v1/engine/attach` 与能力位 `engine.handover` 读它)。收尾链在**装了交接车道**时才装
|
|
129
|
+
* (租约腿已装配 ∧ 非多租户);`observing` = 腿还在观察。 */
|
|
130
|
+
engineHandover?: EngineHandoverDoor;
|
|
95
131
|
}
|
|
96
132
|
/** 排空盒子里的排空事实(三键齐才是;入口整组写,所以「缺一」只出现在手搭的夹具上)。 */
|
|
97
133
|
export declare function drainingFactOf(s: Pick<DrainState, "draining" | "since" | "actionId" | "deadlineAt">): EngineDraining | undefined;
|
|
@@ -10,6 +10,12 @@ export const DRAIN_CAUSE_COUNTS_AS_FIRST_STOP_SIGNAL = {
|
|
|
10
10
|
"signal:SIGHUP": true,
|
|
11
11
|
"self-exit:attachment": false,
|
|
12
12
|
};
|
|
13
|
+
export function blockingInflightOf(b) {
|
|
14
|
+
return b.http + b.leader;
|
|
15
|
+
}
|
|
16
|
+
export function inflightTotal(b) {
|
|
17
|
+
return blockingInflightOf(b) + b.sessionResident;
|
|
18
|
+
}
|
|
13
19
|
export function drainingFactOf(s) {
|
|
14
20
|
return s.draining && s.actionId !== undefined && s.since !== undefined && s.deadlineAt !== undefined ? { actionId: s.actionId, since: s.since, deadlineAt: s.deadlineAt } : undefined;
|
|
15
21
|
}
|
|
@@ -396,7 +396,8 @@ export interface HookManagedUnrun {
|
|
|
396
396
|
* · `llm_carrier_unavailable` / `llm_cap` —— prompt / agent 条目没有模型载体 / 超出每事件的 LLM 条目数;
|
|
397
397
|
* · `powershell_unsupported` —— `shell:"powershell"` 条目(本服务不跑);
|
|
398
398
|
* · `if_unevaluable` —— 条目的 `if` 条件评不出;
|
|
399
|
-
* · `budget_exhausted` ——
|
|
399
|
+
* · `budget_exhausted` —— 托管车道的预算耗尽:本服务的事件墙钟到点(不再起下一条),或托管车道那张注册的**引擎 handler 预算**(`HOOKS_TIMEOUT_MS`)用尽、被引擎叫停(`detail` 点名这一种);
|
|
400
|
+
* 撞上的那一条记一帧,同一次执行里其后的条目不再起、不逐条记(墙钟:每一相、PreToolUse 每一组至多一帧;引擎预算:每个事件恰一帧);任务取消叫停不记;
|
|
400
401
|
* · `spawn_failed` / `timeout` —— 起不来 / 超时被收;
|
|
401
402
|
* · `exit_nonzero` —— 退出码非 0 非 2;`bad_json` —— 看着是 JSON 却读不成(含 http 条目 2xx 回了非 JSON 体、Stop × prompt 的判词读不出);`decision_out_of_set` —— `permissionDecision` 不在 allow / deny / ask 里;
|
|
402
403
|
* · `evidence_unavailable` —— Stop × prompt 条目因会话证据面不足(或运维关了 transcript)被滤掉没跑;
|
|
@@ -19,7 +19,7 @@ export declare const RENDERED_ENTRY_TYPES: readonly ["message", "compaction", "c
|
|
|
19
19
|
* custom —— data-only(session.js 折 messages 只认 message/custom_message 两型),模型看不见它的
|
|
20
20
|
* data;把它渲出来反而是把模型没见过的东西冒充成证据,与 [ref] 是同一判据的反方向。
|
|
21
21
|
*/
|
|
22
|
-
export declare const SKIPPED_ENTRY_TYPES: readonly ["thinking_level_change", "model_change", "label", "session_info", "leaf", "prompt_epoch", "announced_listing", "workspace_state", "git_announcement", "reminder_mark", "custom"];
|
|
22
|
+
export declare const SKIPPED_ENTRY_TYPES: readonly ["thinking_level_change", "model_change", "label", "session_info", "leaf", "prompt_epoch", "announced_listing", "workspace_state", "git_announcement", "reminder_mark", "session_checkout", "custom"];
|
|
23
23
|
/** 渲染结果。**判别式**:证据不足时调用方拿不到 `text`,想用也用不了(见顶注)。 */
|
|
24
24
|
export type BranchTranscript = {
|
|
25
25
|
sufficient: true;
|
|
@@ -7,8 +7,11 @@
|
|
|
7
7
|
* ② **handler 怎么挂**:链成员「无意见 ⇒ `next(e)`」(链 handler 返回 `undefined` 是站点违约,refuse 站上即 fail-closed 拒),
|
|
8
8
|
* 观察者「恒返回 `undefined`」(观察者的判词不采纳、被引擎报告)。两条都在 {@link hangClassic} / {@link observeClassic} 里成形,
|
|
9
9
|
* 调用方只写「这一站我答什么」或「这一站我看什么」,不再各自记得这两条。
|
|
10
|
-
* ③
|
|
11
|
-
*
|
|
10
|
+
* ③ **停表**:引擎交给每只 handler 的 `next.signal` 是「这只 handler 的预算 ∪ 这次派发结束 / 被取消」的合成(core 的派发器自合成,本仓不另合成第二份);
|
|
11
|
+
* 成因只读引擎的读数 `next.budget.remainingMs`(用尽 = 0),不解析信号的文案。两样一起是 handler 体的第二个入参 {@link HandlerHalt} ——
|
|
12
|
+
* 触发之后 handler 不再起新事、在飞的子进程要叫停(`hooks/hook-runner.ts` 的命令钩子腿);是自己的预算用尽,托管车道把撞上的条目记成未跑成。
|
|
13
|
+
* ④ **两种链上挂法**:内层「先答,无意见再 `next(e)`」({@link hangClassic});外层「先 `next(e)`,在答回来的答案上再答」({@link wrapClassic})——
|
|
14
|
+
* 外层 handler 等 `next` 的那段引擎停它的表(core `SelfBudget` 的暂停),所以外层的预算只算它自己的时间。
|
|
12
15
|
*
|
|
13
16
|
* 站的集合 = 引擎事件表里有站的 `classic.*` 名(一条钉从 `MOD_EVENT_TABLE` 派生对拍,不手抄第二份)。
|
|
14
17
|
* 观察站(`StopFailure` / `PostCompact` / `PermissionDenied` / `SessionStart` / `SessionEnd`)只许观察者:答案形是「无」,core 的迁移配方要求挂 `{ observe: true }`。
|
|
@@ -36,16 +39,30 @@ export declare const CLASSIC_STATION_ROLE: {
|
|
|
36
39
|
/** 本仓 handler 体对一站作的答:观察站不作答(`void`);链站 = core 的答案形,去掉本仓从不作的 `null` / `false`(PreToolUse 的两个「不表态」词)。 */
|
|
37
40
|
type StationAnswer<K extends ClassicStation> = (typeof CLASSIC_STATION_ROLE)[K] extends "observe" ? void : Exclude<ClassicEventMap[K]["answer"], null | false>;
|
|
38
41
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
42
|
+
* 引擎交给一只 handler 的停表(头注 ③):`signal` 触发 = 引擎不再等它;`budgetSpent()` = 触发的是不是它**自己的预算**用尽(派发结束 / 任务取消 ⇒ `false`;
|
|
43
|
+
* 信号还没触发 ⇒ `false`)。
|
|
41
44
|
*/
|
|
42
|
-
export
|
|
45
|
+
export interface HandlerHalt {
|
|
46
|
+
readonly signal: AbortSignal;
|
|
47
|
+
budgetSpent(): boolean;
|
|
48
|
+
}
|
|
43
49
|
/**
|
|
44
|
-
*
|
|
50
|
+
* 一站的 handler 体:读 `e`,答这一站的答案;`undefined` = 无意见。`halt` 见 {@link HandlerHalt}。
|
|
51
|
+
* `below` = 外层挂法下(见 {@link wrapClassic})链下方答回来的那份答案(接收后冻结的数据;下方都无意见时是引擎核心的空答案)—— 内层挂法与观察站恒 `undefined`。
|
|
52
|
+
* 外层体答 `undefined` = 下方的答案原样作数;要换掉它就交一份新答案(把下方的成员并进去是体自己的事)。
|
|
53
|
+
*/
|
|
54
|
+
export type ClassicAnswer<K extends ClassicStation> = (e: ClassicEventMap[K]["e"], halt: HandlerHalt, below: unknown) => Promise<StationAnswer<K> | undefined>;
|
|
55
|
+
/**
|
|
56
|
+
* 内层挂法:链站 ⇒ 链成员 `(await answer(e)) ?? next(e)`;观察站 ⇒ 观察者(体照跑、恒返回 `undefined`)。
|
|
45
57
|
* `e` 的型由 core 的 `ClassicEventMap` 给(引擎交来的是本站的 `e`;见头注 ①)。
|
|
46
58
|
*/
|
|
47
59
|
export declare function hangClassic<K extends ClassicStation>(on: On<EngineDollar>, station: K, answer: ClassicAnswer<K>): void;
|
|
48
|
-
/**
|
|
49
|
-
|
|
60
|
+
/**
|
|
61
|
+
* 外层挂法:链站 ⇒ 先 `next(e)`(下方的链成员与引擎核心先跑完,这一只的表在等的那段停着),再拿答回来的答案作答 —— 体答 `undefined` ⇒ 下方的答案原样作数;
|
|
62
|
+
* 下方在拒站上失败 ⇒ 派发已 fail-closed 终局,`next` 抛,体不跑。观察站 ⇒ 与 {@link hangClassic} 同(观察者不进链,没有「下方」)。
|
|
63
|
+
*/
|
|
64
|
+
export declare function wrapClassic<K extends ClassicStation>(on: On<EngineDollar>, station: K, answer: ClassicAnswer<K>): void;
|
|
65
|
+
/** 挂一只观察者(任何站都可):拿分离副本、不进链、恒返回 `undefined`;`halt` 同 {@link ClassicAnswer}。 */
|
|
66
|
+
export declare function observeClassic<K extends ClassicStation>(on: On<EngineDollar>, station: K, watch: (e: ClassicEventMap[K]["e"], halt: HandlerHalt) => void | Promise<void>): void;
|
|
50
67
|
export {};
|
|
51
68
|
//# sourceMappingURL=classic-stations.d.ts.map
|
|
@@ -14,16 +14,42 @@ export const CLASSIC_STATION_ROLE = {
|
|
|
14
14
|
"classic.SessionStart": "observe",
|
|
15
15
|
"classic.SessionEnd": "observe",
|
|
16
16
|
};
|
|
17
|
+
async function withHalt(next, body) {
|
|
18
|
+
const { signal } = next;
|
|
19
|
+
const readCause = () => next.budget.remainingMs === 0;
|
|
20
|
+
let spent = signal.aborted ? readCause() : undefined;
|
|
21
|
+
const latch = () => {
|
|
22
|
+
spent = readCause();
|
|
23
|
+
};
|
|
24
|
+
if (spent === undefined)
|
|
25
|
+
signal.addEventListener("abort", latch, { once: true });
|
|
26
|
+
try {
|
|
27
|
+
return await body({ signal, budgetSpent: () => spent === true });
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
signal.removeEventListener("abort", latch);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
17
33
|
export function hangClassic(on, station, answer) {
|
|
18
34
|
if (CLASSIC_STATION_ROLE[station] === "observe") {
|
|
19
|
-
observeClassic(on, station, async (e,
|
|
35
|
+
observeClassic(on, station, async (e, halt) => void (await answer(e, halt, undefined)));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
on(station, async (_$, e, next) => (await withHalt(next, (halt) => answer(e, halt, undefined))) ?? next(e));
|
|
39
|
+
}
|
|
40
|
+
export function wrapClassic(on, station, answer) {
|
|
41
|
+
if (CLASSIC_STATION_ROLE[station] === "observe") {
|
|
42
|
+
hangClassic(on, station, answer);
|
|
20
43
|
return;
|
|
21
44
|
}
|
|
22
|
-
on(station, async (_$, e, next) =>
|
|
45
|
+
on(station, async (_$, e, next) => {
|
|
46
|
+
const below = await next(e);
|
|
47
|
+
return (await withHalt(next, (halt) => answer(e, halt, below))) ?? below;
|
|
48
|
+
});
|
|
23
49
|
}
|
|
24
50
|
export function observeClassic(on, station, watch) {
|
|
25
51
|
on(station, async (_$, e, next) => {
|
|
26
|
-
await watch(e,
|
|
52
|
+
await withHalt(next, async (halt) => watch(e, halt));
|
|
27
53
|
return undefined;
|
|
28
54
|
}, { observe: true });
|
|
29
55
|
}
|
|
@@ -57,20 +57,27 @@ export declare const SETTINGS_HOOK_STATIONS: {
|
|
|
57
57
|
readonly PreCompact: readonly ["classic.PreCompact"];
|
|
58
58
|
readonly PostCompact: readonly ["classic.PostCompact"];
|
|
59
59
|
};
|
|
60
|
-
/** 这张注册的名字 —— wire 可见(`wiring_manifest.mods[].name`、`allowRuleShadowed[].entries`),也是 handler 失败时引擎拒句点名的扩展名。 */
|
|
61
|
-
export declare const SETTINGS_HOOKS_REGISTRATION = "settings.hooks";
|
|
62
60
|
/**
|
|
63
|
-
*
|
|
61
|
+
* **一条车道一张注册**:每张的名字 —— wire 可见(`wiring_manifest.mods[].name`、`allowRuleShadowed[].entries`、问因项 `askReasons[].registration`),
|
|
62
|
+
* 也是 handler 失败时引擎拒句点名的扩展名。没有托管组的请求只铸非托管那一张(名字与托管车道之前逐字同)。
|
|
63
|
+
*/
|
|
64
|
+
export declare const SETTINGS_HOOKS_REGISTRATIONS: {
|
|
65
|
+
readonly managed: "settings.hooks.managed";
|
|
66
|
+
readonly unmanaged: "settings.hooks";
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* 律四 · `HOOKS_TIMEOUT_MS` 未设时**每张车道注册**每个 handler 的预算(ms;两张注册各一份同值,不另铸数)。
|
|
64
70
|
*
|
|
65
71
|
* **这只数是引擎的钟,不是本服务跑钩子子进程的墙钟。** 引擎对 `budgetMs` 的定义(core `ModRegistration.budgetMs` 的注释,逐字)是
|
|
66
72
|
* "Each handler's own time" —— 每个 handler 自己的时间:一只在 handler 等 `next` / `$` 调用时暂停的钟(core 的 `SelfBudget`),
|
|
67
|
-
* 到点 ⇒ 这个 handler 失败(PreToolUse / UserPromptSubmit 上 fail-closed 拒,其余站跳过)
|
|
68
|
-
*
|
|
73
|
+
* 到点 ⇒ 这个 handler 失败(PreToolUse / UserPromptSubmit 上 fail-closed 拒,其余站跳过)。托管车道是外层、先等 `next`:它的表在非托管车道跑的那段停着 ——
|
|
74
|
+
* 非托管条目吃光的只是非托管那张的预算,托管车道照跑;托管车道自己的预算用尽 ⇒ 撞上的托管条目记未跑成(`budget_exhausted`)。命令钩子子进程自己的墙钟另有两只,都在本文件:
|
|
75
|
+
* 单条命令的 `timeout`(上限 {@link MAX_HOOK_TIMEOUT_SECONDS})与一个事件里一条车道同步条目的总墙钟({@link MAX_HOOK_EVENT_TOTAL_SECONDS},到点不再起下一条)。
|
|
69
76
|
*
|
|
70
77
|
* 缺省取单条命令超时的上限 {@link MAX_HOOK_TIMEOUT_SECONDS} × 1000(两只钟起点不同:引擎的钟在 handler 起跑时就开始走,命令的 `timeout` 钟要等 shell 解析和 spawn 之后,
|
|
71
78
|
* 同一 handler 里串行的几条还共用这一只钟 —— 配到 600 s 的命令跑满时预算会先到)。不是抄引擎缺省(10 s):一条跑 11 s 的 PreToolUse 钩子会因此被 fail-closed 拒。
|
|
72
79
|
*
|
|
73
|
-
* 两只钟的交汇:预算到,与任务被取消一样,引擎对这个 handler 的信号({@link
|
|
80
|
+
* 两只钟的交汇:预算到,与任务被取消一样,引擎对这个 handler 的信号({@link HandlerHalt} 的 `signal`)触发 ——
|
|
74
81
|
* 在跑的命令钩子的进程组被叫停(SIGTERM → {@link HOOK_KILL_GRACE_MS} → 组长还在则组 SIGKILL),这个 handler 里还没起的条目不再起。
|
|
75
82
|
*/
|
|
76
83
|
export declare const DEFAULT_SETTINGS_HOOKS_BUDGET_MS: number;
|
|
@@ -115,7 +122,7 @@ export interface RunnableMatcherGroup {
|
|
|
115
122
|
source?: HookGroupSource;
|
|
116
123
|
hooks: RunnableHookEntry[];
|
|
117
124
|
}
|
|
118
|
-
/** 受理面通过后的 hooks 配置:每个事件下的条目都是本仓会跑的型 —— 执行侧({@link
|
|
125
|
+
/** 受理面通过后的 hooks 配置:每个事件下的条目都是本仓会跑的型 —— 执行侧({@link createSettingsHooksRegistrations})只收这一型,「不认识的类型」到不了它手里。 */
|
|
119
126
|
export type RunnableHooksConfig = Partial<Record<HookEventName, RunnableMatcherGroup[]>>;
|
|
120
127
|
/** 分类器点名的一块(被剔的块或整份被拒的理由):码 + 坐标 + 坏字段。 */
|
|
121
128
|
export type HooksRefusalPiece = HooksDroppedPiece | HooksRejection;
|
|
@@ -485,7 +492,7 @@ export declare function hookMatcherMatches(matcher: string | undefined, value: s
|
|
|
485
492
|
* 「交替/正则」是 matcher 字段的文法——本实现名部走 hookMatcherMatches 属**超集**(alternation 也认),
|
|
486
493
|
* 如实登记不收窄;②250 的 `if` 已扩到 prompt/agent/http/mcp_tool 条目(本 runner 只翻 command,不涉)。 */
|
|
487
494
|
export declare function hookIfMatches(cond: string, payload: unknown): true | "not_matched" | "unevaluable" | "over_budget";
|
|
488
|
-
/**
|
|
495
|
+
/** createSettingsHooksRegistrations 的装配上下文(payload 的 CC-verbatim 字段来源)。 */
|
|
489
496
|
export interface HookRunnerContext {
|
|
490
497
|
logger: Logger;
|
|
491
498
|
sessionId: string;
|
|
@@ -520,6 +527,11 @@ export interface HookRunnerContext {
|
|
|
520
527
|
* `[]` = 没有层写名单(不限,与引擎外的参照实现「名单未设 ⇒ 不限」同读)。今天唯一的层来源是请求的 `settings`(`task-settings.ts` 的 `httpHookAllowlist`,源 `flagSettings`)。
|
|
521
528
|
*/
|
|
522
529
|
httpHookAllowlistLayers: readonly HttpHookAllowlistLayer[];
|
|
530
|
+
/**
|
|
531
|
+
* 部署座在**任务座之下**挂了链成员的站点式样(引擎的准入记录读出,`boot/deployment-mods.ts` 的 `belowTaskChainPatternsOf`)。必填:每个装配点都要说 ——
|
|
532
|
+
* `[]` = 任务座之下没有部署座的链成员(部署注册只在任务座之上、只有观察者,或没有部署注册)。读者 = {@link managedTightenOnlyOn}(托管车道在这些站上只许收紧)。
|
|
533
|
+
*/
|
|
534
|
+
belowTaskChainPatterns: readonly string[];
|
|
523
535
|
}
|
|
524
536
|
/** 阶段三b 模型调用载体契约(prompt=直连一次 completion;agent=runTask 收敛后的 final text)。
|
|
525
537
|
* 返回 ok/text 或 ok:false/error——载体自己兜超时(timeoutMs 是硬顶,载体可更严),绝不 throw。 */
|
|
@@ -655,20 +667,25 @@ export interface LlmHookExtra {
|
|
|
655
667
|
transcript?: string;
|
|
656
668
|
}
|
|
657
669
|
/**
|
|
658
|
-
* 律一 · 把校验过的 {@link RunnableHooksConfig}
|
|
659
|
-
*
|
|
670
|
+
* 律一 · 把校验过的 {@link RunnableHooksConfig} 铸成任务座上的 mods 注册 —— **一条车道一张**(车道 = `hook-source.ts` 的 {@link hookLaneOf}):
|
|
671
|
+
* 托管车道 `settings.hooks.managed`(托管插件相 + 托管策略相)在前、非托管车道 `settings.hooks` 在后({@link LANE_CHAIN_ORDER});没有组的车道不铸,两道都空 ⇒ `[]`(`TaskSpec.mods` 不铸)。
|
|
672
|
+
* 没有托管组的请求只得到非托管那一张,与分道之前逐字同(名字、挂站、预算、`once` 键)。`budgetMs` = {@link settingsHooksBudgetMs} 的读数(调用方交),两张各带一份同值。
|
|
673
|
+
*
|
|
674
|
+
* 一条规则:**车道是引擎的注册,不是一只 handler 里的分支** —— 每张注册自带引擎的 handler 预算、本服务的事件墙钟、LLM 条数、`once` 账与未跑成账本,两道之间不共用任何一只钟。
|
|
675
|
+
* 托管车道外层挂(`wrapClassic`):先把事件交下去(非托管车道、更下方的注册与引擎核心先答完,托管的表在这段停着),再在答回来的那份上跑自己的两相并折叠 ——
|
|
676
|
+
* 托管判的因此恒是要执行的那一份,非托管吃光自己的预算也挤不掉它(拒站上非托管失败 = 派发 fail-closed 终局;其余站引擎跳过非托管,托管照跑)。
|
|
660
677
|
*
|
|
661
678
|
* 构造约束(每一条都有引擎侧的硬理由):
|
|
662
679
|
* · `register` 同步、只读本注册自己的配置、不按腿分支 —— 返回 thenable 即 `config.mods_invalid`;委派子代把父的任务座当自己的派发,
|
|
663
680
|
* 两条腿挂的是同一套 handler,父链才塌进子代的 phase 1(否则多一次约束判定);
|
|
664
|
-
* · `once` 去重集({@link runMatchingEntries} 的 `onceFired`)
|
|
681
|
+
* · `once` 去重集({@link runMatchingEntries} 的 `onceFired`)在铸注册这一层、每请求每车道一份,不放进 `register`(那是每腿一次);
|
|
665
682
|
* · 答案全部由本仓从 CC JSON 铸,引擎在 handler 边界屏(唯一判官),本仓不预屏;
|
|
666
|
-
* ·
|
|
667
|
-
* 观察站的 handler 恒返回 `undefined` ——
|
|
668
|
-
* · `uses.calls: []`(必填;本仓的 handler 不调 `$`),`uses.events` 从 {@link SETTINGS_HOOK_STATIONS}
|
|
669
|
-
* ·
|
|
683
|
+
* · 内层无意见 ⇒ `next(e)`;外层无可添 ⇒ 下方的答案原样作数(链 handler 返回 `undefined` 是站点违约,PreToolUse / UserPromptSubmit 的失败列是 refuse ⇒ 每次都会被拒);
|
|
684
|
+
* 观察站的 handler 恒返回 `undefined` —— 三条都在 `classic-stations.ts` 的两种挂法里成形;
|
|
685
|
+
* · `uses.calls: []`(必填;本仓的 handler 不调 `$`),`uses.events` 从 {@link SETTINGS_HOOK_STATIONS} 派生(只挂本车道有组的事件);
|
|
686
|
+
* · 每只翻译的第二个入参是引擎的停表({@link HandlerHalt}:`next.signal` = 这只 handler 的预算 ∪ 这次派发结束 / 被取消,成因读 `next.budget`;见 {@link DEFAULT_SETTINGS_HOOKS_BUDGET_MS}):
|
|
670
687
|
* 一路贯穿到命令腿的 spawn({@link runCommandHook}),本仓不自合成第二只、不另设钟。
|
|
671
688
|
*/
|
|
672
|
-
export declare function
|
|
689
|
+
export declare function createSettingsHooksRegistrations(config: RunnableHooksConfig, ctx: HookRunnerContext, budgetMs: number): readonly ModRegistration<EngineDollar>[];
|
|
673
690
|
export {};
|
|
674
691
|
//# sourceMappingURL=hook-runner.d.ts.map
|