@sema-agent/server 7.148.0 → 7.150.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 +551 -9
- package/README.md +17 -2
- package/README.zh-CN.md +5 -1
- package/USAGE.md +126 -68
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +33 -33
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/approval-leg-origin.d.ts +33 -0
- package/dist/approval-leg-origin.js +4 -0
- package/dist/audit.d.ts +1 -1
- package/dist/audit.js +2 -2
- package/dist/auth-bridge.d.ts +9 -1
- package/dist/auth-bridge.js +4 -4
- package/dist/auth-keys.d.ts +10 -1
- package/dist/auth-keys.js +8 -0
- package/dist/background-session-reap.d.ts +1 -1
- package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
- package/dist/boot/deferred-sandbox-path-env.js +1 -2
- package/dist/boot/execution-env.js +10 -11
- package/dist/boot/execution-lane.d.ts +6 -4
- package/dist/boot/execution-lane.js +3 -1
- package/dist/boot/org-delivery.d.ts +3 -2
- package/dist/boot/parked-revive-gate.d.ts +5 -5
- package/dist/boot/parked-revive-gate.js +2 -3
- package/dist/boot/resolve-spec.d.ts +4 -5
- package/dist/boot/resolve-spec.js +66 -28
- package/dist/boot/retention-lane.d.ts +1 -1
- package/dist/boot/runner-deps.js +3 -1
- package/dist/boot/session-faces.js +21 -21
- package/dist/boot/stage-01-config.d.ts +1 -0
- package/dist/boot/stage-01-config.js +4 -3
- package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
- package/dist/boot/stage-02-web-admin-pull.js +10 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +4 -4
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +1 -0
- package/dist/boot/stage-06-runners.d.ts +2 -1
- package/dist/boot/stage-06-runners.js +1 -2
- package/dist/boot/stage-07-capability-layer.d.ts +2 -1
- package/dist/boot/stage-07-capability-layer.js +1 -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-10-http-server.d.ts +35 -2
- package/dist/boot/stage-10-http-server.js +9 -4
- package/dist/boot/stores.d.ts +3 -0
- package/dist/boot/stores.js +22 -19
- package/dist/boot/web-admin.d.ts +1 -1
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/capability-facts.d.ts +70 -60
- package/dist/capability-facts.js +45 -49
- package/dist/card-settle-stored-scope.d.ts +56 -0
- package/dist/card-settle-stored-scope.js +27 -0
- package/dist/card-void.d.ts +7 -12
- package/dist/card-void.js +2 -7
- package/dist/config-catalog.d.ts +12 -0
- package/dist/config-catalog.js +37 -33
- package/dist/config-invariants.d.ts +5 -4
- package/dist/config-invariants.js +4 -4
- package/dist/config-types.d.ts +49 -16
- package/dist/config.d.ts +6 -5
- package/dist/config.js +35 -24
- package/dist/context-window-notice.d.ts +31 -0
- package/dist/context-window-notice.js +28 -0
- package/dist/deployment-governance.d.ts +7 -28
- package/dist/deployment-governance.js +1 -14
- package/dist/deployment-shape.d.ts +20 -11
- package/dist/deployment-shape.js +1 -0
- package/dist/device-ws-hub.d.ts +1 -0
- package/dist/device-ws-hub.js +64 -28
- package/dist/device-ws-protocol.d.ts +20 -6
- package/dist/device-ws-protocol.js +9 -2
- package/dist/engine-identity.d.ts +7 -1
- package/dist/engine-identity.js +5 -0
- package/dist/env-name-allowlist-knobs.d.ts +6 -5
- package/dist/env-name-allowlist-knobs.js +5 -0
- package/dist/execution-lane-caps.d.ts +20 -5
- package/dist/execution-lane-caps.js +18 -4
- package/dist/field-conflict.d.ts +2 -2
- package/dist/fleet/engine-signal.d.ts +14 -1
- package/dist/fleet/engine-signal.js +5 -1
- package/dist/fleet/fleet-reconciler.d.ts +1 -1
- package/dist/fleet/serving-lease.d.ts +18 -3
- package/dist/fleet/serving-lease.js +81 -35
- package/dist/fleet-client.d.ts +13 -11
- package/dist/fleet-client.js +53 -33
- package/dist/history-anchor.js +1 -1
- package/dist/hitl-delivery.d.ts +1 -3
- package/dist/hitl-delivery.js +1 -1
- package/dist/hooks/command-spawn.d.ts +7 -2
- package/dist/hooks/command-spawn.js +13 -6
- package/dist/hooks/hook-runner.d.ts +5 -0
- package/dist/hooks/hook-runner.js +11 -7
- package/dist/http/active-run-conflict.d.ts +3 -7
- package/dist/http/active-run-conflict.js +2 -2
- package/dist/http/admission.js +5 -5
- package/dist/http/approval-sweeps.js +5 -3
- package/dist/http/caller-trust.d.ts +2 -2
- package/dist/http/caller-trust.js +3 -3
- package/dist/http/checkpoint-ctx.d.ts +18 -63
- package/dist/http/checkpoint-ctx.js +1 -24
- package/dist/http/claim-heartbeat.d.ts +6 -6
- package/dist/http/claim-heartbeat.js +6 -6
- package/dist/http/claimed-run.d.ts +16 -9
- package/dist/http/claimed-run.js +8 -0
- package/dist/http/dispatch.js +7 -5
- package/dist/http/durable-run-leg.d.ts +8 -1
- package/dist/http/durable-run-leg.js +34 -15
- package/dist/http/execution-context.d.ts +115 -14
- package/dist/http/execution-context.js +130 -40
- package/dist/http/owner-access.d.ts +246 -21
- package/dist/http/owner-access.js +186 -8
- package/dist/http/principal-gate.d.ts +18 -11
- package/dist/http/principal-gate.js +3 -1
- package/dist/http/readiness.d.ts +4 -2
- package/dist/http/resume-ctx-read.d.ts +14 -3
- package/dist/http/resume-ctx-read.js +24 -3
- package/dist/http/resume-legs.d.ts +35 -3
- package/dist/http/resume-legs.js +262 -106
- package/dist/http/route-ctx.d.ts +29 -11
- package/dist/http/routes/a2a-serve.js +17 -14
- package/dist/http/routes/approvals-assistant.d.ts +3 -2
- package/dist/http/routes/approvals-assistant.js +11 -11
- package/dist/http/routes/capabilities.js +5 -4
- package/dist/http/routes/config-catalog.d.ts +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -1
- package/dist/http/routes/diagnostics.js +30 -5
- package/dist/http/routes/mcp-reconnect.js +2 -2
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +2 -3
- package/dist/http/routes/principal-inbox.d.ts +3 -1
- package/dist/http/routes/principal-inbox.js +3 -2
- package/dist/http/routes/projects.d.ts +1 -0
- package/dist/http/routes/projects.js +5 -3
- package/dist/http/routes/retention-ops.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.js +2 -2
- package/dist/http/routes/runs.d.ts +1 -1
- package/dist/http/routes/runs.js +33 -37
- package/dist/http/routes/session-memory-sync.js +4 -5
- package/dist/http/routes/session-sync.d.ts +24 -5
- package/dist/http/routes/session-sync.js +45 -37
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions.d.ts +1 -1
- package/dist/http/routes/sessions.js +18 -44
- package/dist/http/routes/tasks.js +136 -101
- package/dist/http/routes/trace-usage.js +14 -14
- package/dist/http/routes/workflows.d.ts +1 -0
- package/dist/http/routes/workflows.js +20 -38
- package/dist/http/server.d.ts +12 -9
- package/dist/http/server.js +35 -6
- package/dist/http/stored-body-replay.d.ts +3 -24
- package/dist/http/stored-body-replay.js +2 -12
- package/dist/http/submit-ctx.d.ts +2 -2
- package/dist/http/submit-ctx.js +5 -5
- package/dist/http/submit-replay.d.ts +15 -2
- package/dist/http/submit-replay.js +10 -4
- package/dist/http/wire-types.d.ts +6 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/leader/endpoint.js +9 -8
- package/dist/leg-facts.d.ts +13 -2
- package/dist/leg-facts.js +2 -0
- package/dist/local-store-declaration.d.ts +54 -0
- package/dist/local-store-declaration.js +59 -0
- package/dist/mcp-assertion-header.d.ts +1 -1
- package/dist/mcp-assertion-header.js +1 -1
- package/dist/mcp-authorization-face.d.ts +18 -14
- package/dist/mcp-authorization-face.js +3 -2
- package/dist/memory-project-layer.js +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +4 -4
- package/dist/memory-store-refusal.d.ts +1 -1
- package/dist/memory-sync-client.js +2 -2
- package/dist/model-context-window.d.ts +50 -11
- package/dist/model-context-window.js +20 -1
- package/dist/model-entry-refusal.d.ts +5 -4
- package/dist/model-entry-refusal.js +17 -3
- package/dist/model-reach-scope.d.ts +2 -1
- package/dist/observability/corrupt-read-seat.d.ts +1 -1
- package/dist/observability/fail-open.d.ts +19 -7
- package/dist/observability/fail-open.js +19 -7
- package/dist/observability/logger.d.ts +7 -0
- package/dist/observability/logger.js +3 -1
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +1 -1
- package/dist/observability/secret-env-scrub.js +1 -1
- package/dist/org-delivery-gate.d.ts +31 -6
- package/dist/org-delivery-gate.js +7 -4
- package/dist/owner-faces.d.ts +30 -5
- package/dist/owner-faces.js +2 -0
- package/dist/park-row-commit.d.ts +67 -0
- package/dist/park-row-commit.js +76 -0
- package/dist/parked-decide.d.ts +9 -7
- package/dist/parked-decide.js +25 -6
- package/dist/peer-directory.d.ts +1 -1
- package/dist/peer-seat.d.ts +1 -1
- package/dist/peer-seat.js +52 -59
- package/dist/plugins/adoption-log-sql.d.ts +3 -3
- package/dist/plugins/adoption-log-sql.js +19 -19
- package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
- package/dist/plugins/approval-ask-store-sql.js +3 -5
- package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
- package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
- package/dist/plugins/approval-exemption-skill-m46.d.ts +0 -6
- package/dist/plugins/approval-exemption-skill-m46.js +6 -10
- package/dist/plugins/approval-exemption-store.d.ts +26 -1
- package/dist/plugins/approval-exemption-store.js +28 -19
- package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -3
- package/dist/plugins/ensure-schema-lock-policy.js +75 -9
- package/dist/plugins/exec-path-law.d.ts +34 -0
- package/dist/plugins/exec-path-law.js +13 -0
- package/dist/plugins/file-run-store.d.ts +22 -9
- package/dist/plugins/file-run-store.js +51 -23
- package/dist/plugins/local-checkpoint-store.d.ts +8 -1
- package/dist/plugins/local-checkpoint-store.js +5 -1
- package/dist/plugins/local-data-root-admission.d.ts +52 -0
- package/dist/plugins/local-data-root-admission.js +100 -0
- package/dist/plugins/local-engine-high-water.d.ts +28 -7
- package/dist/plugins/local-engine-high-water.js +27 -22
- package/dist/plugins/local-session-store.d.ts +134 -49
- package/dist/plugins/local-session-store.js +149 -108
- package/dist/plugins/memory-run-store.d.ts +14 -9
- package/dist/plugins/memory-run-store.js +48 -22
- package/dist/plugins/one-time-migration-marker.d.ts +19 -3
- package/dist/plugins/one-time-migration-marker.js +7 -1
- package/dist/plugins/one-time-migrations.d.ts +110 -59
- package/dist/plugins/one-time-migrations.js +224 -136
- package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
- package/dist/plugins/org-delivery-quota-store.js +29 -14
- package/dist/plugins/permission-rule-store-sql.d.ts +1 -1
- package/dist/plugins/permission-rule-store-sql.js +5 -9
- package/dist/plugins/pg-pool.d.ts +1 -1
- package/dist/plugins/pg-pool.js +14 -12
- package/dist/plugins/posix-shell-fs.js +4 -2
- package/dist/plugins/remote-env-adb.js +3 -2
- package/dist/plugins/remote-env-device.d.ts +3 -2
- package/dist/plugins/remote-env-device.js +8 -6
- package/dist/plugins/remote-env-e2b.d.ts +4 -8
- package/dist/plugins/remote-env-e2b.js +21 -15
- package/dist/plugins/remote-env-host.d.ts +51 -10
- package/dist/plugins/remote-env-host.js +97 -31
- package/dist/plugins/remote-env-k8s.js +7 -4
- package/dist/plugins/remote-env-local-docker.d.ts +6 -3
- package/dist/plugins/remote-env-local-docker.js +11 -25
- package/dist/plugins/remote-env-ssh.js +4 -3
- package/dist/plugins/remote-shell.d.ts +3 -1
- package/dist/plugins/remote-shell.js +3 -3
- package/dist/plugins/replica-registration.d.ts +35 -0
- package/dist/plugins/replica-registration.js +48 -0
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/run-store-sql.d.ts +27 -8
- package/dist/plugins/run-store-sql.js +46 -18
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +8 -12
- package/dist/plugins/scenario-prefix-switch-m42.js +9 -6
- package/dist/plugins/schema-consistency.d.ts +91 -5
- package/dist/plugins/schema-consistency.js +189 -7
- package/dist/plugins/schema-drift-sentence.d.ts +6 -5
- package/dist/plugins/schema-drift-sentence.js +13 -5
- package/dist/plugins/serving-requirement.d.ts +49 -0
- package/dist/plugins/serving-requirement.js +53 -0
- package/dist/plugins/session-placement.d.ts +5 -4
- package/dist/plugins/shell-sentinel-owner-m48.d.ts +22 -0
- package/dist/plugins/shell-sentinel-owner-m48.js +6 -0
- package/dist/plugins/stop-then-start-sentence.d.ts +55 -0
- package/dist/plugins/stop-then-start-sentence.js +51 -0
- package/dist/plugins/store-backend.d.ts +23 -9
- package/dist/plugins/store-backend.js +58 -24
- package/dist/plugins/store-contracts.d.ts +45 -5
- package/dist/plugins/store-contracts.js +19 -0
- package/dist/plugins/tidb-pool.d.ts +11 -6
- package/dist/plugins/tidb-pool.js +22 -12
- package/dist/plugins/workspace-custody.d.ts +44 -0
- package/dist/plugins/workspace-custody.js +47 -0
- package/dist/process-instance.d.ts +3 -0
- package/dist/process-instance.js +7 -0
- package/dist/project-context.d.ts +66 -0
- package/dist/project-context.js +70 -0
- package/dist/project-identity.d.ts +8 -7
- package/dist/project-identity.js +40 -17
- package/dist/project-layer-key.d.ts +13 -2
- package/dist/project-layer-key.js +9 -1
- package/dist/question.d.ts +9 -21
- package/dist/question.js +28 -19
- package/dist/read-deny-floor-notice.d.ts +36 -0
- package/dist/read-deny-floor-notice.js +40 -0
- package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
- package/dist/request-declaration-disposition.js +39 -0
- package/dist/run-cancel-card.d.ts +28 -0
- package/dist/run-cancel-card.js +20 -0
- package/dist/run-cancel-context.d.ts +10 -0
- package/dist/run-cancel-context.js +1 -0
- package/dist/run-local.js +28 -13
- package/dist/runs.d.ts +31 -7
- package/dist/runs.js +34 -16
- package/dist/security.d.ts +3 -3
- package/dist/server-secret-env.d.ts +34 -20
- package/dist/server-secret-env.js +61 -18
- package/dist/session-agent.d.ts +4 -12
- package/dist/session-agent.js +0 -6
- package/dist/session-children-retire.d.ts +6 -3
- package/dist/session-children-retire.js +2 -5
- package/dist/session-incarnation.d.ts +18 -4
- package/dist/session-incarnation.js +4 -1
- package/dist/session-notify-door.d.ts +4 -4
- package/dist/session-notify-door.js +5 -6
- package/dist/session-rules-seat.d.ts +4 -3
- package/dist/session-rules-seat.js +3 -11
- package/dist/session-sync.js +3 -3
- package/dist/session-workspace.d.ts +3 -3
- package/dist/store-admission.d.ts +33 -0
- package/dist/store-admission.js +49 -0
- package/dist/store-posture.d.ts +5 -0
- package/dist/store-posture.js +18 -1
- package/dist/stores-sql/checkpoint-store-sql.d.ts +22 -3
- package/dist/stores-sql/checkpoint-store-sql.js +109 -23
- package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
- package/dist/stores-sql/file-history-store-sql.js +3 -5
- package/dist/stores-sql/retention-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.js +4 -6
- package/dist/task-a2a.d.ts +3 -8
- package/dist/task-a2a.js +25 -27
- package/dist/task-cwd.d.ts +27 -10
- package/dist/task-cwd.js +3 -0
- package/dist/task-mcp.d.ts +3 -3
- package/dist/task-mcp.js +1 -1
- package/dist/task-settings.d.ts +13 -0
- package/dist/task-settings.js +14 -7
- package/dist/terminal.d.ts +2 -0
- package/dist/terminal.js +21 -6
- package/dist/tool-approval.d.ts +50 -41
- package/dist/tool-approval.js +96 -73
- package/dist/trace/engine-notice-wire.d.ts +38 -21
- package/dist/trace/engine-notice-wire.js +25 -4
- package/dist/trace/ledger-sink.d.ts +3 -1
- package/dist/trace/project.d.ts +8 -5
- package/dist/trace/project.js +11 -9
- package/dist/trace/projection-table.d.ts +33 -2
- package/dist/trace/redact.d.ts +12 -0
- package/dist/trace/redact.js +5 -1
- package/dist/trace/self-minted-wire.d.ts +268 -56
- package/dist/trace/self-minted-wire.js +110 -40
- package/dist/trace/tool-card.d.ts +3 -0
- package/dist/trace/tool-card.js +24 -0
- package/dist/trace/tool-output-media.d.ts +2 -1
- package/dist/unattended-permission-mode.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.js +22 -9
- package/dist/web-admin-client/effective-keys.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
- package/docs/error-text-lexicon.json +317 -0
- package/package.json +4 -2
- package/scripts/embed-minimal.mjs +183 -0
- package/wire-changes/server@7.149.0.json +372 -0
- package/wire-changes/server@7.150.0.json +335 -0
- package/dist/plain-string-record.js +0 -70
|
@@ -40,8 +40,9 @@ export function createServingLeaseClient(o) {
|
|
|
40
40
|
const mono = o.clock?.mono ?? (() => performance.now());
|
|
41
41
|
const epoch = o.clock?.epoch ?? Date.now;
|
|
42
42
|
const stickyPath = o.dataRoot === undefined ? undefined : join(o.dataRoot, SERVING_LEASE_DENIED_FILE);
|
|
43
|
-
let
|
|
44
|
-
const enabled = () =>
|
|
43
|
+
let fence = { kind: "presumed", since: mono() };
|
|
44
|
+
const enabled = () => fence.kind === "on";
|
|
45
|
+
let viewListener;
|
|
45
46
|
let denied = false;
|
|
46
47
|
let stopped = false;
|
|
47
48
|
let lease;
|
|
@@ -49,6 +50,37 @@ export function createServingLeaseClient(o) {
|
|
|
49
50
|
let timer;
|
|
50
51
|
let inFlight;
|
|
51
52
|
let failStreak = 0;
|
|
53
|
+
const view = () => {
|
|
54
|
+
if (denied)
|
|
55
|
+
return { state: "denied" };
|
|
56
|
+
if (fence.kind === "off")
|
|
57
|
+
return { state: "none" };
|
|
58
|
+
const now = mono();
|
|
59
|
+
if (lease !== undefined && now < lease.expiresAtMono)
|
|
60
|
+
return { state: "granted", leaseUntil: lease.leaseUntil };
|
|
61
|
+
return { state: "expired", pastOutageMax: now - (lease?.expiresAtMono ?? fence.since) >= o.outageMaxMs };
|
|
62
|
+
};
|
|
63
|
+
const changing = (change) => {
|
|
64
|
+
const before = view().state;
|
|
65
|
+
change();
|
|
66
|
+
if (view().state !== before)
|
|
67
|
+
viewListener?.();
|
|
68
|
+
};
|
|
69
|
+
const unconfirmed = (why) => {
|
|
70
|
+
recordFailOpen("server.fleet.serving-lease-unconfirmed", `worker=${o.center.worker} generation=${o.generation} why=${why}`);
|
|
71
|
+
failStreak += 1;
|
|
72
|
+
if (failStreak === 1) {
|
|
73
|
+
o.logger?.warn("fleet_serving_lease_unconfirmed", {
|
|
74
|
+
error: why,
|
|
75
|
+
note: "the fleet center has not confirmed this instance's serving lease (heartbeat or renewal unanswered, refused or unreadable, or the verdict was unknown) — this instance keeps serving until its last grant, or with no grant yet the moment the fence took effect (this process's start, for a member that has not heard the center), is FLEET_LEASE_OUTAGE_MAX_MS behind, then refuses new work (unconfirmed is never a denial)",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const confirmed = (by) => {
|
|
80
|
+
if (failStreak > 0)
|
|
81
|
+
o.logger?.info("fleet_serving_lease_recovered", { after: failStreak, by });
|
|
82
|
+
failStreak = 0;
|
|
83
|
+
};
|
|
52
84
|
const clear = () => {
|
|
53
85
|
if (timer !== undefined)
|
|
54
86
|
clearTimeout(timer);
|
|
@@ -79,6 +111,9 @@ export function createServingLeaseClient(o) {
|
|
|
79
111
|
const enterDenied = (denial) => {
|
|
80
112
|
if (denied)
|
|
81
113
|
return;
|
|
114
|
+
changing(() => enterDeniedNow(denial));
|
|
115
|
+
};
|
|
116
|
+
const enterDeniedNow = (denial) => {
|
|
82
117
|
denied = true;
|
|
83
118
|
lease = undefined;
|
|
84
119
|
clear();
|
|
@@ -118,25 +153,22 @@ export function createServingLeaseClient(o) {
|
|
|
118
153
|
why = err instanceof Error ? err.message : String(err);
|
|
119
154
|
}
|
|
120
155
|
if (reading === undefined || reading.verdict === "unknown") {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
failStreak += 1;
|
|
124
|
-
if (failStreak === 1)
|
|
125
|
-
o.logger?.warn("fleet_serving_lease_unconfirmed", { error: unconfirmed, note: "the serving lease could not be renewed (the center is unreachable or did not confirm this instance) — this instance keeps serving until the lease expires and then for FLEET_LEASE_OUTAGE_MAX_MS more before it refuses new work (unconfirmed is never a denial)" });
|
|
156
|
+
if (fence.kind === "on")
|
|
157
|
+
unconfirmed(reading === undefined ? why : "verdict=unknown (the center holds no lifecycle object for this instance right now)");
|
|
126
158
|
schedule(nextInMs);
|
|
127
159
|
return;
|
|
128
160
|
}
|
|
129
|
-
|
|
130
|
-
o.logger?.info("fleet_serving_lease_recovered", { after: failStreak });
|
|
131
|
-
failStreak = 0;
|
|
161
|
+
confirmed("verdict");
|
|
132
162
|
switch (reading.verdict) {
|
|
133
|
-
case "granted":
|
|
163
|
+
case "granted": {
|
|
134
164
|
if (denied)
|
|
135
165
|
return;
|
|
136
|
-
|
|
166
|
+
const granted = { expiresAtMono: sendMono + reading.ttlMs, leaseUntil: sendEpoch + reading.ttlMs };
|
|
167
|
+
changing(() => (lease = granted));
|
|
137
168
|
nextInMs = reading.ttlMs / 3;
|
|
138
169
|
schedule(nextInMs);
|
|
139
170
|
return;
|
|
171
|
+
}
|
|
140
172
|
case "denied":
|
|
141
173
|
enterDenied({ from: "center", reason: reading.reason });
|
|
142
174
|
return;
|
|
@@ -154,34 +186,48 @@ export function createServingLeaseClient(o) {
|
|
|
154
186
|
});
|
|
155
187
|
return inFlight;
|
|
156
188
|
};
|
|
189
|
+
const noteDeclaration = (caps) => {
|
|
190
|
+
const on = caps.servingLease === true;
|
|
191
|
+
if (!on)
|
|
192
|
+
recordFailOpen("server.fleet.serving-lease-capability-absent", `worker=${o.center.worker} generation=${o.generation} was=${fence.kind}`);
|
|
193
|
+
if (on && fence.kind !== "on") {
|
|
194
|
+
fence = { kind: "on", since: fence.since ?? mono() };
|
|
195
|
+
o.logger?.info("fleet_serving_lease_enabled", { generation: o.generation, worker: o.center.worker });
|
|
196
|
+
void renewNow();
|
|
197
|
+
}
|
|
198
|
+
else if (!on && fence.kind !== "off") {
|
|
199
|
+
const was = fence.kind;
|
|
200
|
+
fence = { kind: "off", since: was === "on" ? fence.since : undefined };
|
|
201
|
+
clear();
|
|
202
|
+
confirmed("not-enforced");
|
|
203
|
+
if (was === "on")
|
|
204
|
+
o.logger?.warn("fleet_serving_lease_disabled", { note: "the fleet center no longer announces the servingLease capability — the lease is out of effect (leaseState none) until it does again; its outage clock is not reset (when the capability returns, the last grant and the moment the fence first took effect in this process still count)" });
|
|
205
|
+
}
|
|
206
|
+
};
|
|
157
207
|
return {
|
|
158
|
-
|
|
208
|
+
noteHeartbeat(hearing) {
|
|
159
209
|
if (stopped || denied)
|
|
160
210
|
return;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
211
|
+
switch (hearing.kind) {
|
|
212
|
+
case "silent":
|
|
213
|
+
if (fence.kind === "presumed")
|
|
214
|
+
unconfirmed(`heartbeat:${hearing.why}`);
|
|
215
|
+
return;
|
|
216
|
+
case "reading":
|
|
217
|
+
if (fence.kind === "presumed")
|
|
218
|
+
changing(() => noteDeclaration(hearing.capabilities));
|
|
219
|
+
else
|
|
220
|
+
noteDeclaration(hearing.capabilities);
|
|
221
|
+
return;
|
|
222
|
+
default: {
|
|
223
|
+
const unhandled = hearing;
|
|
224
|
+
throw new Error(`serving lease: unknown heartbeat hearing ${JSON.stringify(unhandled)}`);
|
|
225
|
+
}
|
|
173
226
|
}
|
|
174
227
|
},
|
|
175
|
-
view
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (capability === undefined || !capability.announced)
|
|
179
|
-
return { state: "none" };
|
|
180
|
-
const now = mono();
|
|
181
|
-
if (lease !== undefined && now < lease.expiresAtMono)
|
|
182
|
-
return { state: "granted", leaseUntil: lease.leaseUntil };
|
|
183
|
-
const expiredAt = lease?.expiresAtMono ?? capability.firstSeenAtMono;
|
|
184
|
-
return { state: "expired", pastOutageMax: now - expiredAt >= o.outageMaxMs };
|
|
228
|
+
view,
|
|
229
|
+
onViewChange(listener) {
|
|
230
|
+
viewListener = listener;
|
|
185
231
|
},
|
|
186
232
|
resumeStickyDenial() {
|
|
187
233
|
if (stickyPath === undefined || denied)
|
package/dist/fleet-client.d.ts
CHANGED
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
* - **`modelRefusals?`([ref])**:在场 ⇔ 本副本对最近一次被判的中心候选目录有可报的拒(逐条名 → 码 / 模型面组拒码)—— 取自
|
|
19
19
|
* `/health.modelRefusals` **同一只**取值口(装配层接 `deployment.modelRefusalsState`),同一个对象,形 = settings-schema
|
|
20
20
|
* `ModelRefusalsSignal`;缺席即键缺席(同 `restart`)。
|
|
21
|
-
* - **自报状态翻转即刻再 announce**(第一时间自摘流 / 自挂流 / 报重启 /
|
|
22
|
-
* `announceNow`;roster 落地、restart 信号与模型拒因变化由装配层把 web-admin 的 `onSelfReportChange` 接到 `announceNow
|
|
23
|
-
* (
|
|
21
|
+
* - **自报状态翻转即刻再 announce**(第一时间自摘流 / 自挂流 / 报重启 / 报拒因 / 报租约态,比任何观测都快):draining 翻转由收尾链直调
|
|
22
|
+
* `announceNow`;roster 落地、restart 信号与模型拒因变化由装配层把 web-admin 的 `onSelfReportChange` 接到 `announceNow`;租约态由事件引起的
|
|
23
|
+
* 翻转(推定的终结 / 续租判决 / 被拒 —— 不自激的那几样,见 `fleet/serving-lease.ts` 规则 2′)由装配层把服务租约客户端的 `onViewChange` 接到
|
|
24
|
+
* `announceNow`(三处 = 三类输入各自的唯一写者,触发都是 `announceNow`)。退出前 DELETE(显式注销,幂等)。
|
|
24
25
|
* - **usage 批报**:tracer 的 `brain.call` 已带 ALS principal —— FleetUsageAccumulator 按 principal×model
|
|
25
26
|
* 累计,每窗口(默认 60s,半开 [windowStartMs, windowEndMs))POST /api/fleet/usage。幂等键
|
|
26
27
|
* (worker,instanceId,windowStartMs) 重发安全;**发送失败 → merge 回累计器**,并入下一窗(失败窗从未入账,
|
|
@@ -30,8 +31,9 @@
|
|
|
30
31
|
* 不在下一个人的凭证下上报(不记到别人头上);它留在原箱,那个身份回来时照常报出去,收尾时还压着就响亮一行。
|
|
31
32
|
* - 全部 fire-and-forget + swallow-guarded:fleet 面是运维旁路,绝不影响任务面;网络错误只 warn 一次/连败类。
|
|
32
33
|
*/
|
|
33
|
-
import { UsageReport, type CapabilityReading, type
|
|
34
|
+
import { UsageReport, type CapabilityReading, type ModelRefusalsSignal } from "@sema-agent/settings-schema/fleet";
|
|
34
35
|
import { type EngineSignalFacts } from "./fleet/engine-signal.js";
|
|
36
|
+
import type { HeartbeatHearing } from "./fleet/serving-lease.js";
|
|
35
37
|
import { type CenterEndpoint } from "./web-admin-client/center-request.js";
|
|
36
38
|
import { type CenterCredentialSource } from "./center-credential.js";
|
|
37
39
|
/** Bound on distinct principal×model keys per window — a runaway/multi-tenant burst collapses into
|
|
@@ -118,10 +120,10 @@ export interface FleetClientOpts {
|
|
|
118
120
|
/** [ref]:模型拒因信号(装配层接 `/health` 同一只取值口 `deployment.modelRefusalsState`)。每条公告现读一次;`undefined` ⇒ 公告不带
|
|
119
121
|
* `modelRefusals` 键。必填:与 `restart` 同 —— 心跳面没有「不知道有没有拒」这一形(老引擎不发这个键,中心按 `version` 判「不可知」)。 */
|
|
120
122
|
modelRefusals: () => ModelRefusalsSignal | undefined;
|
|
121
|
-
/** 编排器片 2
|
|
122
|
-
*
|
|
123
|
-
* `
|
|
124
|
-
|
|
123
|
+
/** 编排器片 2:**每一次公告尝试恰好交一只**听到的东西(装配层接服务租约客户端的 `noteHeartbeat`;只有 fleet 成员接)。`reading` = 中心的显式
|
|
124
|
+
* 声明(规则见 {@link capabilityReadingOf});其余一律 `silent`(`why` 闭集:`HTTP <状态>` / 请求抛出的那句 / `reply-unreadable:<因>` /
|
|
125
|
+
* `not-sent:<那句>`)—— 没听到不是「没有」,租约客户端按沉默处置。没接这一只 ⇒ 不读答复体(非成员的心跳答复不进任何租约臂)。 */
|
|
126
|
+
onHeartbeat?: (hearing: HeartbeatHearing) => void;
|
|
125
127
|
/** [ref]:按凭证身份分箱的用量账(缺席 = 用量环不装)。 */
|
|
126
128
|
usage?: FleetUsageBins;
|
|
127
129
|
heartbeatMs?: number;
|
|
@@ -149,13 +151,13 @@ export interface FleetClient {
|
|
|
149
151
|
flushFinalUsage: () => Promise<void>;
|
|
150
152
|
}
|
|
151
153
|
/**
|
|
152
|
-
* 心跳 2xx 答复读不成能力读数的原因 —— 一张闭集:`body` = 答复体本身读不出(空体 / 截断 / 非 JSON)
|
|
153
|
-
* `readCapabilityReading` 的 `unreadable`
|
|
154
|
+
* 心跳 2xx 答复读不成能力读数的原因 —— 一张闭集:`body` = 答复体本身读不出(空体 / 截断 / 非 JSON),其余三词 = 契约读器
|
|
155
|
+
* `readCapabilityReading` 的 `unreadable` 三因(`not-an-object` / `capabilities-absent` / `invalid-capabilities`)。
|
|
154
156
|
*/
|
|
155
157
|
export type AnnounceReplyFlaw = "body" | Extract<CapabilityReading, {
|
|
156
158
|
kind: "unreadable";
|
|
157
159
|
}>["why"];
|
|
158
|
-
/**
|
|
160
|
+
/** 本仓对一次心跳答复的读法:契约两态,`unreadable` 的原因扩成 {@link AnnounceReplyFlaw}(`detail` = 读不出体时的那句错误,当数据交出)。 */
|
|
159
161
|
export type AnnounceReplyReading = Exclude<CapabilityReading, {
|
|
160
162
|
kind: "unreadable";
|
|
161
163
|
}> | {
|
package/dist/fleet-client.js
CHANGED
|
@@ -110,9 +110,40 @@ export function startFleetClient(opts) {
|
|
|
110
110
|
let failStreak = 0;
|
|
111
111
|
let unreadableStreak = 0;
|
|
112
112
|
const address = opts.advertiseAddress;
|
|
113
|
+
const announceFailed = (error) => {
|
|
114
|
+
failStreak += 1;
|
|
115
|
+
if (failStreak === 1)
|
|
116
|
+
opts.logger?.warn("fleet_announce_failed", { error });
|
|
117
|
+
};
|
|
118
|
+
const hearReply = async (res, leaseState) => {
|
|
119
|
+
const reading = await capabilityReadingOf(res);
|
|
120
|
+
switch (reading.kind) {
|
|
121
|
+
case "declared":
|
|
122
|
+
unreadableStreak = 0;
|
|
123
|
+
return { kind: "reading", capabilities: reading.capabilities };
|
|
124
|
+
case "unreadable":
|
|
125
|
+
recordFailOpen("server.fleet.announce-reply-unreadable", `why=${reading.why} leaseState=${leaseState}`);
|
|
126
|
+
unreadableStreak += 1;
|
|
127
|
+
if (unreadableStreak === 1) {
|
|
128
|
+
opts.logger?.warn("fleet_announce_reply_unreadable", {
|
|
129
|
+
status: res.status,
|
|
130
|
+
why: reading.why,
|
|
131
|
+
...(reading.detail !== undefined ? { error: reading.detail } : {}),
|
|
132
|
+
note: "the fleet center's heartbeat reply is not a capability declaration (unreadable body, not an object, no capabilities key, or a malformed capabilities value) — no reading taken; the serving lease keeps its state and outage clock (a member that has heard no declaration stays inside the presumed fence)",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return { kind: "silent", why: `reply-unreadable:${reading.why}` };
|
|
136
|
+
default: {
|
|
137
|
+
const unhandled = reading;
|
|
138
|
+
throw new Error(`heartbeat reply: unknown capability reading ${JSON.stringify(unhandled)}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
113
142
|
const announceOnce = async () => {
|
|
114
143
|
if (stopped || address === undefined)
|
|
115
144
|
return;
|
|
145
|
+
let hearing;
|
|
146
|
+
let sending = false;
|
|
116
147
|
try {
|
|
117
148
|
const signal = opts.signal();
|
|
118
149
|
const engineSignal = buildEngineSignal(signal);
|
|
@@ -129,43 +160,32 @@ export function startFleetClient(opts) {
|
|
|
129
160
|
...fleetMembershipKeys(signal),
|
|
130
161
|
[ENGINE_SIGNAL_KEY]: engineSignal,
|
|
131
162
|
};
|
|
163
|
+
sending = true;
|
|
132
164
|
const { res } = await send("fleet-instances", "POST", body);
|
|
133
|
-
if (!res.ok)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const reading = await capabilityReadingOf(res);
|
|
137
|
-
switch (reading.kind) {
|
|
138
|
-
case "declared":
|
|
139
|
-
case "undeclared":
|
|
140
|
-
unreadableStreak = 0;
|
|
141
|
-
opts.onCenterCapabilities(reading.kind === "declared" ? reading.capabilities : undefined);
|
|
142
|
-
break;
|
|
143
|
-
case "unreadable":
|
|
144
|
-
recordFailOpen("server.fleet.announce-reply-unreadable", `why=${reading.why} leaseState=${signal.lease.state}`);
|
|
145
|
-
unreadableStreak += 1;
|
|
146
|
-
if (unreadableStreak === 1) {
|
|
147
|
-
opts.logger?.warn("fleet_announce_reply_unreadable", {
|
|
148
|
-
status: res.status,
|
|
149
|
-
why: reading.why,
|
|
150
|
-
...(reading.detail !== undefined ? { error: reading.detail } : {}),
|
|
151
|
-
note: "the fleet center's heartbeat reply is not a capability reading (unreadable body, not an object, or a malformed capabilities value) — no reading taken; the last one stands (the serving lease keeps its state and outage clock)",
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
break;
|
|
155
|
-
default: {
|
|
156
|
-
const unhandled = reading;
|
|
157
|
-
throw new Error(`heartbeat reply: unknown capability reading ${JSON.stringify(unhandled)}`);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
165
|
+
if (!res.ok) {
|
|
166
|
+
hearing = { kind: "silent", why: `HTTP ${res.status}` };
|
|
167
|
+
announceFailed(`announce ${res.status}${await rejectionSnippet(res)}`);
|
|
160
168
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
169
|
+
else {
|
|
170
|
+
if (failStreak > 0)
|
|
171
|
+
opts.logger?.info("fleet_announce_recovered", { after: failStreak });
|
|
172
|
+
failStreak = 0;
|
|
173
|
+
if (opts.onHeartbeat !== undefined)
|
|
174
|
+
hearing = await hearReply(res, signal.lease.state);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
179
|
+
hearing = { kind: "silent", why: sending ? error : `not-sent:${error}` };
|
|
180
|
+
announceFailed(error);
|
|
181
|
+
}
|
|
182
|
+
if (hearing === undefined || opts.onHeartbeat === undefined)
|
|
183
|
+
return;
|
|
184
|
+
try {
|
|
185
|
+
opts.onHeartbeat(hearing);
|
|
164
186
|
}
|
|
165
187
|
catch (err) {
|
|
166
|
-
|
|
167
|
-
if (failStreak === 1)
|
|
168
|
-
opts.logger?.warn("fleet_announce_failed", { error: err instanceof Error ? err.message : String(err) });
|
|
188
|
+
opts.logger?.warn("fleet_heartbeat_listener_failed", { error: err instanceof Error ? err.message : String(err), note: "the serving-lease client threw while taking this heartbeat's hearing — this beat did not reach the fence; the announce loop goes on" });
|
|
169
189
|
}
|
|
170
190
|
};
|
|
171
191
|
let inFlight;
|
package/dist/history-anchor.js
CHANGED
|
@@ -10,7 +10,7 @@ async function unknownAnchorCause(readers, reader, sessionId, handle) {
|
|
|
10
10
|
if (reader === undefined)
|
|
11
11
|
return NO_ANCHOR;
|
|
12
12
|
try {
|
|
13
|
-
const run = await readVisibleRun(reader, readers.runs, handle);
|
|
13
|
+
const run = (await readVisibleRun(reader, readers.runs, handle))?.row;
|
|
14
14
|
if (run === undefined || run.sessionId !== sessionId)
|
|
15
15
|
return NO_ANCHOR;
|
|
16
16
|
return (await readers.anchors.resolve(sessionId, handle, run.owner)) === undefined ? PROMPT_WITHOUT_ANCHOR : NO_ANCHOR;
|
package/dist/hitl-delivery.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export interface HitlSinks {
|
|
|
29
29
|
readonly live?: boolean;
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
|
-
* 一条腿的传输(无状态)。`deliver`
|
|
32
|
+
* 一条腿的传输(无状态)。`deliver` 是**属性**(不是方法签名)—— 帧型参数因此逆变。腿上装出的那一只是全族型(`HitlTransport<HitlFrame>`,
|
|
33
33
|
* 一腿一只、三族共用);**窄化发生在装进族上下文那一步**:审批上下文的判别联合把传输型钉到本族帧(呈卡族收不进旧族帧),竞速机的目标从族上下文取
|
|
34
34
|
* ⇒ 混族的 (transport, frame) 对编译不过。直接拿全族型传输喂竞速机的代码不受这道型约束(三只协调器都从族上下文取,`P14` 编译期钉)。
|
|
35
35
|
*/
|
|
@@ -39,8 +39,6 @@ export interface HitlTransport<F extends HitlFrame> {
|
|
|
39
39
|
* 写抛 / 已死 ⇒ 直写没接下(不碰死连接)。③ 交回每只涉及的 sink 接下与否。**永不 reject。**
|
|
40
40
|
*/
|
|
41
41
|
readonly deliver: (frame: F, onto?: HitlSinks) => Promise<HitlSinks>;
|
|
42
|
-
/** 判决面谓词:有直写 ⇒ 直写此刻活着;只有账本 ⇒ 恒真(账本 tail 随时可被接上)。谓词读不出 ⇒ 假(判决面 fail-closed 到停驻)。 */
|
|
43
|
-
readonly reachable: () => boolean;
|
|
44
42
|
}
|
|
45
43
|
/** 直写面:一个具名帧写 + 「此刻还活着吗」。 */
|
|
46
44
|
export interface HitlLiveSink<F extends HitlFrame> {
|
package/dist/hitl-delivery.js
CHANGED
|
@@ -66,7 +66,7 @@ export function createHitlTransport(sinks) {
|
|
|
66
66
|
}
|
|
67
67
|
return ledgerAccepted !== undefined ? { ledger: ledgerAccepted, live: liveAccepted } : { live: liveAccepted };
|
|
68
68
|
};
|
|
69
|
-
return { deliver
|
|
69
|
+
return { deliver };
|
|
70
70
|
}
|
|
71
71
|
export function createHitlOpenRace(args) {
|
|
72
72
|
const opens = args.targets.map((t) => t.transport.deliver(t.frame));
|
|
@@ -46,9 +46,14 @@ export type CommandSpawnPlan = {
|
|
|
46
46
|
export declare function composeHookEnvLayer(requestEnv: Readonly<Record<string, string>> | undefined, cwd: string, plugin: HookPluginContext | undefined): Record<string, string> & {
|
|
47
47
|
CLAUDE_PROJECT_DIR: string;
|
|
48
48
|
};
|
|
49
|
+
/**
|
|
50
|
+
* 这条 hook 的子进程会拿到的环境:有效环境层叠在继承那一半之上(组合属主 = `hook-runner.ts` 的 `hookChildEnv`,本模块不写第二份组合,只问它的读数)。
|
|
51
|
+
* 组合拒(授予形坏 / 放回点到本服务自有凭据名)⇒ 抛,起法按「起不来」答(与 spawn 时组合抛同一个结局)。
|
|
52
|
+
*/
|
|
53
|
+
export type HookChildEnvOf = (layer: Readonly<Record<string, string>>) => Readonly<Record<string, string | undefined>>;
|
|
49
54
|
/**
|
|
50
55
|
* 一条 command 条目的起法(规则见文件头)。`cwd` = 本次 hook 的工作目录(`CLAUDE_PROJECT_DIR` 的缺省值);`requestEnv` = 请求 `settings.env`(hook 环境的一层;**必传**:没有就显式传 `undefined`,
|
|
51
|
-
* 漏传是编译红,不会静默丢掉请求给的 env),合并与读法见 {@link composeHookEnvLayer} / {@link contextVarValue}。有副作用的只有两步,都只在条目带 `plugin` 时发生:读插件根是否是目录、建数据目录(CC 同:注入即建)。
|
|
56
|
+
* 漏传是编译红,不会静默丢掉请求给的 env),合并与读法见 {@link composeHookEnvLayer} / {@link contextVarValue};`childEnvOf` = 组合的读数入口({@link HookChildEnvOf})。有副作用的只有两步,都只在条目带 `plugin` 时发生:读插件根是否是目录、建数据目录(CC 同:注入即建)。
|
|
52
57
|
*/
|
|
53
|
-
export declare function planCommandSpawn(entry: RunnableCommandHookEntry, cwd: string, requestEnv: Readonly<Record<string, string>> | undefined): Promise<CommandSpawnPlan>;
|
|
58
|
+
export declare function planCommandSpawn(entry: RunnableCommandHookEntry, cwd: string, requestEnv: Readonly<Record<string, string>> | undefined, childEnvOf: HookChildEnvOf): Promise<CommandSpawnPlan>;
|
|
54
59
|
//# sourceMappingURL=command-spawn.d.ts.map
|
|
@@ -59,17 +59,24 @@ const refOf = (name) => "${" + name + "}";
|
|
|
59
59
|
export function composeHookEnvLayer(requestEnv, cwd, plugin) {
|
|
60
60
|
return { ...requestEnv, CLAUDE_PROJECT_DIR: requestEnv?.CLAUDE_PROJECT_DIR ?? cwd, ...hookPluginEnv(plugin) };
|
|
61
61
|
}
|
|
62
|
-
function contextVarValue(name,
|
|
63
|
-
const v =
|
|
62
|
+
function contextVarValue(name, env) {
|
|
63
|
+
const v = env[name];
|
|
64
64
|
return v !== undefined && v.length > 0 ? v : undefined;
|
|
65
65
|
}
|
|
66
|
-
export async function planCommandSpawn(entry, cwd, requestEnv) {
|
|
66
|
+
export async function planCommandSpawn(entry, cwd, requestEnv, childEnvOf) {
|
|
67
67
|
const texts = [entry.command, ...(entry.args ?? [])];
|
|
68
68
|
const p = entry.plugin;
|
|
69
69
|
const env = composeHookEnvLayer(requestEnv, cwd, p);
|
|
70
|
+
let seen;
|
|
71
|
+
try {
|
|
72
|
+
seen = childEnvOf(env);
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
return { kind: "refused", error: e instanceof Error ? e.message : String(e) };
|
|
76
|
+
}
|
|
70
77
|
const values = new Map([["CLAUDE_PROJECT_DIR", env.CLAUDE_PROJECT_DIR]]);
|
|
71
78
|
for (const [name, ref] of CONTEXT_VARS) {
|
|
72
|
-
const value = contextVarValue(name,
|
|
79
|
+
const value = contextVarValue(name, seen);
|
|
73
80
|
if (value !== undefined) {
|
|
74
81
|
values.set(name, value);
|
|
75
82
|
continue;
|
|
@@ -79,8 +86,8 @@ export async function planCommandSpawn(entry, cwd, requestEnv) {
|
|
|
79
86
|
return {
|
|
80
87
|
kind: "refused",
|
|
81
88
|
error: p !== undefined
|
|
82
|
-
? `hook command references ${ref} but only ${ROOT_REF} is available for this hook (it carries no plugin data directory and the
|
|
83
|
-
: `hook command references ${ref} but the hook is not associated with a plugin and the
|
|
89
|
+
? `hook command references ${ref} but only ${ROOT_REF} is available for this hook (it carries no plugin data directory and the hook environment does not set ${name}; a request can set it in settings.env)`
|
|
90
|
+
: `hook command references ${ref} but the hook is not associated with a plugin and the hook environment does not set ${name} (the variable is set for hooks that carry a plugin context; a request can set it in settings.env)`,
|
|
84
91
|
};
|
|
85
92
|
}
|
|
86
93
|
if (p !== undefined) {
|
|
@@ -533,6 +533,11 @@ export interface HookRunnerContext {
|
|
|
533
533
|
* `[]` = 没有层写名单(不限,与引擎外的参照实现「名单未设 ⇒ 不限」同读)。今天唯一的层来源是请求的 `settings`(`task-settings.ts` 的 `httpHookAllowlist`,源 `flagSettings`)。
|
|
534
534
|
*/
|
|
535
535
|
httpHookAllowlistLayers: readonly HttpHookAllowlistLayer[];
|
|
536
|
+
/**
|
|
537
|
+
* 部署方点名放回 hook 环境的服务进程变量名(`HOOK_ENV_ALLOW` 的生效名单,`config.hookEnvAllow`;读者 = {@link hookChildEnv} 的两个入口:命令腿 spawn 与 http 插值)。必填:每个装配点都要说 ——
|
|
538
|
+
* `[]` = 不放回(hook 环境 = 十二名基础名单 + 有效环境层)。这是服务进程变量进 hook 环境的**唯一**来源;请求层(条目 `allowedEnvVars`、`settings.httpHookAllowedEnvVars`)只收窄、不授予。
|
|
539
|
+
*/
|
|
540
|
+
hookEnvAllow: readonly string[];
|
|
536
541
|
/**
|
|
537
542
|
* 部署座在**任务座之下**挂了链成员的站点式样(引擎的准入记录读出,`boot/deployment-mods.ts` 的 `belowTaskChainPatternsOf`)。必填:每个装配点都要说 ——
|
|
538
543
|
* `[]` = 任务座之下没有部署座的链成员(部署注册只在任务座之上、只有观察者,或没有部署注册)。读者 = PreToolUse 翻译的只许收紧({@link chainsBelowTaskOn};请求钩子两条车道在这些站上只许收紧)。
|
|
@@ -36,12 +36,15 @@ const MIN_HOOK_CONTEXT_PARTIAL_CHARS = 256;
|
|
|
36
36
|
const MAX_TOOL_RESPONSE_CHARS = 32_768;
|
|
37
37
|
const MAX_TOOL_INPUT_CHARS = 262_144;
|
|
38
38
|
const HOOK_ENV_ALLOWLIST = ["PATH", "HOME", "SHELL", "USER", "LOGNAME", "TERM", "TZ", "LANG", "LC_ALL", "LC_CTYPE", "LC_MESSAGES", "TMPDIR"];
|
|
39
|
-
function hookChildEnv(layer, spawning) {
|
|
40
|
-
const o = { policy: [...HOOK_ENV_ALLOWLIST], allow:
|
|
39
|
+
function hookChildEnv(layer, putBack, spawning) {
|
|
40
|
+
const o = { policy: [...HOOK_ENV_ALLOWLIST], allow: putBack, grants: [layer] };
|
|
41
41
|
return spawning ? hostSubprocessEnv({ site: "server.hooks.spawn-env", ...o }) : composeHostSubprocessEnv(o);
|
|
42
42
|
}
|
|
43
|
+
function hookChildView(ctx) {
|
|
44
|
+
return (layer) => hookChildEnv(layer, ctx.hookEnvAllow, false);
|
|
45
|
+
}
|
|
43
46
|
function hookEnv(ctx) {
|
|
44
|
-
return
|
|
47
|
+
return hookChildView(ctx)(composeHookEnvLayer(ctx.shellEnv, ctx.cwd, undefined));
|
|
45
48
|
}
|
|
46
49
|
export const SETTINGS_HOOK_STATIONS = {
|
|
47
50
|
PreToolUse: "classic.PreToolUse",
|
|
@@ -861,7 +864,7 @@ function readBelowPreToolUse(below) {
|
|
|
861
864
|
};
|
|
862
865
|
}
|
|
863
866
|
function spawnPlannedCommand(plan, ctx, stdio) {
|
|
864
|
-
const env = hookChildEnv(plan.env, true);
|
|
867
|
+
const env = hookChildEnv(plan.env, ctx.hookEnvAllow, true);
|
|
865
868
|
if (plan.kind === "exec")
|
|
866
869
|
return spawn(plan.file, [...plan.args], { cwd: ctx.cwd, env, stdio, detached: true });
|
|
867
870
|
const sh = hostShell();
|
|
@@ -926,7 +929,7 @@ function createHookTreeLadder(child, ctx, event) {
|
|
|
926
929
|
};
|
|
927
930
|
}
|
|
928
931
|
async function runCommandHook(entry, payload, ctx, halt, event) {
|
|
929
|
-
const plan = await planCommandSpawn(entry, ctx.cwd, ctx.shellEnv);
|
|
932
|
+
const plan = await planCommandSpawn(entry, ctx.cwd, ctx.shellEnv, hookChildView(ctx));
|
|
930
933
|
if (plan.kind === "refused")
|
|
931
934
|
return { code: null, stdout: "", stderr: "", timedOut: false, spawnError: plan.error };
|
|
932
935
|
if (plan.kind === "shell") {
|
|
@@ -1011,7 +1014,7 @@ async function fireAsyncCommandHook(entry, payload, ctx, event, halt) {
|
|
|
1011
1014
|
try {
|
|
1012
1015
|
const rewake = entry.asyncRewake === true;
|
|
1013
1016
|
const timeoutMs = Math.min(entry.timeout ?? DEFAULT_HOOK_TIMEOUT_SECONDS, MAX_HOOK_TIMEOUT_SECONDS) * 1000;
|
|
1014
|
-
const plan = await planCommandSpawn(entry, ctx.cwd, ctx.shellEnv);
|
|
1017
|
+
const plan = await planCommandSpawn(entry, ctx.cwd, ctx.shellEnv, hookChildView(ctx));
|
|
1015
1018
|
if (plan.kind === "refused")
|
|
1016
1019
|
throw new Error(plan.error);
|
|
1017
1020
|
if (plan.kind === "shell")
|
|
@@ -1740,7 +1743,8 @@ function createLaneRegistration(lane, config, ctx, budgetMs) {
|
|
|
1740
1743
|
asked = alsoAsks(lane, prior.asked ?? prior.unreadable?.message, clipDataText(typeof h.permissionDecisionReason === "string" ? h.permissionDecisionReason : "a PreToolUse hook requires approval", MAX_HOOK_FEEDBACK_CHARS));
|
|
1741
1744
|
spoke = true;
|
|
1742
1745
|
}
|
|
1743
|
-
|
|
1746
|
+
const counted = decision === "allow" || decision === "ask" || decision === undefined;
|
|
1747
|
+
if (counted) {
|
|
1744
1748
|
if (ran.distrust !== undefined && hookLaneOf(ran.source) === "unmanaged" && (decision === "allow" || proposesRewrite)) {
|
|
1745
1749
|
noteVerdictUntrusted(ctx, { event: "PreToolUse", reason: ran.distrust, entry, source: ran.source, toolName });
|
|
1746
1750
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { type GovernancePosture } from "../runtime-governance.js";
|
|
17
17
|
import type { ShellGateDoctrine } from "@sema-agent/core";
|
|
18
|
-
import {
|
|
18
|
+
import type { RunStore } from "../plugins/store-backend.js";
|
|
19
19
|
import { type PendingRowBinding } from "../plugins/checkpoint-store-face.js";
|
|
20
20
|
/** 与 runs.ts/tasks.ts 三个 409 位共享的旧文案(byte-frozen:api-error-text-freeze 门认这句)。 */
|
|
21
21
|
export declare const ACTIVE_RUN_CONFLICT_BASE_TEXT = "session already has an active run \u2014 POST /v1/runs/{activeTaskId}/cancel stops it (same-instance interactive runs abort immediately)";
|
|
@@ -113,12 +113,8 @@ interface ConflictProbeDeps<TToken> {
|
|
|
113
113
|
/** 结构形入参,但 `status` 取 run 行的**闭词表**(合并码重扫):判 park 要走词表属主
|
|
114
114
|
* `isParkedRunStatus`,而它的入参是穷举联合 —— 这一格写成开放 `string` 就只能在调用点裸 cast,
|
|
115
115
|
* 绕开那道编译期执法(core 加一个 park 类成员时本文件不会红)。 */
|
|
116
|
-
/**
|
|
117
|
-
runStore?:
|
|
118
|
-
getRun?: (id: string) => Promise<{
|
|
119
|
-
status?: RunRecord["status"];
|
|
120
|
-
} | null | undefined>;
|
|
121
|
-
} | undefined;
|
|
116
|
+
/** 持有者那一行的状态(不带属主的一格事实,经属主存取器 `runFactsOf` 读;409 体只点名**活着**的持有者 —— 心跳过期的持有者在认领冲突处已被判死、认领已放,见 run 店 `createRun`)。 */
|
|
117
|
+
runStore?: Pick<RunStore, "getRun"> | undefined;
|
|
122
118
|
checkpointStore?: {
|
|
123
119
|
peekPendingScope?: (sessionId: string, binding?: PendingRowBinding) => Promise<string | null | undefined>;
|
|
124
120
|
findPendingTokenBySession?: (sessionId: string, scope?: string, binding?: PendingRowBinding) => Promise<TToken | null | undefined>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { governanceOriginOf } from "../runtime-governance.js";
|
|
2
2
|
import { isParkedRunStatus } from "../plugins/store-contracts.js";
|
|
3
|
+
import { runFactsOf } from "./owner-access.js";
|
|
3
4
|
import { LIVE_RUN_GATE_KINDS } from "../plugins/checkpoint-store-face.js";
|
|
4
5
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
5
6
|
import { refusalBody } from "./refusal-body.js";
|
|
@@ -46,8 +47,7 @@ export async function buildActiveRunConflict(deps, sessionId, activeTaskId) {
|
|
|
46
47
|
if (!activeTaskId)
|
|
47
48
|
return base;
|
|
48
49
|
try {
|
|
49
|
-
const
|
|
50
|
-
const status = row?.status;
|
|
50
|
+
const status = deps.runStore === undefined ? undefined : (await runFactsOf(deps.runStore, activeTaskId))?.status;
|
|
51
51
|
if (status === undefined || (!isParkedRunStatus(status) && status !== "running"))
|
|
52
52
|
return base;
|
|
53
53
|
if (status === "running") {
|
package/dist/http/admission.js
CHANGED
|
@@ -2,7 +2,7 @@ import { APPROVAL_REACH, IMAGE_ADMISSION, isApprovalReach, isPermissionMode, isT
|
|
|
2
2
|
import { isAtMentionable, matchCatalogModel, refusedCatalogEntry, selectableCatalog } from "../model-select.js";
|
|
3
3
|
import { echoModelRef, modelUnavailableMessage } from "../model-entry-refusal.js";
|
|
4
4
|
import { HttpError, sessionIdShapeIssue } from "../security.js";
|
|
5
|
-
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS, PERMISSION_MODE_WORDS, autoCompactEnabledOk, parseTaskSettings, readSettingsEnv, settingsEnvComposeIssue, settingsEnvCredentialIssue, settingsPermissionBucketIssue, skillListingBudgetFractionOk, skillListingMaxDescOk, hooksKeyIssue, taskHooksHonored, taskSettingsKeyIssue } from "../task-settings.js";
|
|
5
|
+
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, appendFold, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS, PERMISSION_MODE_WORDS, autoCompactEnabledOk, parseTaskSettings, readSettingsEnv, settingsEnvComposeIssue, settingsEnvCredentialIssue, settingsPermissionBucketIssue, skillListingBudgetFractionOk, skillListingMaxDescOk, hooksKeyIssue, taskHooksHonored, taskSettingsKeyIssue } from "../task-settings.js";
|
|
6
6
|
import { serverCredentialValueSources } from "../server-secret-env.js";
|
|
7
7
|
import { tightenOnlySeatIssue } from "../tighten-only-seats.js";
|
|
8
8
|
import { approvalFrameFamiliesIssue } from "../tool-approval.js";
|
|
@@ -25,7 +25,7 @@ import { legFactsOf } from "../leg-facts.js";
|
|
|
25
25
|
import { createSubmissionSeat } from "../submission-exit.js";
|
|
26
26
|
import { assertRequestFormBeforeMint } from "../request-form.js";
|
|
27
27
|
import { headerStr } from "./principal-gate.js";
|
|
28
|
-
import {
|
|
28
|
+
import { SUBMISSION_CLAIM_FACE, resolveBeforeClaim, stampSpecRoot } from "./execution-context.js";
|
|
29
29
|
import { callerTrust, requestTrustRefusal } from "./caller-trust.js";
|
|
30
30
|
import { createRunAcceptance } from "../run-acceptance.js";
|
|
31
31
|
import { promptScreenDeclared } from "../prompt-screen.js";
|
|
@@ -214,7 +214,7 @@ export function createAdmission(ctx) {
|
|
|
214
214
|
sendError(res, 400, "request.field_invalid", `settings.outputStyle must be at most ${MAX_SETTINGS_OUTPUT_STYLE_CHARS} characters`, limitFacts("settings.outputStyle", "characters", st.outputStyle.length, MAX_SETTINGS_OUTPUT_STYLE_CHARS));
|
|
215
215
|
return null;
|
|
216
216
|
}
|
|
217
|
-
const foldedChars = typeof st.outputStyle === "string" && st.outputStyle.length > 0 && typeof body.appendSystemPrompt === "string" ? body.appendSystemPrompt
|
|
217
|
+
const foldedChars = typeof st.outputStyle === "string" && st.outputStyle.length > 0 && typeof body.appendSystemPrompt === "string" ? appendFold([body.appendSystemPrompt, st.outputStyle]).chars : 0;
|
|
218
218
|
if (foldedChars > MAX_SYSTEM_PROMPT_CHARS) {
|
|
219
219
|
sendError(res, 400, "request.field_conflict", `appendSystemPrompt + settings.outputStyle fold into one system-prompt block — combined they must be at most ${MAX_SYSTEM_PROMPT_CHARS} characters (including the 2-char joiner)`, fieldConflictFacts("system_prompt_fold", ["appendSystemPrompt", "settings.outputStyle"], { count: foldedChars, cap: MAX_SYSTEM_PROMPT_CHARS }));
|
|
220
220
|
return null;
|
|
@@ -749,12 +749,12 @@ export function createAdmission(ctx) {
|
|
|
749
749
|
const seat = createSubmissionSeat({ deployment: deps.config, metrics: deps.metrics, dropSessionScopedOf: deps.dropSessionScopedOf, sessionWatch: deps.sessionWatch });
|
|
750
750
|
try {
|
|
751
751
|
const auth = await deps.authorize({ req, body: admitted, onBirth: (birth, face) => seat.noteBirth(birth, face) });
|
|
752
|
-
const execution = await
|
|
752
|
+
const execution = await resolveBeforeClaim(deps, { kind: "fresh", root: auth.sessionId, principal: auth.subject === undefined ? undefined : personOf(recoveredSubject(auth.subject)), judged: auth.head, req, deviceId: admitted.deviceId }, SUBMISSION_CLAIM_FACE);
|
|
753
753
|
const engineNotices = [];
|
|
754
754
|
const resolved = await deps.resolveSpec(admitted, req, auth, { leg: "fresh", ...(legRoute?.liveStream === true ? { liveStream: true } : {}), onNotice: (n) => engineNotices.push(n), lease, execution });
|
|
755
755
|
const facts = legFactsOf(resolved);
|
|
756
756
|
const runner = runnerFor(resolved);
|
|
757
|
-
const spec = stampSpecRoot(resolved, execution, admitted);
|
|
757
|
+
const spec = stampSpecRoot(resolved, execution, cascade ? { use: "ladder_host" } : { use: "session", requireExistingSession: admitted.requireExistingSession });
|
|
758
758
|
const model = modelIdOf(spec.model);
|
|
759
759
|
const acceptance = createRunAcceptance({ sessionTitler: deps.sessionTitler }, { sessionId: spec.sessionId, objective: spec.objective, screened: promptScreenDeclared([deps.deploymentMods, spec.mods]), ...(model !== undefined ? { model } : {}) });
|
|
760
760
|
seat.attachAcceptance(acceptance);
|