@sema-agent/server 7.150.0 → 7.151.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 +185 -0
- package/USAGE.md +53 -25
- package/deploy/sema-up/README.md +7 -1
- package/deploy/sema-up/chart/Chart.yaml +1 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +5 -0
- package/deploy/sema-up/chart/templates/_helpers.tpl +15 -16
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -3
- package/deploy/sema-up/chart/templates/server.yaml +5 -3
- package/deploy/sema-up/chart/values-full-single.yaml +3 -1
- package/deploy/sema-up/chart/values.yaml +8 -1
- package/deploy/sema-up/cluster-up.sh +1 -0
- package/deploy/sema-up/kube-up.sh +1 -1
- package/deploy/sema-up/lib/flagshape.sh +1 -1
- package/deploy/sema-up/lib/helm-values-cluster.py +2 -0
- package/deploy/sema-up/lib/helm-values-kube.py +2 -0
- package/dist/adoption/import.js +3 -3
- package/dist/approval-ask-machine.d.ts +1 -1
- package/dist/approval-card.d.ts +2 -2
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/auth-bridge.d.ts +20 -0
- package/dist/auth-bridge.js +9 -0
- package/dist/background-session-reap.d.ts +1 -1
- package/dist/bake-runner/main.d.ts +4 -2
- package/dist/bind-host.d.ts +2 -3
- package/dist/bind-host.js +2 -4
- package/dist/boot/budget-tracing.js +1 -1
- package/dist/boot/engine-lease.d.ts +5 -0
- package/dist/boot/engine-lease.js +1 -0
- package/dist/boot/execution-env.d.ts +8 -14
- package/dist/boot/execution-env.js +54 -17
- package/dist/boot/execution-lane.js +32 -5
- package/dist/boot/late-binds.d.ts +8 -4
- package/dist/boot/late-binds.js +7 -1
- package/dist/boot/memory-consolidation.d.ts +3 -2
- package/dist/boot/memory-consolidation.js +6 -6
- package/dist/boot/reapers.d.ts +5 -0
- package/dist/boot/reapers.js +12 -1
- package/dist/boot/resolve-spec.d.ts +3 -3
- package/dist/boot/resolve-spec.js +31 -22
- package/dist/boot/runner-deps.d.ts +9 -2
- package/dist/boot/runner-deps.js +3 -3
- package/dist/boot/session-age-out.d.ts +36 -0
- package/dist/boot/session-age-out.js +101 -0
- package/dist/boot/session-faces.d.ts +5 -14
- package/dist/boot/session-faces.js +9 -5
- package/dist/boot/stage-01-config.js +2 -0
- package/dist/boot/stage-05-execution-env.d.ts +5 -3
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +7 -4
- package/dist/boot/stage-06-runners.js +15 -6
- package/dist/boot/stage-07-capability-layer.d.ts +7 -4
- package/dist/boot/stage-07-capability-layer.js +2 -1
- package/dist/boot/stage-08-reapers.d.ts +7 -4
- package/dist/boot/stage-08-reapers.js +4 -2
- package/dist/boot/stage-09-leader.d.ts +11 -5
- package/dist/boot/stage-09-leader.js +12 -3
- package/dist/boot/stage-10-http-server.d.ts +11 -5
- package/dist/boot/stage-10-http-server.js +3 -6
- package/dist/boot/web-admin.js +3 -1
- package/dist/boot/workflow-orchestration.d.ts +7 -2
- package/dist/boot/workflow-orchestration.js +24 -7
- package/dist/bounded-rotation.d.ts +29 -0
- package/dist/bounded-rotation.js +24 -0
- package/dist/capability-facts.d.ts +22 -13
- package/dist/capability-facts.js +28 -18
- package/dist/center-credential.d.ts +25 -6
- package/dist/center-credential.js +25 -5
- package/dist/config-catalog.d.ts +26 -7
- package/dist/config-catalog.js +14 -11
- package/dist/config-invariants.d.ts +2 -1
- package/dist/config-invariants.js +4 -4
- package/dist/config-types.d.ts +10 -4
- package/dist/config.d.ts +51 -2
- package/dist/config.js +35 -24
- package/dist/deployment-shape.d.ts +9 -1
- package/dist/deployment-shape.js +2 -1
- package/dist/device-protocol.d.ts +5 -4
- package/dist/device-protocol.js +1 -1
- package/dist/device-ws-hub.js +3 -2
- package/dist/device-ws-protocol.d.ts +63 -10
- package/dist/device-ws-protocol.js +56 -25
- package/dist/engine-identity.d.ts +19 -7
- package/dist/engine-identity.js +10 -2
- package/dist/entry-mode-clamp-notice.d.ts +2 -1
- package/dist/execution-env-refusal.d.ts +23 -0
- package/dist/execution-env-refusal.js +5 -0
- package/dist/execution-lane-caps.d.ts +3 -1
- package/dist/field-conflict.d.ts +1 -1
- package/dist/file-history-capture.d.ts +11 -4
- package/dist/file-history-capture.js +9 -6
- package/dist/file-history-disclosure.d.ts +7 -5
- package/dist/file-history-disclosure.js +7 -5
- package/dist/fleet/drain-entry.d.ts +4 -0
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/host-lsp-manager.d.ts +2 -2
- package/dist/http/admission.js +17 -2
- package/dist/http/approval-sweeps.js +5 -5
- package/dist/http/checkpoint-ctx.d.ts +2 -0
- package/dist/http/checkpoint-ctx.js +1 -0
- package/dist/http/dispatch.d.ts +2 -1
- package/dist/http/dispatch.js +34 -27
- package/dist/http/durable-run-leg.js +16 -16
- package/dist/http/health-self-report.d.ts +9 -1
- package/dist/http/health-self-report.js +2 -0
- package/dist/http/owner-access.d.ts +31 -11
- package/dist/http/owner-access.js +37 -6
- package/dist/http/resume-ctx-read.js +25 -3
- package/dist/http/resume-legs.d.ts +2 -1
- package/dist/http/resume-legs.js +259 -79
- package/dist/http/route-ctx.d.ts +18 -8
- package/dist/http/route-table.d.ts +1 -1
- package/dist/http/routes/admin-memory-optout.d.ts +1 -1
- package/dist/http/routes/agents-roster.js +2 -3
- package/dist/http/routes/approvals-assistant.js +4 -3
- package/dist/http/routes/background.d.ts +3 -3
- package/dist/http/routes/capabilities.js +8 -5
- package/dist/http/routes/fleet.js +10 -8
- package/dist/http/routes/projects.d.ts +3 -2
- package/dist/http/routes/projects.js +4 -5
- package/dist/http/routes/runs.js +102 -36
- package/dist/http/routes/session-sync.d.ts +4 -3
- package/dist/http/routes/session-sync.js +172 -77
- package/dist/http/routes/sessions-list.js +13 -7
- package/dist/http/routes/sessions.js +10 -6
- package/dist/http/routes/tasks.js +30 -16
- package/dist/http/routes/trace-usage.js +2 -2
- package/dist/http/server.d.ts +21 -27
- package/dist/http/server.js +2 -2
- package/dist/http/submit-ctx.js +5 -4
- package/dist/http/wire-types.d.ts +14 -4
- package/dist/identity-source.d.ts +20 -3
- package/dist/identity-source.js +14 -0
- package/dist/index.d.ts +4 -0
- package/dist/lane-home-probe.d.ts +3 -7
- package/dist/lane-home-probe.js +0 -1
- package/dist/leader/leader.d.ts +2 -1
- package/dist/leg-facts.d.ts +13 -5
- package/dist/leg-facts.js +2 -1
- package/dist/memory-scope.d.ts +5 -4
- package/dist/memory-scope.js +4 -4
- package/dist/memory-sync-client.d.ts +30 -2
- package/dist/memory-sync-client.js +16 -9
- package/dist/observability/err-text.d.ts +7 -0
- package/dist/observability/err-text.js +5 -0
- package/dist/observability/fail-open.d.ts +7 -3
- package/dist/observability/fail-open.js +7 -3
- package/dist/observability/metrics.js +1 -0
- package/dist/orchestration/workflow-notify-journal.d.ts +18 -1
- package/dist/orchestration/workflow-notify-journal.js +12 -1
- package/dist/orchestration/workflow-park-decision.d.ts +70 -18
- package/dist/orchestration/workflow-park-decision.js +49 -10
- package/dist/orchestration/workflow-park-holder.d.ts +65 -0
- package/dist/orchestration/workflow-park-holder.js +98 -0
- package/dist/orchestration/workflow-record-fence.d.ts +36 -0
- package/dist/orchestration/workflow-record-fence.js +42 -0
- package/dist/owner-faces.d.ts +1 -1
- package/dist/parked-decide.d.ts +104 -53
- package/dist/parked-decide.js +153 -117
- package/dist/peer-directory.d.ts +2 -2
- package/dist/peer-inbound-face.d.ts +2 -6
- package/dist/peer-inbound-face.js +0 -3
- package/dist/permission-mode-admission.d.ts +41 -5
- package/dist/permission-mode-admission.js +29 -3
- package/dist/plugins/adoption-log-sql.d.ts +3 -1
- package/dist/plugins/adoption-log-sql.js +7 -1
- package/dist/plugins/approval-ask-store-file.d.ts +10 -2
- package/dist/plugins/approval-ask-store-file.js +8 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +6 -1
- package/dist/plugins/approval-ask-store-memory.js +28 -9
- package/dist/plugins/approval-ask-store-sql.d.ts +43 -2
- package/dist/plugins/approval-ask-store-sql.js +34 -5
- package/dist/plugins/approval-exemption-store.js +3 -0
- package/dist/plugins/approval-nonce-store.js +2 -0
- package/dist/plugins/breaker-state-sql.js +2 -0
- package/dist/plugins/caching-session-store.d.ts +9 -15
- package/dist/plugins/caching-session-store.js +6 -0
- package/dist/plugins/device-store-sql.js +6 -0
- package/dist/plugins/error-origin.d.ts +18 -0
- package/dist/plugins/error-origin.js +10 -0
- package/dist/plugins/execution-placement-sql.js +6 -6
- package/dist/plugins/image-bake-store-sql.js +6 -0
- package/dist/plugins/image-index-sql.js +4 -0
- package/dist/plugins/landed-attendants.d.ts +212 -0
- package/dist/plugins/landed-attendants.js +161 -0
- package/dist/plugins/leader-run-store-sql.js +4 -0
- package/dist/plugins/local-session-live-reference.d.ts +40 -0
- package/dist/plugins/local-session-live-reference.js +39 -0
- package/dist/plugins/local-session-store.d.ts +71 -33
- package/dist/plugins/local-session-store.js +129 -27
- package/dist/plugins/memory-engine-pg.js +4 -0
- package/dist/plugins/memory-engine-sql-apply.js +5 -2
- package/dist/plugins/memory-engine-tidb.js +4 -0
- package/dist/plugins/memory-optout-grant-store-sql.js +4 -0
- package/dist/plugins/memory-sync-store-pg.js +5 -0
- package/dist/plugins/memory-sync-store-tidb.js +5 -0
- package/dist/plugins/one-time-migrations.d.ts +57 -63
- package/dist/plugins/one-time-migrations.js +162 -90
- package/dist/plugins/org-delivery-quota-store.js +4 -1
- package/dist/plugins/outcome-ledger-sql.js +2 -0
- package/dist/plugins/permission-rule-store-sql.js +11 -4
- package/dist/plugins/pg-cost-quota.js +4 -0
- package/dist/plugins/pg-pool.js +24 -1
- package/dist/plugins/pg-rate-limiter.js +2 -0
- package/dist/plugins/posix-shell-fs.d.ts +6 -1
- package/dist/plugins/posix-shell-fs.js +4 -1
- package/dist/plugins/remote-env-adb.d.ts +19 -15
- package/dist/plugins/remote-env-adb.js +41 -15
- package/dist/plugins/remote-env-connect-seat.d.ts +105 -0
- package/dist/plugins/remote-env-connect-seat.js +72 -0
- package/dist/plugins/remote-env-device.d.ts +22 -5
- package/dist/plugins/remote-env-device.js +106 -8
- package/dist/plugins/remote-env-e2b.d.ts +15 -6
- package/dist/plugins/remote-env-e2b.js +42 -32
- package/dist/plugins/remote-env-file-error.js +6 -2
- package/dist/plugins/remote-env-host.d.ts +17 -0
- package/dist/plugins/remote-env-host.js +30 -38
- package/dist/plugins/remote-env-k8s.d.ts +41 -39
- package/dist/plugins/remote-env-k8s.js +53 -30
- package/dist/plugins/remote-env-local-docker.d.ts +13 -8
- package/dist/plugins/remote-env-local-docker.js +34 -41
- package/dist/plugins/remote-env-ssh.d.ts +18 -12
- package/dist/plugins/remote-env-ssh.js +60 -59
- package/dist/plugins/remote-shell.d.ts +98 -1
- package/dist/plugins/remote-shell.js +218 -0
- package/dist/plugins/resume-anchor-store-sql.d.ts +11 -2
- package/dist/plugins/resume-anchor-store-sql.js +12 -5
- package/dist/plugins/retention-lane-store-sql.js +4 -0
- package/dist/plugins/retention-sql.d.ts +20 -2
- package/dist/plugins/retention-sql.js +11 -0
- package/dist/plugins/run-store-sql.d.ts +21 -2
- package/dist/plugins/run-store-sql.js +31 -7
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +1 -1
- package/dist/plugins/scenario-prefix-switch-m42.js +2 -2
- package/dist/plugins/schema-consistency.d.ts +4 -2
- package/dist/plugins/schema-consistency.js +19 -2
- package/dist/plugins/send-file-ledger.js +3 -0
- package/dist/plugins/serving-requirement.d.ts +26 -7
- package/dist/plugins/serving-requirement.js +11 -4
- package/dist/plugins/session-birth.d.ts +6 -2
- package/dist/plugins/session-capture-record-store-sql.js +2 -0
- package/dist/plugins/session-ext-table.d.ts +14 -1
- package/dist/plugins/session-ext-table.js +5 -1
- package/dist/plugins/session-row-sql.d.ts +49 -2
- package/dist/plugins/session-row-sql.js +56 -13
- package/dist/plugins/session-store-sql.d.ts +34 -18
- package/dist/plugins/session-store-sql.js +98 -17
- package/dist/plugins/shared-memory-store-sql.d.ts +1 -0
- package/dist/plugins/shared-memory-store-sql.js +6 -2
- package/dist/plugins/sql-driver.d.ts +2 -1
- package/dist/plugins/sql-driver.js +18 -3
- package/dist/plugins/sql-errors.d.ts +11 -6
- package/dist/plugins/sql-errors.js +5 -6
- package/dist/plugins/stop-then-start-sentence.d.ts +10 -4
- package/dist/plugins/stop-then-start-sentence.js +14 -10
- package/dist/plugins/stop-then-start.d.ts +109 -0
- package/dist/plugins/stop-then-start.js +104 -0
- package/dist/plugins/store-backend.d.ts +40 -13
- package/dist/plugins/store-backend.js +169 -27
- package/dist/plugins/store-boundary.d.ts +57 -0
- package/dist/plugins/store-boundary.js +139 -0
- package/dist/plugins/store-contracts.d.ts +17 -0
- package/dist/plugins/store-fault-sentence-m53.d.ts +7 -0
- package/dist/plugins/store-fault-sentence-m53.js +25 -0
- package/dist/plugins/store-fault.d.ts +26 -7
- package/dist/plugins/store-fault.js +18 -2
- package/dist/plugins/task-attachment-store.js +9 -0
- package/dist/plugins/task-list-store-sql.d.ts +2 -2
- package/dist/plugins/task-list-store-sql.js +10 -8
- package/dist/plugins/tidb-cost-quota.js +4 -0
- package/dist/plugins/tidb-pool.js +2 -1
- package/dist/plugins/tidb-rate-limiter.js +4 -0
- package/dist/plugins/user-input-ledger-sql.js +4 -0
- package/dist/plugins/workflow-run-store-sql.d.ts +28 -10
- package/dist/plugins/workflow-run-store-sql.js +64 -4
- package/dist/plugins/workspace-fill.d.ts +4 -7
- package/dist/plugins/workspace-fill.js +2 -1
- package/dist/plugins/write-behind-counter.js +5 -0
- package/dist/project-context.d.ts +0 -2
- package/dist/project-context.js +4 -8
- package/dist/project-registry.d.ts +46 -0
- package/dist/project-registry.js +44 -0
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +4 -0
- package/dist/request-value-closure.js +10 -0
- package/dist/run-acceptance.d.ts +3 -2
- package/dist/run-acceptance.js +4 -6
- package/dist/run-local.js +1 -0
- package/dist/runs.d.ts +37 -8
- package/dist/runs.js +53 -12
- package/dist/runtime-governance.d.ts +5 -4
- package/dist/security.d.ts +30 -32
- package/dist/security.js +41 -7
- package/dist/server-secret-env.d.ts +12 -10
- package/dist/server-secret-env.js +24 -15
- package/dist/session-bindings.d.ts +82 -0
- package/dist/session-bindings.js +18 -0
- package/dist/session-children-retire.d.ts +2 -2
- package/dist/session-children-retire.js +2 -6
- package/dist/session-deletion.d.ts +16 -1
- package/dist/session-error-wire.d.ts +3 -3
- package/dist/session-error-wire.js +3 -3
- package/dist/session-incarnation.d.ts +10 -0
- package/dist/session-incarnation.js +10 -1
- package/dist/session-kind.d.ts +57 -0
- package/dist/session-kind.js +22 -0
- package/dist/session-row.d.ts +21 -1
- package/dist/session-row.js +7 -1
- package/dist/session-sync-kernel.d.ts +24 -14
- package/dist/session-sync-kernel.js +3 -6
- package/dist/session-sync.d.ts +4 -98
- package/dist/session-sync.js +1 -71
- package/dist/session-titler.d.ts +10 -3
- package/dist/session-titler.js +19 -6
- package/dist/ssh-host-key.d.ts +5 -0
- package/dist/ssh-host-key.js +3 -0
- package/dist/stores-sql/file-history-store-sql.d.ts +52 -2
- package/dist/stores-sql/file-history-store-sql.js +108 -70
- package/dist/stores-sql/pg-session-storage.d.ts +34 -7
- package/dist/stores-sql/pg-session-storage.js +37 -13
- package/dist/stores-sql/retention-store-sql.d.ts +1 -9
- package/dist/stores-sql/retention-store-sql.js +2 -10
- package/dist/stores-sql/session-policy-store-sql.d.ts +30 -7
- package/dist/stores-sql/session-policy-store-sql.js +61 -44
- package/dist/stores-sql/session-write-hooks.d.ts +11 -0
- package/dist/stores-sql/tidb-session-store.d.ts +58 -7
- package/dist/stores-sql/tidb-session-store.js +37 -13
- package/dist/submission-exit.d.ts +3 -2
- package/dist/submission-exit.js +9 -1
- package/dist/task-cwd.d.ts +2 -2
- package/dist/task-settings.d.ts +6 -5
- package/dist/terminal.d.ts +33 -2
- package/dist/terminal.js +139 -9
- package/dist/tool-approval.d.ts +140 -6
- package/dist/tool-approval.js +165 -72
- package/dist/trace/self-minted-wire.d.ts +290 -27
- package/dist/trace/self-minted-wire.js +101 -13
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +78 -27
- package/package.json +2 -2
- package/wire-changes/server@7.151.0.json +762 -0
- package/dist/http/session-inherit-env.d.ts +0 -36
- package/dist/http/session-inherit-env.js +0 -6
package/dist/tool-approval.js
CHANGED
|
@@ -27,6 +27,7 @@ import { APPROVAL_EXEMPTED_EVENT } from "./plugins/approval-exemption-store.js";
|
|
|
27
27
|
import { refusalBody } from "./http/refusal-body.js";
|
|
28
28
|
import { withoutSettlementEvidence } from "./gate-settlement.js";
|
|
29
29
|
import { extensionRequesterOf } from "./trace/extension-call-face.js";
|
|
30
|
+
import { boundedRotation } from "./bounded-rotation.js";
|
|
30
31
|
const defaultLogger = createLogger();
|
|
31
32
|
const MAX_APPROVAL_ARGS_BYTES = 16384;
|
|
32
33
|
const DEFAULT_APPROVAL_TTL_MS = 5 * 60_000;
|
|
@@ -69,7 +70,7 @@ function durableRuleMaterial(material) {
|
|
|
69
70
|
}
|
|
70
71
|
export const NOT_PENDING_UNKNOWN_CAUSE = "unknown_or_other_replica";
|
|
71
72
|
export const TOOL_APPROVAL_READ_STATES = ["pending", "decided", "parked", "void"];
|
|
72
|
-
function decidedRowFacts(row) {
|
|
73
|
+
export function decidedRowFacts(row) {
|
|
73
74
|
if (row.state !== "DECIDED" || (row.decision !== "approve" && row.decision !== "deny") || row.provisional || row.decidedAtMs === null)
|
|
74
75
|
return undefined;
|
|
75
76
|
return { decision: row.decision === "approve" ? "allow" : "deny", decidedAtMs: row.decidedAtMs, ...(row.decisionNote !== null ? { decisionNote: row.decisionNote } : {}) };
|
|
@@ -85,6 +86,22 @@ function buildAskRowReadProjection(row, nowMs) {
|
|
|
85
86
|
};
|
|
86
87
|
}
|
|
87
88
|
export const ASK_DECISION_CONSUMER_ABSENT = "absent";
|
|
89
|
+
function standingParkedDecision(gate, row, idempotencyKey) {
|
|
90
|
+
if (gate?.kind !== "decided")
|
|
91
|
+
return undefined;
|
|
92
|
+
const decision = gate.decision === "allow" ? "approve" : "deny";
|
|
93
|
+
if (row.decision === decision && row.decidedAtMs !== null && row.decidedAtMs >= gate.decidedAtMs) {
|
|
94
|
+
return {
|
|
95
|
+
kind: "standing",
|
|
96
|
+
decision,
|
|
97
|
+
decidedAtMs: row.decidedAtMs,
|
|
98
|
+
...(row.decisionNote !== null ? { decisionNote: row.decisionNote } : {}),
|
|
99
|
+
...(row.decisionActor !== null ? { actor: row.decisionActor } : {}),
|
|
100
|
+
byThisRequest: idempotencyKey !== undefined && row.idempotencyKey === idempotencyKey,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return { kind: "standing", decision, decidedAtMs: gate.decidedAtMs, ...(gate.decisionNote !== undefined ? { decisionNote: gate.decisionNote } : {}), byThisRequest: false };
|
|
104
|
+
}
|
|
88
105
|
const MAX_INFLIGHT_TERMINAL_CLAIMS = 2;
|
|
89
106
|
const DURABLE_CONVERGE_BACKOFF_MS = [50, 100, 200, 400, 800, 1600];
|
|
90
107
|
export const DURABLE_CONVERGE_READ_TIMEOUT_MS = 2_000;
|
|
@@ -378,6 +395,18 @@ function parseToolApprovalFields(body, retired) {
|
|
|
378
395
|
}
|
|
379
396
|
return { ok: false, error: 'decision must be one of "allow" | "allow_session" | "deny"' };
|
|
380
397
|
}
|
|
398
|
+
export function askDecisionRecordOf(write) {
|
|
399
|
+
return {
|
|
400
|
+
decision: write.decision,
|
|
401
|
+
...(write.actor !== undefined ? { decisionActor: write.actor } : {}),
|
|
402
|
+
decisionNote: write.note ?? null,
|
|
403
|
+
idempotencyKey: write.idempotencyKey ?? null,
|
|
404
|
+
...liveHostSource(write.decider),
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function liveDecideInputOf(write) {
|
|
408
|
+
return { ...askDecisionRecordOf(write), ...(write.decision === "approve" && write.updatedInput !== undefined ? { updatedInput: write.updatedInput } : {}) };
|
|
409
|
+
}
|
|
381
410
|
export function buildUnattendedPolicyRefusal(reason) {
|
|
382
411
|
return { allow: false, settledBy: POLICY_REFUSAL_DECISION.settledBy, reason };
|
|
383
412
|
}
|
|
@@ -562,8 +591,8 @@ export class ToolApprovalCoordinator {
|
|
|
562
591
|
admitMaxPerOwner;
|
|
563
592
|
admitByTask = new Map();
|
|
564
593
|
admitByOwner = new Map();
|
|
565
|
-
|
|
566
|
-
|
|
594
|
+
suspendedPollCursor = { offset: 0 };
|
|
595
|
+
adoptedPollCursor = { offset: 0 };
|
|
567
596
|
editedDecisionsInFlight = new Map();
|
|
568
597
|
storeErrorWarned = false;
|
|
569
598
|
storeErrorTally = 0;
|
|
@@ -2460,21 +2489,50 @@ export class ToolApprovalCoordinator {
|
|
|
2460
2489
|
const openGenerationNow = this.lookupParkRedemption(row.askId) !== undefined ? await this.openGateGeneration(row) : undefined;
|
|
2461
2490
|
const prior = this.lookupParkRedemption(row.askId);
|
|
2462
2491
|
if (prior?.redeemed !== undefined && receiptAnswersNow(prior.redeemed.gateGeneration, openGenerationNow))
|
|
2463
|
-
return this.replayRedeemReceipt(id, prior, row, prior.redeemed, parsed);
|
|
2492
|
+
return this.replayRedeemReceipt(id, prior, row, prior.redeemed, parsed, row);
|
|
2464
2493
|
if (prior?.inflight !== undefined) {
|
|
2465
2494
|
const shared = await prior.inflight;
|
|
2466
2495
|
if (shared.status < 200 || shared.status >= 300)
|
|
2467
2496
|
return shared;
|
|
2468
2497
|
if (!receiptAnswersNow(shared.gateGeneration, openGenerationNow))
|
|
2469
2498
|
return this.redeemParkedAsk(id, row, parsed, httpReq, principal);
|
|
2470
|
-
return this.replayRedeemReceipt(id, prior, row, { decision: shared.decision, body: shared.body, gateGeneration: shared.gateGeneration, ...(shared.rule?.terminal === true ? { rule: shared.rule.body } : {}) }, parsed);
|
|
2499
|
+
return this.replayRedeemReceipt(id, prior, row, { decision: shared.decision, body: shared.body, gateGeneration: shared.gateGeneration, ...(shared.rule?.terminal === true ? { rule: shared.rule.body } : {}) }, parsed, shared.recorded ?? { decisionNote: null });
|
|
2471
2500
|
}
|
|
2501
|
+
const store = this.askStore;
|
|
2502
|
+
if (store === undefined)
|
|
2503
|
+
return this.notPending();
|
|
2504
|
+
const request = {
|
|
2505
|
+
write: { decision: parsed.value === "deny" ? "deny" : "approve", decider: parsed.decider, ...(parsed.note !== undefined ? { note: parsed.note } : {}) },
|
|
2506
|
+
...(parsed.value === "allow_session" ? { remember: "session" } : {}),
|
|
2507
|
+
...(parsed.persistRule !== undefined ? { persistRule: parsed.persistRule } : {}),
|
|
2508
|
+
...(parsed.updatedInput !== undefined && parsed.value !== "deny" ? { updatedInput: parsed.updatedInput } : {}),
|
|
2509
|
+
...(principal !== undefined ? { principal } : {}),
|
|
2510
|
+
operator: false,
|
|
2511
|
+
...(httpReq !== undefined ? { httpReq } : {}),
|
|
2512
|
+
};
|
|
2513
|
+
const out = await this.runParkedFlight(redeem, store, row, row.gateToken, request);
|
|
2514
|
+
if (out.status < 200 || out.status >= 300)
|
|
2515
|
+
return { status: out.status, body: out.body };
|
|
2516
|
+
const rule = out.rule;
|
|
2517
|
+
return {
|
|
2518
|
+
status: out.status,
|
|
2519
|
+
body: {
|
|
2520
|
+
...out.body,
|
|
2521
|
+
approvalId: id,
|
|
2522
|
+
askId: row.askId,
|
|
2523
|
+
accepted: "parked_redeemed",
|
|
2524
|
+
...(parsed.value === "allow_session" ? { rememberApplied: out.rememberApplied === true } : {}),
|
|
2525
|
+
...noteRecordedReceipt(parsed.note, noteLandedOnRow(parsed.note, out.recorded?.decisionNote ?? null)),
|
|
2526
|
+
...(rule?.body ?? {}),
|
|
2527
|
+
},
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
async runParkedFlight(redeem, store, row, gateToken, request) {
|
|
2531
|
+
const decision = request.write.decision;
|
|
2472
2532
|
const admission = this.parkRedemptionAdmission(row.owner);
|
|
2473
2533
|
if (admission !== undefined)
|
|
2474
|
-
return admission;
|
|
2534
|
+
return { ...admission, body: admission.body, decision };
|
|
2475
2535
|
const memo = this.openParkRedemption(row.askId, row.owner);
|
|
2476
|
-
const decision = parsed.value === "deny" ? "deny" : "approve";
|
|
2477
|
-
const gateToken = row.gateToken;
|
|
2478
2536
|
const flight = (async () => {
|
|
2479
2537
|
let res;
|
|
2480
2538
|
try {
|
|
@@ -2484,12 +2542,13 @@ export class ToolApprovalCoordinator {
|
|
|
2484
2542
|
gateBoundCallId: row.gateBoundCallId,
|
|
2485
2543
|
gateBoundInputHash: row.gateBoundInputHash,
|
|
2486
2544
|
decision,
|
|
2487
|
-
...(
|
|
2488
|
-
decider:
|
|
2489
|
-
...(
|
|
2490
|
-
...(
|
|
2491
|
-
...(principal !== undefined ? { principal } : {}),
|
|
2492
|
-
|
|
2545
|
+
...(request.write.note !== undefined ? { note: request.write.note } : {}),
|
|
2546
|
+
decider: request.write.decider,
|
|
2547
|
+
...(request.updatedInput !== undefined && decision === "approve" ? { updatedInput: request.updatedInput } : {}),
|
|
2548
|
+
...(request.remember !== undefined ? { remember: request.remember } : {}),
|
|
2549
|
+
...(request.principal !== undefined ? { principal: request.principal } : {}),
|
|
2550
|
+
operator: request.operator,
|
|
2551
|
+
...(request.httpReq !== undefined ? { httpReq: request.httpReq } : {}),
|
|
2493
2552
|
});
|
|
2494
2553
|
}
|
|
2495
2554
|
catch (err) {
|
|
@@ -2498,8 +2557,9 @@ export class ToolApprovalCoordinator {
|
|
|
2498
2557
|
}
|
|
2499
2558
|
if (res.status < 200 || res.status >= 300)
|
|
2500
2559
|
return { ...res, decision };
|
|
2501
|
-
const
|
|
2502
|
-
|
|
2560
|
+
const recorded = await this.recordParkedOutcome(store, row, request.write);
|
|
2561
|
+
const rule = await this.persistRuleFromRow(row, request.persistRule, request.updatedInput !== undefined);
|
|
2562
|
+
return { ...res, decision, ...(recorded !== undefined ? { recorded } : {}), ...(rule !== undefined ? { rule } : {}) };
|
|
2503
2563
|
})();
|
|
2504
2564
|
memo.inflight = flight;
|
|
2505
2565
|
let out;
|
|
@@ -2512,25 +2572,57 @@ export class ToolApprovalCoordinator {
|
|
|
2512
2572
|
if (out.status < 200 || out.status >= 300) {
|
|
2513
2573
|
if (this.parkRedemptions.get(row.askId) === memo)
|
|
2514
2574
|
this.parkRedemptions.delete(row.askId);
|
|
2515
|
-
return
|
|
2575
|
+
return out;
|
|
2516
2576
|
}
|
|
2517
|
-
const rule = out.rule;
|
|
2518
2577
|
if (this.retainParkRedemption(row.askId, memo))
|
|
2519
|
-
memo.redeemed = { decision, body: out.body, gateGeneration: out.gateGeneration, ...(rule?.terminal === true ? { rule: rule.body } : {}) };
|
|
2520
|
-
return
|
|
2521
|
-
status: out.status,
|
|
2522
|
-
body: {
|
|
2523
|
-
...out.body,
|
|
2524
|
-
approvalId: id,
|
|
2525
|
-
askId: row.askId,
|
|
2526
|
-
accepted: "parked_redeemed",
|
|
2527
|
-
...(parsed.value === "allow_session" ? { rememberApplied: out.rememberApplied === true } : {}),
|
|
2528
|
-
...(rule?.body ?? {}),
|
|
2529
|
-
},
|
|
2530
|
-
};
|
|
2578
|
+
memo.redeemed = { decision, body: out.body, gateGeneration: out.gateGeneration, ...(out.rule?.terminal === true ? { rule: out.rule.body } : {}) };
|
|
2579
|
+
return out;
|
|
2531
2580
|
}
|
|
2532
|
-
async
|
|
2533
|
-
|
|
2581
|
+
async recordParkedOutcome(store, row, write) {
|
|
2582
|
+
try {
|
|
2583
|
+
const out = await this.writeAskDecision(store, row.askId, { kind: "parked" }, row.owner, write);
|
|
2584
|
+
if (out.ok)
|
|
2585
|
+
return out.row;
|
|
2586
|
+
recordFailOpen("server.approval.parked-decision-not-recorded", `askId=${row.askId} reason=${out.reason}`);
|
|
2587
|
+
return undefined;
|
|
2588
|
+
}
|
|
2589
|
+
catch (err) {
|
|
2590
|
+
this.noteStoreError(err, "recordParkedDecision");
|
|
2591
|
+
recordFailOpen("server.approval.parked-decision-not-recorded", `askId=${row.askId} reason=store_error`);
|
|
2592
|
+
return undefined;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
async decideParkedAsk(store, row, request, idempotencyKey) {
|
|
2596
|
+
for (;;) {
|
|
2597
|
+
const gate = await this.readParkedGate(row);
|
|
2598
|
+
const current = await this.askRowFor(store, row.askId, "getAsk(parked-decision)");
|
|
2599
|
+
if (current === null || current.state !== "PARKED" || !askRowCarriesOwner(current, row.owner))
|
|
2600
|
+
return { kind: "gone" };
|
|
2601
|
+
const inflight = this.lookupParkRedemption(row.askId)?.inflight;
|
|
2602
|
+
if (inflight !== undefined) {
|
|
2603
|
+
await inflight;
|
|
2604
|
+
continue;
|
|
2605
|
+
}
|
|
2606
|
+
const standing = standingParkedDecision(gate, current, idempotencyKey);
|
|
2607
|
+
if (standing !== undefined)
|
|
2608
|
+
return standing;
|
|
2609
|
+
if (request.write.decision === "approve" && request.policyMayNotApprove === true)
|
|
2610
|
+
return { kind: "policy_refused" };
|
|
2611
|
+
const redeem = this.parkedRedeem?.();
|
|
2612
|
+
if (redeem === undefined || row.gateToken === null) {
|
|
2613
|
+
if (row.gateToken === null)
|
|
2614
|
+
defaultLogger.error("tool approval parked row carries no gate token — nothing to redeem", { askId: row.askId });
|
|
2615
|
+
return { kind: "gate_only" };
|
|
2616
|
+
}
|
|
2617
|
+
const out = await this.runParkedFlight(redeem, store, current, row.gateToken, request);
|
|
2618
|
+
if (out.status === 200)
|
|
2619
|
+
return { kind: "accepted", body: out.body, ...(out.recorded !== undefined ? { recorded: out.recorded } : {}) };
|
|
2620
|
+
if ((await this.readParkedGate(row))?.kind === "decided")
|
|
2621
|
+
continue;
|
|
2622
|
+
return { kind: "refused", status: out.status, body: out.body };
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
async persistRuleFromRow(row, persistRule, inputWasEdited) {
|
|
2534
2626
|
if (persistRule === undefined)
|
|
2535
2627
|
return undefined;
|
|
2536
2628
|
const flag = (rulePersisted, ruleRefusal, echo) => ({
|
|
@@ -2550,7 +2642,7 @@ export class ToolApprovalCoordinator {
|
|
|
2550
2642
|
const command = row.ruleCommand;
|
|
2551
2643
|
if (command === null)
|
|
2552
2644
|
return flag(false, "rule_material_absent");
|
|
2553
|
-
if (
|
|
2645
|
+
if (inputWasEdited)
|
|
2554
2646
|
return flag(false, "rule_input_edited");
|
|
2555
2647
|
const offers = card.ruleOffers ?? [];
|
|
2556
2648
|
const scope = row.ruleScopeRoot !== null ? { kind: "project", root: row.ruleScopeRoot } : undefined;
|
|
@@ -2607,10 +2699,10 @@ export class ToolApprovalCoordinator {
|
|
|
2607
2699
|
return { ...flag(false, "rule_store_error"), terminal: false };
|
|
2608
2700
|
}
|
|
2609
2701
|
}
|
|
2610
|
-
async replayRedeemReceipt(id, memo, row, receipt, parsed) {
|
|
2702
|
+
async replayRedeemReceipt(id, memo, row, receipt, parsed, recordedNote) {
|
|
2611
2703
|
let rule = receipt.decision === "approve" ? receipt.rule : this.lateRuleRefusal(parsed);
|
|
2612
2704
|
if (receipt.decision === "approve" && rule === undefined && parsed.persistRule !== undefined) {
|
|
2613
|
-
const flight = memo.ruleInflight ?? this.persistRuleFromRow(row, parsed);
|
|
2705
|
+
const flight = memo.ruleInflight ?? this.persistRuleFromRow(row, parsed.persistRule, parsed.updatedInput !== undefined);
|
|
2614
2706
|
const isOwner = memo.ruleInflight === undefined;
|
|
2615
2707
|
if (isOwner)
|
|
2616
2708
|
memo.ruleInflight = flight;
|
|
@@ -2636,6 +2728,7 @@ export class ToolApprovalCoordinator {
|
|
|
2636
2728
|
accepted: "parked_redeemed",
|
|
2637
2729
|
idempotent: true,
|
|
2638
2730
|
decision: receipt.decision === "approve" ? "allow" : "deny",
|
|
2731
|
+
...noteRecordedReceipt(parsed.note, noteLandedOnRow(parsed.note, recordedNote.decisionNote)),
|
|
2639
2732
|
...(rule ?? {}),
|
|
2640
2733
|
},
|
|
2641
2734
|
};
|
|
@@ -2719,19 +2812,36 @@ export class ToolApprovalCoordinator {
|
|
|
2719
2812
|
return withFlag(false, "rule_store_error");
|
|
2720
2813
|
}
|
|
2721
2814
|
}
|
|
2815
|
+
writeAskDecision(store, askId, form, owner, write) {
|
|
2816
|
+
switch (form.kind) {
|
|
2817
|
+
case "live":
|
|
2818
|
+
return this.withStoreDeadline(store.decideAsk(askId, form.batchId, owner, liveDecideInputOf(write)), "decideAsk", DURABLE_CALL_TIMEOUT_MS, (late) => {
|
|
2819
|
+
if (!late.ok)
|
|
2820
|
+
return;
|
|
2821
|
+
if (decidedRowFacts(late.row) === undefined) {
|
|
2822
|
+
defaultLogger.error("ask decision landed after its deadline on a row that is not an authoritative human decision — not settled", { askId, state: late.row.state, provisional: late.row.provisional });
|
|
2823
|
+
return;
|
|
2824
|
+
}
|
|
2825
|
+
this.notifyExternalDecision(askId, outcomeOfDecidedRow(late.row));
|
|
2826
|
+
});
|
|
2827
|
+
case "parked":
|
|
2828
|
+
return this.withStoreDeadline(store.recordParkedDecision(askId, owner, askDecisionRecordOf(write)), "recordParkedDecision", DURABLE_CALL_TIMEOUT_MS);
|
|
2829
|
+
default: {
|
|
2830
|
+
const unhandled = form;
|
|
2831
|
+
throw new Error(`writeAskDecision: unhandled form ${JSON.stringify(unhandled)}`);
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2722
2835
|
async respondWithCas(askStore, askId, batchId, owner, entry, parsed, ruleEligibility) {
|
|
2723
2836
|
let winnerRow;
|
|
2724
2837
|
let ambiguousConfirmedRow;
|
|
2725
2838
|
let noteLanded = false;
|
|
2726
2839
|
try {
|
|
2727
|
-
const decided = await this.
|
|
2840
|
+
const decided = await this.writeAskDecision(askStore, askId, { kind: "live", batchId }, owner, {
|
|
2728
2841
|
decision: parsed.value === "deny" ? "deny" : "approve",
|
|
2729
|
-
|
|
2730
|
-
...(parsed.
|
|
2731
|
-
...
|
|
2732
|
-
}), "decideAsk", DURABLE_CALL_TIMEOUT_MS, (late) => {
|
|
2733
|
-
if (late.ok)
|
|
2734
|
-
this.notifyExternalDecision(askId, outcomeOfDecidedRow(late.row));
|
|
2842
|
+
decider: parsed.decider,
|
|
2843
|
+
...(parsed.note !== undefined ? { note: parsed.note } : {}),
|
|
2844
|
+
...(parsed.updatedInput !== undefined ? { updatedInput: parsed.updatedInput } : {}),
|
|
2735
2845
|
});
|
|
2736
2846
|
if (decided.ok) {
|
|
2737
2847
|
winnerRow = decided.row;
|
|
@@ -3046,28 +3156,11 @@ export class ToolApprovalCoordinator {
|
|
|
3046
3156
|
continue;
|
|
3047
3157
|
seen.add(entry.askId);
|
|
3048
3158
|
}
|
|
3049
|
-
const waitingStart = waiting.length > 0 ? this.suspendedPollOffset % waiting.length : 0;
|
|
3050
|
-
const adoptedStart = adopted.length > 0 ? this.adoptedPollOffset % adopted.length : 0;
|
|
3051
|
-
const ordered = [
|
|
3052
|
-
...[...waiting.slice(waitingStart), ...waiting.slice(0, waitingStart)].map((askId) => ({ askId, tier: "waiting" })),
|
|
3053
|
-
...[...adopted.slice(adoptedStart), ...adopted.slice(0, adoptedStart)].map((askId) => ({ askId, tier: "adopted" })),
|
|
3054
|
-
];
|
|
3055
|
-
const deadlineMs = Date.now() + SUSPENDED_POLL_BUDGET_MS;
|
|
3056
3159
|
let settled = 0;
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
for (const { askId, tier } of ordered) {
|
|
3062
|
-
if (polled >= SUSPENDED_POLL_MAX_PER_TICK || Date.now() >= deadlineMs) {
|
|
3063
|
-
truncated = true;
|
|
3064
|
-
break;
|
|
3065
|
-
}
|
|
3066
|
-
polled++;
|
|
3067
|
-
if (tier === "waiting")
|
|
3068
|
-
this.suspendedPollOffset = waitingStart + ++polledWaiting;
|
|
3069
|
-
else
|
|
3070
|
-
this.adoptedPollOffset = adoptedStart + ++polledAdopted;
|
|
3160
|
+
const { visited: polled, truncated } = await boundedRotation([
|
|
3161
|
+
{ items: waiting, cursor: this.suspendedPollCursor },
|
|
3162
|
+
{ items: adopted, cursor: this.adoptedPollCursor },
|
|
3163
|
+
], { maxPerTick: SUSPENDED_POLL_MAX_PER_TICK, budgetMs: SUSPENDED_POLL_BUDGET_MS }, async (askId) => {
|
|
3071
3164
|
let row;
|
|
3072
3165
|
const readSeq = this.nextRowReadSeq();
|
|
3073
3166
|
try {
|
|
@@ -3075,24 +3168,24 @@ export class ToolApprovalCoordinator {
|
|
|
3075
3168
|
}
|
|
3076
3169
|
catch (err) {
|
|
3077
3170
|
this.noteStoreError(err, "getAsk(poll-suspended)");
|
|
3078
|
-
|
|
3171
|
+
return;
|
|
3079
3172
|
}
|
|
3080
3173
|
if (row === null) {
|
|
3081
3174
|
this.forgetAdoptedAsk(askId);
|
|
3082
|
-
|
|
3175
|
+
return;
|
|
3083
3176
|
}
|
|
3084
3177
|
if (row.state === "STREAM_PENDING") {
|
|
3085
3178
|
this.followRowOwner(askId, row.owner, readSeq);
|
|
3086
|
-
|
|
3179
|
+
return;
|
|
3087
3180
|
}
|
|
3088
3181
|
if (this.editedDecisionInFlight(askId))
|
|
3089
|
-
|
|
3182
|
+
return;
|
|
3090
3183
|
const endingCause = askRowEndingCause(row);
|
|
3091
3184
|
if (endingCause !== undefined) {
|
|
3092
3185
|
const before = this.waitingRegistrationCount(askId);
|
|
3093
3186
|
this.settleVoidedSiblings([askId], endingCause);
|
|
3094
3187
|
settled += before;
|
|
3095
|
-
|
|
3188
|
+
return;
|
|
3096
3189
|
}
|
|
3097
3190
|
const outcome = replayTerminalAskRow(row);
|
|
3098
3191
|
if (outcome === "unavailable") {
|
|
@@ -3100,11 +3193,11 @@ export class ToolApprovalCoordinator {
|
|
|
3100
3193
|
const parkCause = askRowParkTrigger(row) ?? (Date.now() >= row.expiresAtMs ? APPROVAL_ENDING_CAUSES.TTL_EXPIRED : APPROVAL_ENDING_CAUSES.SHUTDOWN_BEFORE_PARK);
|
|
3101
3194
|
this.settleSameAskIdParkRoute(askId, parkCause);
|
|
3102
3195
|
settled += before;
|
|
3103
|
-
|
|
3196
|
+
return;
|
|
3104
3197
|
}
|
|
3105
3198
|
const delivered = this.notifyExternalDecision(askId, outcome).settled;
|
|
3106
3199
|
settled += delivered;
|
|
3107
|
-
}
|
|
3200
|
+
});
|
|
3108
3201
|
const backlog = Math.max(0, waiting.length + adopted.length - polled);
|
|
3109
3202
|
if (truncated) {
|
|
3110
3203
|
defaultLogger.warn("approval_suspended_poll_truncated", {
|
|
@@ -3113,8 +3206,8 @@ export class ToolApprovalCoordinator {
|
|
|
3113
3206
|
backlog,
|
|
3114
3207
|
maxPerTick: SUSPENDED_POLL_MAX_PER_TICK,
|
|
3115
3208
|
budgetMs: SUSPENDED_POLL_BUDGET_MS,
|
|
3116
|
-
resumeFromOffset: this.
|
|
3117
|
-
resumeAdoptedFromOffset: this.
|
|
3209
|
+
resumeFromOffset: this.suspendedPollCursor.offset,
|
|
3210
|
+
resumeAdoptedFromOffset: this.adoptedPollCursor.offset,
|
|
3118
3211
|
});
|
|
3119
3212
|
}
|
|
3120
3213
|
return { polled, settled, backlog, truncated };
|