@sema-agent/server 7.145.0 → 7.147.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 +876 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +374 -99
- package/deploy/facts/CHANGES.json +25 -0
- package/deploy/facts/README.md +57 -0
- package/deploy/facts/lanes.json +61 -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/README.md +2 -2
- package/deploy/sema-up/chart/templates/_helpers.tpl +41 -0
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +47 -7
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +9 -4
- package/deploy/sema-up/chart/templates/server.yaml +9 -4
- package/deploy/sema-up/chart/values.yaml +8 -1
- package/deploy/sema-up/cluster-up.sh +2 -1
- package/deploy/sema-up/lib/flagshape.sh +4 -0
- package/deploy/sema-up/lib/lanes.json +2 -5
- package/deploy/sema-up/sema-up.sh +12 -9
- package/deploy/sema-up/stack/compose.yaml +5 -4
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +34 -17
- 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 +7 -16
- 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/approval.d.ts +3 -3
- package/dist/audit.js +2 -2
- package/dist/bench/s1/live-deps.js +3 -1
- package/dist/boot/crash-last.js +2 -1
- package/dist/boot/deployment-mods.d.ts +11 -1
- package/dist/boot/deployment-mods.js +15 -2
- package/dist/boot/device-lane.js +7 -5
- package/dist/boot/engine-lease.d.ts +195 -22
- package/dist/boot/engine-lease.js +146 -20
- package/dist/boot/execution-env.d.ts +5 -0
- package/dist/boot/execution-env.js +57 -33
- package/dist/boot/execution-lane.d.ts +37 -9
- package/dist/boot/execution-lane.js +74 -20
- 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 +23 -5
- package/dist/boot/governance-seams.d.ts +2 -2
- package/dist/boot/governance-seams.js +1 -0
- package/dist/boot/leader.js +1 -1
- package/dist/boot/memory-boundary.d.ts +11 -11
- package/dist/boot/memory-boundary.js +3 -3
- package/dist/boot/memory-consolidation.d.ts +42 -41
- package/dist/boot/memory-consolidation.js +26 -28
- package/dist/boot/org-memory.js +3 -2
- package/dist/boot/parked-revive-gate.d.ts +1 -1
- package/dist/boot/parked-revive-gate.js +1 -1
- package/dist/boot/reapers.d.ts +12 -4
- package/dist/boot/reapers.js +40 -16
- package/dist/boot/resolve-spec.d.ts +2 -0
- package/dist/boot/resolve-spec.js +126 -123
- package/dist/boot/retention-lane.d.ts +4 -42
- package/dist/boot/runner-deps.js +1 -1
- package/dist/boot/session-faces.d.ts +1 -0
- package/dist/boot/session-faces.js +11 -3
- package/dist/boot/shutdown.js +27 -9
- package/dist/boot/stage-01-config.d.ts +1 -1
- package/dist/boot/stage-01-config.js +7 -3
- package/dist/boot/stage-02-web-admin-pull.d.ts +1 -1
- package/dist/boot/stage-02-web-admin-pull.js +10 -8
- package/dist/boot/stage-03-store-backend.d.ts +2 -1
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +2 -1
- package/dist/boot/stage-05-execution-env.d.ts +4 -1
- package/dist/boot/stage-05-execution-env.js +6 -6
- package/dist/boot/stage-06-runners.d.ts +5 -2
- package/dist/boot/stage-06-runners.js +12 -13
- package/dist/boot/stage-07-capability-layer.d.ts +5 -2
- package/dist/boot/stage-07-capability-layer.js +7 -7
- package/dist/boot/stage-08-reapers.d.ts +5 -2
- package/dist/boot/stage-08-reapers.js +3 -3
- package/dist/boot/stage-09-leader.d.ts +6 -2
- package/dist/boot/stage-10-http-server.d.ts +6 -2
- package/dist/boot/stage-10-http-server.js +5 -3
- package/dist/boot/stores.d.ts +10 -0
- package/dist/boot/stores.js +53 -26
- package/dist/boot/web-admin.js +3 -3
- package/dist/boot/workflow-orchestration.d.ts +1 -1
- package/dist/boot/workflow-orchestration.js +2 -1
- package/dist/capabilities/center-plugins.d.ts +2 -0
- package/dist/capabilities/center-plugins.js +6 -2
- package/dist/capabilities/composite-members.d.ts +4 -1
- package/dist/capabilities/composite-members.js +4 -1
- package/dist/capabilities/execution-lane.d.ts +7 -7
- package/dist/capabilities/execution-lane.js +2 -2
- package/dist/capabilities/hands-lane.d.ts +1 -1
- package/dist/capabilities/scenarios.d.ts +34 -20
- package/dist/capabilities/scenarios.js +13 -9
- package/dist/capabilities/settings-contract.d.ts +29 -0
- package/dist/capabilities/settings-contract.js +14 -0
- package/dist/capabilities/skills.d.ts +5 -5
- package/dist/capability-facts.d.ts +56 -1
- package/dist/capability-facts.js +59 -8
- package/dist/center-credential-renewer.js +3 -2
- package/dist/checkpoint-ctx-seal-m28.d.ts +1 -1
- package/dist/config-catalog.js +19 -9
- package/dist/config-invariants.d.ts +18 -3
- package/dist/config-invariants.js +36 -19
- package/dist/config-lkg.d.ts +1 -1
- package/dist/config-lkg.js +5 -6
- package/dist/config-provider.d.ts +2 -2
- package/dist/config-provider.js +2 -0
- package/dist/config-types.d.ts +228 -185
- package/dist/config.d.ts +74 -6
- package/dist/config.js +146 -64
- package/dist/degenerate-instrument.js +1 -1
- 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 +95 -0
- package/dist/deployment-shape.d.ts +21 -12
- package/dist/deployment-shape.js +24 -6
- package/dist/device-ws-hub.d.ts +48 -3
- package/dist/device-ws-hub.js +48 -2
- package/dist/env-facts.d.ts +7 -5
- package/dist/env-facts.js +10 -6
- package/dist/error-code-family.d.ts +35 -0
- package/dist/error-code-family.js +26 -0
- package/dist/execution-config-root.d.ts +5 -5
- package/dist/execution-config-root.js +5 -5
- package/dist/execution-env-purpose.d.ts +14 -0
- package/dist/execution-env-purpose.js +29 -0
- package/dist/execution-lane-caps.d.ts +49 -86
- package/dist/execution-lane-caps.js +52 -62
- package/dist/execution-placement.d.ts +18 -2
- package/dist/execution-placement.js +19 -0
- package/dist/file-history-disclosure.d.ts +2 -2
- package/dist/fleet/drain-entry.d.ts +46 -3
- package/dist/fleet/drain-entry.js +6 -0
- package/dist/fleet/fleet-bus.d.ts +2 -1
- package/dist/fleet/serving-lease.js +3 -4
- package/dist/history-anchor.d.ts +22 -0
- package/dist/history-anchor.js +14 -0
- 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/command-spawn.d.ts +1 -4
- package/dist/hooks/command-spawn.js +0 -11
- package/dist/hooks/hook-runner.d.ts +85 -24
- package/dist/hooks/hook-runner.js +326 -109
- 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/approval-sweeps.d.ts +2 -5
- package/dist/http/approval-sweeps.js +3 -2
- package/dist/http/checkpoint-ctx.d.ts +76 -71
- package/dist/http/checkpoint-ctx.js +40 -40
- package/dist/http/claim-latency.d.ts +10 -0
- package/dist/http/claim-latency.js +12 -0
- package/dist/http/dispatch.js +19 -3
- package/dist/http/durable-run-leg.d.ts +5 -0
- package/dist/http/durable-run-leg.js +3 -1
- package/dist/http/execution-context.d.ts +7 -1
- package/dist/http/execution-context.js +13 -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 +2 -2
- package/dist/http/memory-compliance-failure-reply.js +5 -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/memory-store-refusal-reply.d.ts +3 -3
- package/dist/http/memory-store-refusal-reply.js +3 -0
- package/dist/http/owner-access.d.ts +11 -0
- package/dist/http/owner-access.js +7 -4
- package/dist/http/principal-gate.d.ts +28 -16
- package/dist/http/principal-gate.js +10 -19
- package/dist/http/request-guards.d.ts +2 -2
- package/dist/http/request-guards.js +5 -5
- package/dist/http/resume-legs.d.ts +29 -10
- package/dist/http/resume-legs.js +94 -56
- package/dist/http/rewind-files-reply.d.ts +34 -0
- package/dist/http/rewind-files-reply.js +36 -0
- package/dist/http/route-ctx.d.ts +43 -26
- package/dist/http/route-table.js +4 -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 +7 -26
- package/dist/http/routes/background.js +1 -0
- package/dist/http/routes/capabilities.js +12 -3
- package/dist/http/routes/devices.js +1 -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 +2 -2
- package/dist/http/routes/memory-origin.js +2 -4
- package/dist/http/routes/memory-policy.js +5 -1
- package/dist/http/routes/notify-wake.js +2 -1
- package/dist/http/routes/observability.js +3 -6
- package/dist/http/routes/projects.d.ts +70 -0
- package/dist/http/routes/projects.js +86 -0
- package/dist/http/routes/rules.js +30 -2
- package/dist/http/routes/runs.js +15 -11
- package/dist/http/routes/session-sync.js +39 -35
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions.d.ts +7 -1
- package/dist/http/routes/sessions.js +108 -2
- package/dist/http/routes/tasks.js +32 -8
- 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 +31 -14
- package/dist/http/server.js +29 -3
- package/dist/http/stored-body-replay.d.ts +12 -7
- package/dist/http/stored-body-replay.js +28 -5
- package/dist/http/wire-types.d.ts +27 -13
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/instruction-source-notice.d.ts +1 -1
- 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-embedder-reembed.d.ts +38 -0
- package/dist/memory-embedder-reembed.js +20 -0
- package/dist/memory-incident-sink.d.ts +0 -28
- package/dist/memory-incident-sink.js +5 -0
- 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 +38 -2
- package/dist/memory-scope.js +33 -2
- package/dist/memory-store-refusal.d.ts +17 -4
- package/dist/memory-store-refusal.js +1 -1
- package/dist/memory-sync-client.d.ts +2 -0
- package/dist/memory-sync-client.js +13 -11
- package/dist/memory-sync.js +1 -1
- package/dist/model-compat.d.ts +35 -21
- package/dist/model-compat.js +49 -21
- package/dist/model-entry-refusal.d.ts +11 -1
- package/dist/model-entry-refusal.js +7 -0
- package/dist/namespaced-rule-name.d.ts +18 -1
- package/dist/namespaced-rule-name.js +12 -0
- package/dist/net-address.d.ts +53 -0
- package/dist/net-address.js +111 -0
- package/dist/observability/fail-open.d.ts +29 -17
- package/dist/observability/fail-open.js +29 -17
- package/dist/observability/metrics.d.ts +2 -0
- package/dist/observability/metrics.js +2 -0
- package/dist/observability/secret-env-scrub.d.ts +30 -4
- package/dist/observability/secret-env-scrub.js +15 -3
- package/dist/orchestration/workflow-agent-session-index.d.ts +7 -12
- package/dist/orchestration/workflow-agent-session-index.js +1 -6
- package/dist/orchestration/workflow-completion-inbox.d.ts +26 -6
- package/dist/orchestration/workflow-completion-inbox.js +63 -45
- package/dist/orchestration/workflow-notify-journal.js +2 -3
- package/dist/org-delivery-gate.js +1 -1
- package/dist/org-memory-admission.d.ts +5 -2
- package/dist/org-memory-admission.js +25 -10
- package/dist/parked-decide.d.ts +33 -26
- package/dist/parked-decide.js +36 -44
- package/dist/peer-directory.d.ts +6 -12
- package/dist/peer-directory.js +5 -1
- package/dist/plugins/approval-ask-store-file.d.ts +10 -2
- package/dist/plugins/approval-ask-store-file.js +37 -4
- package/dist/plugins/approval-ask-store-memory.d.ts +5 -3
- package/dist/plugins/approval-ask-store-memory.js +11 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +3 -0
- package/dist/plugins/approval-ask-store-sql.js +3 -1
- package/dist/plugins/blob-backend.d.ts +38 -195
- package/dist/plugins/blob-backend.js +33 -229
- package/dist/plugins/caching-session-store.d.ts +12 -5
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-index-columns.d.ts +1 -1
- package/dist/plugins/checkpoint-index-columns.js +3 -2
- package/dist/plugins/checkpoint-store-face.d.ts +426 -0
- package/dist/plugins/checkpoint-store-face.js +152 -0
- package/dist/plugins/device-store-sql.d.ts +2 -2
- package/dist/plugins/device-store-sql.js +5 -10
- package/dist/plugins/execution-lane-rename-m40.d.ts +20 -0
- package/dist/plugins/execution-lane-rename-m40.js +10 -0
- package/dist/plugins/execution-placement-sql.d.ts +63 -88
- package/dist/plugins/execution-placement-sql.js +94 -54
- package/dist/plugins/file-run-store.d.ts +26 -18
- package/dist/plugins/file-run-store.js +64 -75
- 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 +22 -11
- package/dist/plugins/imported-layer-root-retirement.js +4 -8
- package/dist/plugins/inbox-payload-column.d.ts +14 -0
- package/dist/plugins/inbox-payload-column.js +13 -0
- package/dist/plugins/local-checkpoint-store.d.ts +7 -2
- package/dist/plugins/local-checkpoint-store.js +13 -8
- package/dist/plugins/local-data-dir-modes-m39.d.ts +48 -0
- package/dist/plugins/local-data-dir-modes-m39.js +182 -0
- package/dist/plugins/local-engine-high-water.d.ts +11 -0
- package/dist/plugins/local-engine-high-water.js +59 -0
- package/dist/plugins/local-session-store.d.ts +119 -30
- package/dist/plugins/local-session-store.js +156 -53
- package/dist/plugins/local-task-attachment-store.d.ts +2 -0
- package/dist/plugins/local-task-attachment-store.js +10 -11
- package/dist/plugins/memory-embedder-fingerprint.d.ts +33 -10
- package/dist/plugins/memory-embedder-fingerprint.js +52 -12
- package/dist/plugins/memory-embedder.d.ts +1 -1
- package/dist/plugins/memory-embedder.js +2 -2
- package/dist/plugins/memory-engine-pg.d.ts +30 -6
- package/dist/plugins/memory-engine-pg.js +96 -214
- package/dist/plugins/memory-engine-sql-apply.d.ts +130 -0
- package/dist/plugins/memory-engine-sql-apply.js +358 -0
- package/dist/plugins/memory-engine-tidb.d.ts +38 -9
- package/dist/plugins/memory-engine-tidb.js +99 -230
- 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 +169 -83
- package/dist/plugins/one-time-migrations.js +295 -235
- package/dist/plugins/owner-only-file-mode.d.ts +29 -0
- package/dist/plugins/owner-only-file-mode.js +141 -0
- package/dist/plugins/permission-rule-store-file.d.ts +17 -3
- package/dist/plugins/permission-rule-store-file.js +20 -1
- package/dist/plugins/pg-pool.d.ts +2 -1
- package/dist/plugins/pg-pool.js +12 -5
- package/dist/plugins/release-version.d.ts +20 -0
- package/dist/plugins/release-version.js +28 -0
- package/dist/plugins/remote-env-adb.d.ts +20 -4
- package/dist/plugins/remote-env-adb.js +39 -11
- package/dist/plugins/remote-env-e2b.d.ts +12 -3
- package/dist/plugins/remote-env-e2b.js +6 -4
- package/dist/plugins/remote-env-host.d.ts +9 -1
- package/dist/plugins/remote-env-host.js +6 -5
- package/dist/plugins/remote-env-k8s.d.ts +5 -2
- package/dist/plugins/remote-env-k8s.js +15 -4
- package/dist/plugins/remote-env-local-docker.d.ts +4 -1
- package/dist/plugins/remote-env-local-docker.js +3 -3
- package/dist/plugins/remote-env-ssh.d.ts +8 -2
- package/dist/plugins/remote-env-ssh.js +26 -24
- package/dist/plugins/remote-shell.d.ts +59 -30
- package/dist/plugins/remote-shell.js +175 -62
- package/dist/plugins/retention-declaration.d.ts +37 -0
- package/dist/plugins/retention-declaration.js +9 -0
- package/dist/plugins/retention-deployment.d.ts +126 -0
- package/dist/plugins/retention-deployment.js +88 -0
- package/dist/plugins/retention-lane-store-sql.d.ts +74 -4
- package/dist/plugins/retention-lane-store-sql.js +103 -1
- package/dist/plugins/retention-sql.d.ts +29 -0
- package/dist/plugins/retention-sql.js +8 -0
- package/dist/plugins/run-store-sql.js +2 -2
- package/dist/plugins/schema-consistency.d.ts +0 -6
- package/dist/plugins/schema-consistency.js +1 -2
- package/dist/plugins/schema-ensure-legs.d.ts +1 -1
- package/dist/plugins/schema-ensure-legs.js +13 -3
- package/dist/plugins/session-ext-table.d.ts +74 -0
- package/dist/plugins/session-ext-table.js +121 -0
- 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/session-row-sql.d.ts +8 -7
- package/dist/plugins/session-row-sql.js +15 -7
- package/dist/plugins/session-store-sql.d.ts +131 -0
- package/dist/plugins/session-store-sql.js +298 -0
- package/dist/plugins/session-workspace-cell.d.ts +2 -2
- package/dist/plugins/session-workspace-cell.js +2 -2
- package/dist/plugins/shared-memory-store-sql.d.ts +10 -0
- package/dist/plugins/shared-memory-store-sql.js +13 -13
- package/dist/plugins/snapshot-bucket-presence.js +1 -1
- package/dist/plugins/sql-driver.d.ts +23 -7
- package/dist/plugins/sql-driver.js +41 -8
- package/dist/plugins/sql-errors.d.ts +5 -2
- package/dist/plugins/sql-errors.js +4 -0
- package/dist/plugins/sql-json-column.d.ts +5 -1
- package/dist/plugins/store-backend.d.ts +46 -16
- package/dist/plugins/store-backend.js +192 -69
- package/dist/plugins/store-contracts.d.ts +10 -13
- package/dist/plugins/store-contracts.js +1 -9
- package/dist/plugins/store-sweep.d.ts +63 -0
- package/dist/plugins/store-sweep.js +48 -0
- package/dist/plugins/task-attachment-store.d.ts +6 -8
- package/dist/plugins/task-attachment-store.js +15 -10
- package/dist/plugins/task-list-store-sql.d.ts +1 -1
- package/dist/plugins/text-column-capacity.d.ts +21 -0
- package/dist/plugins/text-column-capacity.js +5 -0
- package/dist/plugins/tidb-pool.d.ts +3 -2
- package/dist/plugins/tidb-pool.js +20 -32
- package/dist/plugins/user-input-ledger-sql.d.ts +1 -1
- package/dist/prompt-screen.d.ts +6 -9
- package/dist/prompt-screen.js +2 -5
- package/dist/read-face-posture.d.ts +6 -6
- package/dist/read-face-posture.js +2 -2
- package/dist/request-value-closure.d.ts +5 -5
- package/dist/request-value-closure.js +1 -1
- package/dist/rules-consent.d.ts +48 -27
- package/dist/rules-consent.js +147 -23
- package/dist/run-cancel-context.d.ts +4 -1
- package/dist/run-cancel-context.js +2 -1
- package/dist/run-local.js +17 -13
- package/dist/runs.d.ts +48 -30
- package/dist/runs.js +27 -17
- package/dist/runtime-caps-resolver.d.ts +15 -8
- package/dist/runtime-caps-resolver.js +14 -10
- 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 +16 -0
- package/dist/sealed-key-layout.js +3 -0
- package/dist/sealed-key.js +8 -6
- package/dist/security.d.ts +43 -9
- package/dist/security.js +9 -2
- package/dist/sema-config-dir.js +2 -2
- package/dist/server-secret-env.d.ts +2 -0
- package/dist/service-version.d.ts +2 -1
- package/dist/service-version.js +2 -2
- package/dist/session-error-wire.d.ts +1 -1
- package/dist/session-incarnation.d.ts +8 -5
- package/dist/session-notify-door.d.ts +5 -3
- package/dist/session-resident-background.d.ts +110 -0
- package/dist/session-resident-background.js +88 -0
- package/dist/session-row.d.ts +1 -1
- 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 +12 -2
- package/dist/session-workspace.js +6 -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/store-posture.d.ts +2 -1
- 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-hooks-m20.d.ts +3 -2
- package/dist/stored-request-hooks-m20.js +19 -14
- package/dist/stored-request-prompt-profile-m30.d.ts +1 -1
- package/dist/stores-sql/blob-index-sql.d.ts +148 -0
- package/dist/stores-sql/blob-index-sql.js +154 -0
- package/dist/{plugins → stores-sql}/checkpoint-store-sql.d.ts +14 -428
- package/dist/{plugins → stores-sql}/checkpoint-store-sql.js +43 -220
- package/dist/{plugins → stores-sql}/file-history-store-sql.d.ts +23 -11
- package/dist/{plugins → stores-sql}/file-history-store-sql.js +23 -18
- 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}/pg-session-storage.d.ts +27 -67
- package/dist/{plugins → stores-sql}/pg-session-storage.js +82 -207
- package/dist/{plugins → stores-sql}/retention-store-sql.d.ts +123 -217
- package/dist/{plugins → stores-sql}/retention-store-sql.js +66 -190
- 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}/session-event-payload.d.ts +1 -1
- package/dist/{plugins → stores-sql}/session-event-payload.js +1 -1
- package/dist/stores-sql/session-policy-store-sql.d.ts +1 -1
- package/dist/stores-sql/session-write-hooks.d.ts +59 -0
- package/dist/stores-sql/session-write-hooks.js +2 -0
- package/dist/{plugins → stores-sql}/tidb-session-storage.d.ts +10 -11
- package/dist/{plugins → stores-sql}/tidb-session-storage.js +10 -21
- package/dist/{plugins → stores-sql}/tidb-session-store.d.ts +35 -112
- package/dist/{plugins → stores-sql}/tidb-session-store.js +73 -169
- package/dist/{plugins → stores-sql}/tool-result-store-sql.d.ts +3 -3
- package/dist/{plugins → stores-sql}/tool-result-store-sql.js +6 -6
- package/dist/stores-sql/windowed-session-storage.d.ts +56 -0
- package/dist/stores-sql/windowed-session-storage.js +61 -0
- 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-a2a.d.ts +7 -1
- package/dist/task-a2a.js +15 -14
- package/dist/task-cwd.d.ts +11 -3
- package/dist/task-cwd.js +17 -1
- package/dist/task-mcp.d.ts +10 -2
- package/dist/task-mcp.js +13 -16
- package/dist/task-settings.d.ts +68 -8
- package/dist/task-settings.js +63 -6
- package/dist/tool-approval.d.ts +23 -10
- package/dist/tool-approval.js +10 -6
- package/dist/trace/core-keyset-guard.d.ts +17 -10
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +4 -0
- package/dist/trace/exit-seat.d.ts +29 -1
- package/dist/trace/exit-seat.js +4 -0
- package/dist/trace/project.d.ts +33 -16
- package/dist/trace/project.js +21 -9
- package/dist/trace/redact.d.ts +13 -2
- package/dist/trace/redact.js +347 -78
- package/dist/trace/self-minted-wire.d.ts +587 -16
- package/dist/trace/self-minted-wire.js +215 -9
- package/dist/trace/sema-provenance.d.ts +45 -41
- package/dist/trace/sema-provenance.js +81 -10
- package/dist/trace/task-notification-facets.d.ts +70 -2
- package/dist/trace/task-notification-facets.js +71 -1
- package/dist/trace/wire-projection-faces.d.ts +39 -3
- package/dist/trace/wire-projection-faces.js +27 -2
- package/dist/unread-request-members.d.ts +66 -0
- package/dist/unread-request-members.js +41 -0
- package/dist/usage-analytics.d.ts +4 -0
- package/dist/user-input-ledger.d.ts +2 -2
- package/dist/web-admin-client/apply-effective.js +22 -26
- package/dist/web-admin-client/candidate-gate.d.ts +35 -3
- package/dist/web-admin-client/candidate-gate.js +53 -5
- package/dist/web-admin-client/center-request.d.ts +4 -1
- package/dist/web-admin-client/center-request.js +2 -1
- package/dist/web-admin-client/effective-keys.js +0 -1
- package/dist/web-admin-client/facade.d.ts +1 -1
- package/dist/web-admin-client/http-client.d.ts +1 -0
- package/dist/web-admin-client/http-client.js +17 -9
- package/dist/web-admin-client/restart-signal.js +5 -2
- package/dist/web-admin-client/skills-mcp.d.ts +20 -6
- package/dist/web-admin-client/skills-mcp.js +26 -21
- package/dist/web-admin-client/types.d.ts +34 -49
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +2 -2
- package/package.json +22 -13
- package/scripts/drain-check-local.mjs +8 -55
- package/scripts/drain-check.mysql.sql +3 -40
- package/scripts/drain-check.pg.sql +3 -40
- package/skills/update-config.md +1 -1
- package/wire-changes/server@7.146.0.json +3221 -0
- package/wire-changes/server@7.147.0.json +813 -0
- package/dist/approval-batch-census.d.ts +0 -27
- package/dist/approval-batch-census.js +0 -27
|
@@ -7,7 +7,7 @@ import { findUrlCredential, formatConfigPath, outgoingPath } from "@sema-agent/s
|
|
|
7
7
|
import { httpHookEnvVarsAllowed, httpHookUrlVerdict } from "@sema-agent/settings-schema";
|
|
8
8
|
import { ENGINE_OWNED_HOOK_EVENTS } from "@sema-agent/settings-schema/hooks";
|
|
9
9
|
import { describeToolCatalog } from "@sema-agent/core";
|
|
10
|
-
import { hangClassic } from "./classic-stations.js";
|
|
10
|
+
import { hangClassic, wrapClassic } from "./classic-stations.js";
|
|
11
11
|
import { MAX_HOOK_NOTICE_TEXT_CHARS, boundedNoticeText } from "../fleet/fleet-bus.js";
|
|
12
12
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
13
13
|
import { LIMIT_REASONS } from "../limit-refusal.js";
|
|
@@ -19,8 +19,8 @@ import { ccPromptSystemFor, wrapCondition, parseCcVerdict, CC_EVALUATOR_MAX_OUTP
|
|
|
19
19
|
import { ccAgentHookSystemFor } from "./cc-agent-hook-prompt.js";
|
|
20
20
|
import { renderBranchTranscript } from "./branch-transcript.js";
|
|
21
21
|
import { buildRequestUrlCredentialRefusal, urlCredentialHeaderExit } from "../url-credential-refusal.js";
|
|
22
|
-
import { HOOK_PHASES, HOOK_SOURCE_VOCABULARY_SENTENCE, hookPhaseOf, readHookGroupSource } from "./hook-source.js";
|
|
23
|
-
import { commandLineChars, composeHookEnvLayer, hookPluginChars, planCommandSpawn,
|
|
22
|
+
import { HOOK_PHASES, HOOK_SOURCE_VOCABULARY_SENTENCE, hookLaneOf, hookPhaseOf, readHookGroupSource } from "./hook-source.js";
|
|
23
|
+
import { commandLineChars, composeHookEnvLayer, hookPluginChars, planCommandSpawn, readHookPlugin } from "./command-spawn.js";
|
|
24
24
|
export const MAX_HOOK_ENTRIES_PER_EVENT = 32;
|
|
25
25
|
export const MAX_HOOK_COMMAND_CHARS = 8_192;
|
|
26
26
|
export const MAX_HOOK_TIMEOUT_SECONDS = 600;
|
|
@@ -52,14 +52,27 @@ export const SETTINGS_HOOK_STATIONS = {
|
|
|
52
52
|
PreCompact: ["classic.PreCompact"],
|
|
53
53
|
PostCompact: ["classic.PostCompact"],
|
|
54
54
|
};
|
|
55
|
-
function
|
|
55
|
+
function chainsBelowTaskOn(patterns, station) {
|
|
56
|
+
return patterns.some((p) => p === station || (p.endsWith(".*") && station.startsWith(p.slice(0, -1))));
|
|
57
|
+
}
|
|
58
|
+
function managedTightenOnlyOn(lane, station, belowTaskChainPatterns) {
|
|
59
|
+
return lane === "managed" && chainsBelowTaskOn(belowTaskChainPatterns, station);
|
|
60
|
+
}
|
|
61
|
+
const MANAGED_REWRITE_UNSHOWN_REASON = "a managed PreToolUse hook rewrote this call's input, but this deployment runs its own handler for this call after the request's hooks and that handler never sees a rewrite made above it, so the rewrite cannot be honored and the call was not executed";
|
|
62
|
+
const MANAGED_ALLOW_UNJUDGED_REASON = "a managed PreToolUse hook allowed this call, but this deployment runs its own handler for this call after the request's hooks, and a request hook's allow does not stand in for that handler's judgment, so the call was not executed (the same outcome as the same hook without a managed source)";
|
|
63
|
+
function hangTranslator(on, event, translators, lane) {
|
|
56
64
|
const translate = translators[event];
|
|
57
65
|
if (translate === undefined)
|
|
58
66
|
return;
|
|
67
|
+
const hang = lane === "managed" ? wrapClassic : hangClassic;
|
|
59
68
|
for (const station of SETTINGS_HOOK_STATIONS[event])
|
|
60
|
-
|
|
69
|
+
hang(on, station, translate);
|
|
61
70
|
}
|
|
62
|
-
export const
|
|
71
|
+
export const SETTINGS_HOOKS_REGISTRATIONS = {
|
|
72
|
+
managed: "settings.hooks.managed",
|
|
73
|
+
unmanaged: "settings.hooks",
|
|
74
|
+
};
|
|
75
|
+
const LANE_CHAIN_ORDER = ["managed", "unmanaged"];
|
|
63
76
|
export const DEFAULT_SETTINGS_HOOKS_BUDGET_MS = MAX_HOOK_TIMEOUT_SECONDS * 1000;
|
|
64
77
|
export function settingsHooksBudgetMs(config) {
|
|
65
78
|
return config.hooksTimeoutMs ?? DEFAULT_SETTINGS_HOOKS_BUDGET_MS;
|
|
@@ -222,7 +235,6 @@ const HOOKS_WIRE_KEY_SHAPE = {
|
|
|
222
235
|
source: `source must be one of ${HOOK_SOURCE_VOCABULARY_SENTENCE}`,
|
|
223
236
|
plugin: "plugin must be { name, root, dataDir?, options? } — name a non-empty string, root and dataDir absolute paths with no \"..\" segment and no NUL byte, " +
|
|
224
237
|
"options an object of string values whose keys are identifiers ([A-Za-z_][A-Za-z0-9_]*); nothing else",
|
|
225
|
-
args: "args must be an array of strings without NUL bytes (exec form: command is the executable, args its arguments)",
|
|
226
238
|
};
|
|
227
239
|
function hooksCapIssue(limit, event, path, count) {
|
|
228
240
|
const c = HOOKS_CAPS[limit];
|
|
@@ -419,16 +431,12 @@ export function parseHooksConfig(raw) {
|
|
|
419
431
|
return hooksRefused({ reason: "unsupported_entry_type", path: [...at, "type"], type: contractEntry.type });
|
|
420
432
|
let entry = contractEntry;
|
|
421
433
|
if (entry.type === "command") {
|
|
422
|
-
const
|
|
423
|
-
const rawArgs = rawMember(rawEntry, "args");
|
|
424
|
-
const rawPlugin = rawMember(rawEntry, "plugin");
|
|
425
|
-
const args = rawArgs === undefined ? undefined : readHookArgs(rawArgs);
|
|
426
|
-
if (rawArgs !== undefined && args === undefined)
|
|
427
|
-
return hooksRefused({ reason: "wire_key_invalid", path: [...at, "args"], key: "args" });
|
|
434
|
+
const rawPlugin = rawMember(rawMember(rawMember(rawGroup, "hooks"), entryIndex), "plugin");
|
|
428
435
|
const plugin = rawPlugin === undefined ? undefined : readHookPlugin(rawPlugin);
|
|
429
436
|
if (rawPlugin !== undefined && plugin === undefined)
|
|
430
437
|
return hooksRefused({ reason: "wire_key_invalid", path: [...at, "plugin"], key: "plugin" });
|
|
431
|
-
|
|
438
|
+
if (plugin !== undefined)
|
|
439
|
+
entry = { ...entry, plugin };
|
|
432
440
|
const line = commandLineChars(entry);
|
|
433
441
|
if (line > MAX_HOOK_COMMAND_CHARS)
|
|
434
442
|
return hooksRefused(hooksCapIssue("command", event, [...at, "command"], line));
|
|
@@ -587,7 +595,7 @@ function noteMatcherOverBudget(ctx, args) {
|
|
|
587
595
|
detail: `${what} \`${expression}\` did not finish within ${budgetMs} ms on \`${value}\`; treated as selected (${count} ${count === 1 ? "entry" : "entries"})`,
|
|
588
596
|
});
|
|
589
597
|
}
|
|
590
|
-
function matcherGroupSelected(ctx, event, group, value, toolName) {
|
|
598
|
+
function matcherGroupSelected(ctx, event, group, value, toolName, entries) {
|
|
591
599
|
const answer = hookMatcherMatches(group.matcher, value);
|
|
592
600
|
switch (answer) {
|
|
593
601
|
case true:
|
|
@@ -598,7 +606,7 @@ function matcherGroupSelected(ctx, event, group, value, toolName) {
|
|
|
598
606
|
ctx.logger.warn("hook_matcher_invalid", { event, matcher: echoCallerText(group.matcher ?? "", { maxChars: MATCHER_ECHO_CHARS }) });
|
|
599
607
|
return "invalid";
|
|
600
608
|
case "over_budget":
|
|
601
|
-
noteMatcherOverBudget(ctx, { event, scope: "matcher", expression: group.matcher ?? "", value, entries
|
|
609
|
+
noteMatcherOverBudget(ctx, { event, scope: "matcher", expression: group.matcher ?? "", value, entries, ...(toolName !== undefined ? { toolName } : {}) });
|
|
602
610
|
return "over_budget";
|
|
603
611
|
default: {
|
|
604
612
|
const unreachable = answer;
|
|
@@ -641,6 +649,41 @@ export const PERMISSIVE_VERDICT_EVENTS = {
|
|
|
641
649
|
PreModelSwitch: false,
|
|
642
650
|
PostModelSwitch: false,
|
|
643
651
|
};
|
|
652
|
+
export const ON_FAILURE_BLOCK_EVENTS = {
|
|
653
|
+
PreToolUse: true,
|
|
654
|
+
PostToolUse: true,
|
|
655
|
+
PostToolUseFailure: true,
|
|
656
|
+
PostToolBatch: true,
|
|
657
|
+
Notification: true,
|
|
658
|
+
UserPromptSubmit: true,
|
|
659
|
+
UserPromptExpansion: true,
|
|
660
|
+
SessionStart: true,
|
|
661
|
+
SessionEnd: true,
|
|
662
|
+
Stop: false,
|
|
663
|
+
StopFailure: false,
|
|
664
|
+
SubagentStart: true,
|
|
665
|
+
SubagentStop: false,
|
|
666
|
+
PreCompact: true,
|
|
667
|
+
PostCompact: false,
|
|
668
|
+
PermissionRequest: true,
|
|
669
|
+
PermissionDenied: true,
|
|
670
|
+
Setup: true,
|
|
671
|
+
TeammateIdle: false,
|
|
672
|
+
TaskCreated: true,
|
|
673
|
+
TaskCompleted: false,
|
|
674
|
+
Elicitation: true,
|
|
675
|
+
ElicitationResult: true,
|
|
676
|
+
ConfigChange: true,
|
|
677
|
+
WorktreeCreate: true,
|
|
678
|
+
WorktreeRemove: true,
|
|
679
|
+
InstructionsLoaded: true,
|
|
680
|
+
CwdChanged: true,
|
|
681
|
+
FileChanged: true,
|
|
682
|
+
MessageDisplay: true,
|
|
683
|
+
DirectoryAdded: true,
|
|
684
|
+
PreModelSwitch: true,
|
|
685
|
+
PostModelSwitch: true,
|
|
686
|
+
};
|
|
644
687
|
const DISTRUST_DETAIL = {
|
|
645
688
|
input_truncated: `the hook did not receive the whole payload (tool_input over ${MAX_TOOL_INPUT_CHARS} characters was replaced by a truncation marker, or a prompt / agent entry's payload text was clipped at that size); its allow / updatedInput are not honored`,
|
|
646
689
|
matcher_over_budget: "the matcher (or the if condition's tool-name part) did not finish within its budget; the entry ran as selected, so its allow / updatedInput are not honored",
|
|
@@ -650,7 +693,7 @@ function noteVerdictUntrusted(ctx, args) {
|
|
|
650
693
|
kind: "hook_verdict_untrusted",
|
|
651
694
|
event: args.event,
|
|
652
695
|
reason: args.reason,
|
|
653
|
-
lane:
|
|
696
|
+
lane: hookLaneOf(args.source),
|
|
654
697
|
hookName: boundedNoticeText(hookDisplayName(args.entry)),
|
|
655
698
|
entryType: args.entry.type,
|
|
656
699
|
...(args.toolName !== undefined ? { toolName: args.toolName } : {}),
|
|
@@ -692,15 +735,22 @@ const VERDICT_GAP_CAUSE = {
|
|
|
692
735
|
};
|
|
693
736
|
function createUnrunLedger(ctx, event, toolName) {
|
|
694
737
|
let managedVerdictGap;
|
|
695
|
-
|
|
738
|
+
let haltNoted = false;
|
|
739
|
+
const ledger = {
|
|
740
|
+
halted(source, entry) {
|
|
741
|
+
if (haltNoted)
|
|
742
|
+
return;
|
|
743
|
+
haltNoted = true;
|
|
744
|
+
ledger.unrun(source, entry, "budget_exhausted", ENGINE_BUDGET_SPENT_DETAIL);
|
|
745
|
+
},
|
|
696
746
|
untrusted(source, entry, reason) {
|
|
697
|
-
if (
|
|
747
|
+
if (hookLaneOf(source) === "unmanaged")
|
|
698
748
|
return;
|
|
699
749
|
managedVerdictGap ??= VERDICT_GAP_CAUSE[reason];
|
|
700
750
|
noteVerdictUntrusted(ctx, { event, reason, entry, source, toolName });
|
|
701
751
|
},
|
|
702
752
|
unrun(source, entry, reason, detail) {
|
|
703
|
-
if (source === undefined ||
|
|
753
|
+
if (source === undefined || hookLaneOf(source) === "unmanaged")
|
|
704
754
|
return;
|
|
705
755
|
managedVerdictGap ??= VERDICT_GAP_CAUSE[reason];
|
|
706
756
|
ctx.logger.warn("hook_managed_unrun", { event, reason, source: echoCallerText(source), type: entry.type, ...(toolName !== undefined ? { tool: toolName } : {}) });
|
|
@@ -721,6 +771,7 @@ function createUnrunLedger(ctx, event, toolName) {
|
|
|
721
771
|
return managedVerdictGap;
|
|
722
772
|
},
|
|
723
773
|
};
|
|
774
|
+
return ledger;
|
|
724
775
|
}
|
|
725
776
|
function noteExitNonzero(ctx, ledger, event, ran, toolName, detail) {
|
|
726
777
|
logHookCommandFailed(ctx, event, ran.run);
|
|
@@ -766,6 +817,39 @@ function buildHookContextField(parts) {
|
|
|
766
817
|
const text = buildHookContext(parts);
|
|
767
818
|
return text === undefined ? {} : { additionalContext: text };
|
|
768
819
|
}
|
|
820
|
+
function belowString(below, key) {
|
|
821
|
+
if (typeof below !== "object" || below === null || !Object.hasOwn(below, key))
|
|
822
|
+
return undefined;
|
|
823
|
+
const value = Reflect.get(below, key);
|
|
824
|
+
return typeof value === "string" ? value : undefined;
|
|
825
|
+
}
|
|
826
|
+
function belowMembers(below) {
|
|
827
|
+
return typeof below === "object" && below !== null ? { ...below } : {};
|
|
828
|
+
}
|
|
829
|
+
function withBelowContext(parts, below) {
|
|
830
|
+
const tail = belowString(below, "additionalContext");
|
|
831
|
+
return tail === undefined ? [...parts] : [...parts, tail];
|
|
832
|
+
}
|
|
833
|
+
function alsoAsks(below, own) {
|
|
834
|
+
return below === undefined || below === own ? own : `${below} (a managed PreToolUse hook also asks: ${own})`;
|
|
835
|
+
}
|
|
836
|
+
function readBelowPreToolUse(below) {
|
|
837
|
+
const action = belowString(below, "action");
|
|
838
|
+
const message = belowString(below, "message") ?? "a PreToolUse hook requires approval";
|
|
839
|
+
const cause = belowString(below, "questionCause");
|
|
840
|
+
const rewrite = typeof below === "object" && below !== null && Object.hasOwn(below, "updatedInput") ? Reflect.get(below, "updatedInput") : undefined;
|
|
841
|
+
const additionalContext = belowString(below, "additionalContext");
|
|
842
|
+
const unreadableCause = cause === "verdict_unfinished" ? "verdict_unfinished" : "verdict_unreadable";
|
|
843
|
+
return {
|
|
844
|
+
denied: action === "deny",
|
|
845
|
+
allowed: action === "allow",
|
|
846
|
+
...(action === "ask" && (cause === undefined || cause === "asked") ? { asked: message } : {}),
|
|
847
|
+
...(action === "ask" && cause !== undefined && cause !== "asked" ? { unreadable: { message, cause: unreadableCause } } : {}),
|
|
848
|
+
realApproval: action === "ask" && typeof below === "object" && below !== null && Reflect.get(below, "requiresRealApproval") === true,
|
|
849
|
+
...(rewrite !== null && typeof rewrite === "object" ? { updatedInput: rewrite } : {}),
|
|
850
|
+
...(additionalContext !== undefined ? { additionalContext } : {}),
|
|
851
|
+
};
|
|
852
|
+
}
|
|
769
853
|
function spawnPlannedCommand(plan, ctx, stdio) {
|
|
770
854
|
const env = hookChildEnv(plan.env, true);
|
|
771
855
|
if (plan.kind === "exec")
|
|
@@ -1139,18 +1223,39 @@ function boundedToolInputPayload(payload) {
|
|
|
1139
1223
|
return bounded === p.tool_input ? payload : { ...p, tool_input: bounded };
|
|
1140
1224
|
}
|
|
1141
1225
|
function readHookStdout(ran, event, ctx, payload, ledger) {
|
|
1142
|
-
const
|
|
1143
|
-
const out = parseHookStdout(run.stdout);
|
|
1226
|
+
const out = parseHookStdout(ran.run.stdout);
|
|
1144
1227
|
if (out !== undefined)
|
|
1145
1228
|
return out;
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
ctx.logger.warn("hook_bad_json_output", { event, type: entry.type, preview: seatText("operator-log-text", t) });
|
|
1149
|
-
noteHookFailure(ctx, { event, entry, reason: "bad_json", exitCode: run.code, stderr: run.stderr, toolName: toolNameOf(payload) });
|
|
1150
|
-
ledger.unrun(ran.source, entry, "bad_json");
|
|
1151
|
-
}
|
|
1229
|
+
if (stdoutVerdictUnreadable(ran.run.stdout))
|
|
1230
|
+
noteBadJson(ctx, ledger, event, ran, toolNameOf(payload));
|
|
1152
1231
|
return undefined;
|
|
1153
1232
|
}
|
|
1233
|
+
function stdoutVerdictUnreadable(stdout) {
|
|
1234
|
+
if (parseHookStdout(stdout) !== undefined)
|
|
1235
|
+
return false;
|
|
1236
|
+
const t = stdout.trim();
|
|
1237
|
+
return t.startsWith("{") || t.startsWith("[");
|
|
1238
|
+
}
|
|
1239
|
+
function noteBadJson(ctx, ledger, event, ran, toolName) {
|
|
1240
|
+
const { run, entry } = ran;
|
|
1241
|
+
ctx.logger.warn("hook_bad_json_output", { event, type: entry.type, preview: seatText("operator-log-text", run.stdout.trim()) });
|
|
1242
|
+
noteHookFailure(ctx, { event, entry, reason: "bad_json", exitCode: run.code, stderr: run.stderr, toolName });
|
|
1243
|
+
ledger.unrun(ran.source, entry, "bad_json");
|
|
1244
|
+
}
|
|
1245
|
+
const PERMISSION_DECISION_WORDS = new Set(["allow", "deny", "ask"]);
|
|
1246
|
+
function preToolUseDecisionOf(out) {
|
|
1247
|
+
const hso = out?.hookSpecificOutput;
|
|
1248
|
+
return hso !== null && typeof hso === "object" && hso.hookEventName === "PreToolUse" ? hso.permissionDecision : undefined;
|
|
1249
|
+
}
|
|
1250
|
+
function noteDecisionOutOfSet(ctx, ledger, ran, decision, toolName, effect) {
|
|
1251
|
+
ctx.logger.warn("hook_permission_decision_unknown", {
|
|
1252
|
+
event: "PreToolUse",
|
|
1253
|
+
decision: seatText("operator-log-text", typeof decision === "string" ? decision : typeof decision),
|
|
1254
|
+
tool: toolName,
|
|
1255
|
+
effect,
|
|
1256
|
+
});
|
|
1257
|
+
ledger.unrun(ran.source, ran.entry, "decision_out_of_set");
|
|
1258
|
+
}
|
|
1154
1259
|
function parseHookStdout(stdout) {
|
|
1155
1260
|
const t = stdout.trim();
|
|
1156
1261
|
if (!t.startsWith("{"))
|
|
@@ -1183,14 +1288,70 @@ function runRunnableEntry(entry, payload, ctx, event, llmExtra, halt) {
|
|
|
1183
1288
|
function isSyncEntry(entry) {
|
|
1184
1289
|
return !(entry.type === "command" && (entry.async === true || entry.asyncRewake === true));
|
|
1185
1290
|
}
|
|
1291
|
+
function blocksOnFailure(entry, event) {
|
|
1292
|
+
if (!ON_FAILURE_BLOCK_EVENTS[event] || !isSyncEntry(entry))
|
|
1293
|
+
return false;
|
|
1294
|
+
if (entry.type !== "command" && entry.type !== "http")
|
|
1295
|
+
return false;
|
|
1296
|
+
const word = entry.onFailure;
|
|
1297
|
+
switch (word) {
|
|
1298
|
+
case "block":
|
|
1299
|
+
return true;
|
|
1300
|
+
case "continue":
|
|
1301
|
+
case undefined:
|
|
1302
|
+
return false;
|
|
1303
|
+
default: {
|
|
1304
|
+
const unreachable = word;
|
|
1305
|
+
void unreachable;
|
|
1306
|
+
return true;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
function blockingFailureRun(event, cause) {
|
|
1311
|
+
return { code: 2, stdout: "", stderr: `${event} hook did not reach a verdict (${cause}); blocking because onFailure is "block"`, timedOut: false };
|
|
1312
|
+
}
|
|
1313
|
+
function ranVerdictFlaw(event, run) {
|
|
1314
|
+
if (run.code === 2)
|
|
1315
|
+
return undefined;
|
|
1316
|
+
if (run.code !== 0)
|
|
1317
|
+
return { reason: "exit_nonzero" };
|
|
1318
|
+
if (stdoutVerdictUnreadable(run.stdout))
|
|
1319
|
+
return { reason: "bad_json" };
|
|
1320
|
+
if (event !== "PreToolUse")
|
|
1321
|
+
return undefined;
|
|
1322
|
+
const out = parseHookStdout(run.stdout);
|
|
1323
|
+
if (out?.continue === false || out?.decision === "block")
|
|
1324
|
+
return undefined;
|
|
1325
|
+
const decision = preToolUseDecisionOf(out);
|
|
1326
|
+
return decision !== undefined && !PERMISSION_DECISION_WORDS.has(decision) ? { reason: "decision_out_of_set", decision } : undefined;
|
|
1327
|
+
}
|
|
1328
|
+
function noteRanVerdictFlaw(ctx, ledger, event, ran, flaw, toolName) {
|
|
1329
|
+
switch (flaw.reason) {
|
|
1330
|
+
case "exit_nonzero":
|
|
1331
|
+
noteExitNonzero(ctx, ledger, event, ran, toolName);
|
|
1332
|
+
return;
|
|
1333
|
+
case "bad_json":
|
|
1334
|
+
noteBadJson(ctx, ledger, event, ran, toolName);
|
|
1335
|
+
return;
|
|
1336
|
+
case "decision_out_of_set":
|
|
1337
|
+
noteDecisionOutOfSet(ctx, ledger, ran, flaw.decision, toolName, 'hook ruling not honored (closed word set is allow|deny|ask); the entry\'s onFailure is "block", so this call is denied');
|
|
1338
|
+
return;
|
|
1339
|
+
default: {
|
|
1340
|
+
const unreachable = flaw;
|
|
1341
|
+
void unreachable;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1186
1345
|
async function runMatchingEntries(event, groups, matchValue, payload, ctx, onceFired, halt, deadlineAt, ledger, llmExtra) {
|
|
1187
1346
|
const results = [];
|
|
1188
1347
|
let llmEntries = 0;
|
|
1189
1348
|
const toolName = toolNameOf(payload);
|
|
1190
1349
|
const inputTruncated = boundedToolInputPayload(payload) !== payload;
|
|
1191
1350
|
const truncatedFor = (entry) => (entry.type === "prompt" || entry.type === "agent" ? llmHookArguments(payload).truncated : inputTruncated);
|
|
1351
|
+
const blockedBy = (entry, source, cause) => ({ entry, run: blockingFailureRun(event, cause), source });
|
|
1352
|
+
let laneSpent = false;
|
|
1192
1353
|
const settle = async (entry, onceKey) => {
|
|
1193
|
-
if (halt.aborted)
|
|
1354
|
+
if (halt.signal.aborted)
|
|
1194
1355
|
return { kind: "halted" };
|
|
1195
1356
|
if (entry.once && onceFired.has(onceKey))
|
|
1196
1357
|
return { kind: "passed" };
|
|
@@ -1234,23 +1395,26 @@ async function runMatchingEntries(event, groups, matchValue, payload, ctx, onceF
|
|
|
1234
1395
|
if (entry.type === "command" && (entry.async || entry.asyncRewake)) {
|
|
1235
1396
|
if (entry.once)
|
|
1236
1397
|
onceFired.add(onceKey);
|
|
1237
|
-
void fireAsyncCommandHook(entry, payload, ctx, event, halt);
|
|
1398
|
+
void fireAsyncCommandHook(entry, payload, ctx, event, halt.signal);
|
|
1238
1399
|
return { kind: "passed" };
|
|
1239
1400
|
}
|
|
1240
|
-
if (Date.now() > deadlineAt) {
|
|
1241
|
-
|
|
1401
|
+
if (laneSpent || Date.now() > deadlineAt) {
|
|
1402
|
+
if (!laneSpent)
|
|
1403
|
+
ctx.logger.warn("hook_event_deadline_exceeded", { event, budgetSec: MAX_HOOK_EVENT_TOTAL_SECONDS });
|
|
1242
1404
|
return { kind: "unrun", reason: "budget_exhausted" };
|
|
1243
1405
|
}
|
|
1244
1406
|
if (entry.once)
|
|
1245
1407
|
onceFired.add(onceKey);
|
|
1246
1408
|
if (entry.type === "prompt" || entry.type === "agent")
|
|
1247
1409
|
llmEntries++;
|
|
1248
|
-
const run = await runRunnableEntry(entry, payload, ctx, event, llmExtra, halt);
|
|
1410
|
+
const run = await runRunnableEntry(entry, payload, ctx, event, llmExtra, halt.signal);
|
|
1249
1411
|
if (run.aborted !== undefined) {
|
|
1250
1412
|
if (run.aborted.signal !== undefined)
|
|
1251
1413
|
ctx.logger.warn("hook_command_killed", { event, type: entry.type, killed: true, signal: run.aborted.signal });
|
|
1252
1414
|
return { kind: "halted" };
|
|
1253
1415
|
}
|
|
1416
|
+
if (halt.signal.aborted)
|
|
1417
|
+
return { kind: "halted" };
|
|
1254
1418
|
if (run.withheld !== undefined) {
|
|
1255
1419
|
const { list, rule, sources } = run.withheld;
|
|
1256
1420
|
ctx.logger.warn("hook_http_withheld", { event, url: entry.type === "http" ? buildDisplaySafeUrl(entry.url) : null, list, rule, sources: [...sources] });
|
|
@@ -1274,19 +1438,35 @@ async function runMatchingEntries(event, groups, matchValue, payload, ctx, onceF
|
|
|
1274
1438
|
return ifOverBudget ? { kind: "ran", run, overBudget: true } : { kind: "ran", run };
|
|
1275
1439
|
};
|
|
1276
1440
|
for (const { group, index } of groups) {
|
|
1277
|
-
const
|
|
1441
|
+
const pending = laneSpent ? group.hooks.filter((entry) => blocksOnFailure(entry, event)) : group.hooks;
|
|
1442
|
+
if (laneSpent && pending.length === 0)
|
|
1443
|
+
continue;
|
|
1444
|
+
const selected = matcherGroupSelected(ctx, event, group, matchValue, toolName, pending);
|
|
1278
1445
|
if (selected === "invalid") {
|
|
1279
|
-
for (const entry of
|
|
1280
|
-
if (isSyncEntry(entry))
|
|
1446
|
+
for (const entry of pending) {
|
|
1447
|
+
if (!isSyncEntry(entry))
|
|
1448
|
+
continue;
|
|
1449
|
+
if (!laneSpent)
|
|
1281
1450
|
ledger.unrun(group.source, entry, "matcher_invalid");
|
|
1451
|
+
if (blocksOnFailure(entry, event))
|
|
1452
|
+
results.push(blockedBy(entry, group.source, "matcher_invalid"));
|
|
1453
|
+
}
|
|
1282
1454
|
continue;
|
|
1283
1455
|
}
|
|
1284
1456
|
if (selected === false)
|
|
1285
1457
|
continue;
|
|
1286
1458
|
for (const [ei, entry] of group.hooks.entries()) {
|
|
1459
|
+
if (laneSpent && !blocksOnFailure(entry, event))
|
|
1460
|
+
continue;
|
|
1287
1461
|
const fate = await settle(entry, `${event}:${index}:${ei}`);
|
|
1288
1462
|
switch (fate.kind) {
|
|
1289
1463
|
case "ran": {
|
|
1464
|
+
const flaw = blocksOnFailure(entry, event) ? ranVerdictFlaw(event, fate.run) : undefined;
|
|
1465
|
+
if (flaw !== undefined) {
|
|
1466
|
+
noteRanVerdictFlaw(ctx, ledger, event, { entry, run: fate.run, source: group.source }, flaw, toolName);
|
|
1467
|
+
results.push(blockedBy(entry, group.source, flaw.reason));
|
|
1468
|
+
break;
|
|
1469
|
+
}
|
|
1290
1470
|
const distrust = !PERMISSIVE_VERDICT_EVENTS[event]
|
|
1291
1471
|
? undefined
|
|
1292
1472
|
: truncatedFor(entry)
|
|
@@ -1300,13 +1480,18 @@ async function runMatchingEntries(event, groups, matchValue, payload, ctx, onceF
|
|
|
1300
1480
|
break;
|
|
1301
1481
|
}
|
|
1302
1482
|
case "unrun":
|
|
1303
|
-
|
|
1483
|
+
if (!laneSpent)
|
|
1484
|
+
ledger.unrun(group.source, entry, fate.reason);
|
|
1485
|
+
if (blocksOnFailure(entry, event))
|
|
1486
|
+
results.push(blockedBy(entry, group.source, fate.reason));
|
|
1304
1487
|
if (fate.reason === "budget_exhausted")
|
|
1305
|
-
|
|
1488
|
+
laneSpent = true;
|
|
1306
1489
|
break;
|
|
1307
1490
|
case "passed":
|
|
1308
1491
|
break;
|
|
1309
1492
|
case "halted":
|
|
1493
|
+
if (isSyncEntry(entry) && halt.budgetSpent())
|
|
1494
|
+
ledger.halted(group.source, entry);
|
|
1310
1495
|
return results;
|
|
1311
1496
|
default: {
|
|
1312
1497
|
const unreachable = fate;
|
|
@@ -1317,14 +1502,12 @@ async function runMatchingEntries(event, groups, matchValue, payload, ctx, onceF
|
|
|
1317
1502
|
}
|
|
1318
1503
|
return results;
|
|
1319
1504
|
}
|
|
1320
|
-
function
|
|
1321
|
-
|
|
1322
|
-
return (
|
|
1323
|
-
const lane = phase === "unmanaged" ? "unmanaged" : "managed";
|
|
1324
|
-
return (startedAt[lane] ??= Date.now() + MAX_HOOK_EVENT_TOTAL_SECONDS * 1000);
|
|
1325
|
-
};
|
|
1505
|
+
function createEventClock() {
|
|
1506
|
+
let deadlineAt;
|
|
1507
|
+
return () => (deadlineAt ??= Date.now() + MAX_HOOK_EVENT_TOTAL_SECONDS * 1000);
|
|
1326
1508
|
}
|
|
1327
|
-
|
|
1509
|
+
const ENGINE_BUDGET_SPENT_DETAIL = "the managed lane's handler ran past its engine budget (HOOKS_TIMEOUT_MS); the managed entries after this one did not run on this event";
|
|
1510
|
+
function hookPhasesOf(groups) {
|
|
1328
1511
|
const indexed = groups.map((group, index) => ({ group, index, phase: hookPhaseOf(group.source) }));
|
|
1329
1512
|
return HOOK_PHASES.map((phase) => ({ phase, groups: indexed.filter((g) => g.phase === phase).map(({ group, index }) => ({ group, index })) })).filter((l) => l.groups.length > 0);
|
|
1330
1513
|
}
|
|
@@ -1332,12 +1515,10 @@ function trustRank(phase) {
|
|
|
1332
1515
|
return HOOK_PHASES.length - 1 - HOOK_PHASES.indexOf(phase);
|
|
1333
1516
|
}
|
|
1334
1517
|
async function runEventLanes(event, groups, matchValue, payload, ctx, onceFired, halt, ledger, llmExtra) {
|
|
1335
|
-
const
|
|
1518
|
+
const deadline = createEventClock();
|
|
1336
1519
|
const ran = [];
|
|
1337
|
-
for (const lane of
|
|
1338
|
-
|
|
1339
|
-
break;
|
|
1340
|
-
for (const r of await runMatchingEntries(event, lane.groups, matchValue, payload, ctx, onceFired, halt, deadlineFor(lane.phase), ledger, llmExtra))
|
|
1520
|
+
for (const lane of hookPhasesOf(groups)) {
|
|
1521
|
+
for (const r of await runMatchingEntries(event, lane.groups, matchValue, payload, ctx, onceFired, halt, deadline(), ledger, llmExtra))
|
|
1341
1522
|
ran.push({ ...r, phase: lane.phase });
|
|
1342
1523
|
}
|
|
1343
1524
|
return ran.sort((a, b) => trustRank(a.phase) - trustRank(b.phase)).map(({ entry, run, source }) => ({ entry, run, source }));
|
|
@@ -1372,12 +1553,26 @@ async function runObserveOnlyEvent(event, groups, matchValue, payload, ctx, once
|
|
|
1372
1553
|
}
|
|
1373
1554
|
}
|
|
1374
1555
|
}
|
|
1375
|
-
export function
|
|
1376
|
-
const
|
|
1377
|
-
const shellOwned = configured.filter((e) => HOOK_EVENT_OWNER[e] === "shell");
|
|
1556
|
+
export function createSettingsHooksRegistrations(config, ctx, budgetMs) {
|
|
1557
|
+
const shellOwned = Object.keys(config).filter((e) => (config[e]?.length ?? 0) > 0 && HOOK_EVENT_OWNER[e] === "shell");
|
|
1378
1558
|
if (shellOwned.length > 0) {
|
|
1379
1559
|
ctx.logger.debug("hook_events_shell_owned", { events: shellOwned });
|
|
1380
1560
|
}
|
|
1561
|
+
return LANE_CHAIN_ORDER.flatMap((lane) => {
|
|
1562
|
+
const registration = createLaneRegistration(lane, laneConfigOf(config, lane), ctx, budgetMs);
|
|
1563
|
+
return registration === undefined ? [] : [registration];
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
function laneConfigOf(config, lane) {
|
|
1567
|
+
const out = {};
|
|
1568
|
+
for (const event of Object.keys(config)) {
|
|
1569
|
+
const groups = config[event]?.filter((group) => hookLaneOf(group.source) === lane);
|
|
1570
|
+
if (groups !== undefined && groups.length > 0)
|
|
1571
|
+
out[event] = groups;
|
|
1572
|
+
}
|
|
1573
|
+
return out;
|
|
1574
|
+
}
|
|
1575
|
+
function createLaneRegistration(lane, config, ctx, budgetMs) {
|
|
1381
1576
|
const lit = (e) => {
|
|
1382
1577
|
const groups = config[e];
|
|
1383
1578
|
return groups && groups.length > 0 ? groups : undefined;
|
|
@@ -1397,17 +1592,23 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1397
1592
|
const onceFired = new Set();
|
|
1398
1593
|
const translators = {};
|
|
1399
1594
|
if (pre) {
|
|
1400
|
-
|
|
1595
|
+
const tightenOnly = managedTightenOnlyOn(lane, "classic.PreToolUse", ctx.belowTaskChainPatterns);
|
|
1596
|
+
translators.PreToolUse = async (e, halt, below) => {
|
|
1597
|
+
const prior = readBelowPreToolUse(below);
|
|
1598
|
+
const realApproval = prior.realApproval ? { requiresRealApproval: true } : {};
|
|
1599
|
+
if (prior.denied)
|
|
1600
|
+
return undefined;
|
|
1401
1601
|
const toolName = e.tool;
|
|
1402
1602
|
const ledger = createUnrunLedger(ctx, "PreToolUse", toolName);
|
|
1403
|
-
const
|
|
1404
|
-
let currentInput = e.input;
|
|
1405
|
-
let rewritten =
|
|
1406
|
-
let asked;
|
|
1407
|
-
let
|
|
1603
|
+
const deadline = createEventClock();
|
|
1604
|
+
let currentInput = prior.updatedInput ?? e.input;
|
|
1605
|
+
let rewritten = prior.updatedInput !== undefined;
|
|
1606
|
+
let asked = prior.asked;
|
|
1607
|
+
let spoke = false;
|
|
1608
|
+
let allowedFor = prior.allowed ? { input: currentInput } : undefined;
|
|
1408
1609
|
let unknownDecision = false;
|
|
1409
1610
|
const contexts = { unmanaged: [], managedPlugin: [], managedPolicy: [] };
|
|
1410
|
-
const contextField = () => buildHookContextField([...HOOK_PHASES].sort((a, b) => trustRank(a) - trustRank(b)).flatMap((p) => contexts[p]));
|
|
1611
|
+
const contextField = () => buildHookContextField([...[...HOOK_PHASES].sort((a, b) => trustRank(a) - trustRank(b)).flatMap((p) => contexts[p]), ...(prior.additionalContext !== undefined ? [prior.additionalContext] : [])]);
|
|
1411
1612
|
const runPass = async (phase, groups, once) => {
|
|
1412
1613
|
let rewroteBy;
|
|
1413
1614
|
for (const laneGroup of groups) {
|
|
@@ -1419,7 +1620,7 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1419
1620
|
tool_input: groupInput,
|
|
1420
1621
|
tool_use_id: e.tool_use_id,
|
|
1421
1622
|
};
|
|
1422
|
-
const singles = await runMatchingEntries("PreToolUse", [laneGroup], toolName, payload, ctx, once,
|
|
1623
|
+
const singles = await runMatchingEntries("PreToolUse", [laneGroup], toolName, payload, ctx, once, halt, deadline(), ledger);
|
|
1423
1624
|
for (const ran of singles) {
|
|
1424
1625
|
const { entry, run } = ran;
|
|
1425
1626
|
if (run.code === 2) {
|
|
@@ -1446,20 +1647,26 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1446
1647
|
const hso = out.hookSpecificOutput;
|
|
1447
1648
|
if (hso !== null && typeof hso === "object" && hso.hookEventName === "PreToolUse") {
|
|
1448
1649
|
const h = hso;
|
|
1449
|
-
if (typeof h.additionalContext === "string")
|
|
1650
|
+
if (typeof h.additionalContext === "string") {
|
|
1450
1651
|
contexts[phase].push(clipDataText(h.additionalContext, MAX_HOOK_FEEDBACK_CHARS));
|
|
1451
|
-
|
|
1652
|
+
spoke = true;
|
|
1653
|
+
}
|
|
1654
|
+
const decision = preToolUseDecisionOf(out);
|
|
1452
1655
|
if (decision === "deny") {
|
|
1453
1656
|
const reason = clipDataText(typeof h.permissionDecisionReason === "string" ? h.permissionDecisionReason : "denied by a PreToolUse hook", MAX_HOOK_FEEDBACK_CHARS);
|
|
1454
1657
|
return { deny: { action: "deny", message: reason, ...contextField() } };
|
|
1455
1658
|
}
|
|
1659
|
+
const proposed = h.updatedInput !== null && typeof h.updatedInput === "object" ? h.updatedInput : undefined;
|
|
1660
|
+
const proposesRewrite = proposed !== undefined && canonicalJsonStringify(proposed) !== canonicalJsonStringify(groupInput);
|
|
1661
|
+
if (tightenOnly && ran.distrust === undefined && (decision === "allow" || (proposesRewrite && (decision === "ask" || decision === undefined)))) {
|
|
1662
|
+
return { deny: { action: "deny", message: proposesRewrite ? MANAGED_REWRITE_UNSHOWN_REASON : MANAGED_ALLOW_UNJUDGED_REASON, ...contextField() } };
|
|
1663
|
+
}
|
|
1456
1664
|
if (decision === "ask") {
|
|
1457
|
-
asked = clipDataText(typeof h.permissionDecisionReason === "string" ? h.permissionDecisionReason : "a PreToolUse hook requires approval", MAX_HOOK_FEEDBACK_CHARS);
|
|
1665
|
+
asked = alsoAsks(prior.asked ?? prior.unreadable?.message, clipDataText(typeof h.permissionDecisionReason === "string" ? h.permissionDecisionReason : "a PreToolUse hook requires approval", MAX_HOOK_FEEDBACK_CHARS));
|
|
1666
|
+
spoke = true;
|
|
1458
1667
|
}
|
|
1459
1668
|
if (decision === "allow" || decision === undefined) {
|
|
1460
|
-
|
|
1461
|
-
const proposesRewrite = proposed !== undefined && canonicalJsonStringify(proposed) !== canonicalJsonStringify(groupInput);
|
|
1462
|
-
if (ran.distrust !== undefined && hookPhaseOf(ran.source) === "unmanaged" && (decision === "allow" || proposesRewrite)) {
|
|
1669
|
+
if (ran.distrust !== undefined && hookLaneOf(ran.source) === "unmanaged" && (decision === "allow" || proposesRewrite)) {
|
|
1463
1670
|
noteVerdictUntrusted(ctx, { event: "PreToolUse", reason: ran.distrust, entry, source: ran.source, toolName });
|
|
1464
1671
|
}
|
|
1465
1672
|
const offered = ran.distrust === undefined ? proposed : undefined;
|
|
@@ -1468,26 +1675,24 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1468
1675
|
currentInput = offered;
|
|
1469
1676
|
rewritten = true;
|
|
1470
1677
|
rewroteBy = ran;
|
|
1678
|
+
spoke = true;
|
|
1471
1679
|
}
|
|
1472
|
-
if (decision === "allow" && ran.distrust === undefined)
|
|
1680
|
+
if (decision === "allow" && ran.distrust === undefined) {
|
|
1473
1681
|
allowedFor = { input: rewrites ? offered : groupInput };
|
|
1682
|
+
spoke = true;
|
|
1683
|
+
}
|
|
1474
1684
|
}
|
|
1475
|
-
else if (decision
|
|
1685
|
+
else if (!PERMISSION_DECISION_WORDS.has(decision)) {
|
|
1476
1686
|
unknownDecision = true;
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
decision: seatText("operator-log-text", typeof decision === "string" ? decision : typeof decision),
|
|
1480
|
-
tool: toolName,
|
|
1481
|
-
effect: "hook ruling not honored (closed word set is allow|deny|ask); this call falls back to the engine gate",
|
|
1482
|
-
});
|
|
1483
|
-
ledger.unrun(ran.source, entry, "decision_out_of_set");
|
|
1687
|
+
spoke = true;
|
|
1688
|
+
noteDecisionOutOfSet(ctx, ledger, ran, decision, toolName, "hook ruling not honored (closed word set is allow|deny|ask); this call falls back to the engine gate");
|
|
1484
1689
|
}
|
|
1485
1690
|
}
|
|
1486
1691
|
}
|
|
1487
1692
|
}
|
|
1488
1693
|
return { rewroteBy };
|
|
1489
1694
|
};
|
|
1490
|
-
for (const lane of
|
|
1695
|
+
for (const lane of hookPhasesOf(pre)) {
|
|
1491
1696
|
if (lane.phase === "unmanaged") {
|
|
1492
1697
|
const pass = await runPass(lane.phase, lane.groups, onceFired);
|
|
1493
1698
|
if (pass.deny !== undefined)
|
|
@@ -1511,38 +1716,45 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1511
1716
|
ledger.unrun(second.rewroteBy.source, second.rewroteBy.entry, "rewrite_unsettled", "the managed hooks of this phase rewrote the tool input again after being re-run on their own rewrite; approval required");
|
|
1512
1717
|
}
|
|
1513
1718
|
}
|
|
1719
|
+
const managedGap = ledger.managedVerdictGap;
|
|
1720
|
+
if (below !== undefined && !spoke && managedGap === undefined)
|
|
1721
|
+
return undefined;
|
|
1514
1722
|
if (asked !== undefined) {
|
|
1515
|
-
return { action: "ask", message: asked, questionCause: "asked", ...(rewritten ? { updatedInput: currentInput } : {}), ...contextField() };
|
|
1723
|
+
return { action: "ask", message: asked, questionCause: "asked", ...(rewritten ? { updatedInput: currentInput } : {}), ...realApproval, ...contextField() };
|
|
1516
1724
|
}
|
|
1517
|
-
const managedGap = ledger.managedVerdictGap;
|
|
1518
1725
|
if (managedGap !== undefined) {
|
|
1519
1726
|
return {
|
|
1520
1727
|
action: "ask",
|
|
1521
1728
|
message: "a managed PreToolUse hook did not run to a verdict — approval required",
|
|
1522
1729
|
questionCause: managedGap,
|
|
1523
1730
|
...(rewritten ? { updatedInput: currentInput } : {}),
|
|
1731
|
+
...realApproval,
|
|
1524
1732
|
...contextField(),
|
|
1525
1733
|
};
|
|
1526
1734
|
}
|
|
1735
|
+
if (prior.unreadable !== undefined) {
|
|
1736
|
+
return { action: "ask", message: prior.unreadable.message, questionCause: prior.unreadable.cause, ...(rewritten ? { updatedInput: currentInput } : {}), ...realApproval, ...contextField() };
|
|
1737
|
+
}
|
|
1527
1738
|
if (unknownDecision) {
|
|
1528
1739
|
return {
|
|
1529
1740
|
action: "ask",
|
|
1530
1741
|
message: "a PreToolUse hook returned an unrecognized permissionDecision — approval required",
|
|
1531
1742
|
questionCause: "verdict_unreadable",
|
|
1532
1743
|
...(rewritten ? { updatedInput: currentInput } : {}),
|
|
1744
|
+
...realApproval,
|
|
1533
1745
|
...contextField(),
|
|
1534
1746
|
};
|
|
1535
1747
|
}
|
|
1536
1748
|
const carried = { ...(rewritten ? { updatedInput: currentInput } : {}), ...contextField() };
|
|
1537
1749
|
if (allowedFor !== undefined && allowedFor.input === currentInput)
|
|
1538
1750
|
return { action: "allow", ...carried };
|
|
1539
|
-
if (rewritten || HOOK_PHASES.some((p) => contexts[p].length > 0))
|
|
1751
|
+
if (rewritten || HOOK_PHASES.some((p) => contexts[p].length > 0) || prior.additionalContext !== undefined)
|
|
1540
1752
|
return carried;
|
|
1541
1753
|
return undefined;
|
|
1542
1754
|
};
|
|
1543
1755
|
}
|
|
1544
1756
|
if (post) {
|
|
1545
|
-
translators.PostToolUse = async (e,
|
|
1757
|
+
translators.PostToolUse = async (e, halt, below) => {
|
|
1546
1758
|
const toolName = e.tool;
|
|
1547
1759
|
const input = e.input;
|
|
1548
1760
|
const output = e.output;
|
|
@@ -1559,7 +1771,7 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1559
1771
|
tool_use_id: e.tool_use_id,
|
|
1560
1772
|
};
|
|
1561
1773
|
const ledger = createUnrunLedger(ctx, "PostToolUse", toolName);
|
|
1562
|
-
const singles = await runEventLanes("PostToolUse", post, toolName, payload, ctx, onceFired,
|
|
1774
|
+
const singles = await runEventLanes("PostToolUse", post, toolName, payload, ctx, onceFired, halt, ledger);
|
|
1563
1775
|
for (const ran of singles) {
|
|
1564
1776
|
const { entry, run } = ran;
|
|
1565
1777
|
if (run.code === 2) {
|
|
@@ -1592,12 +1804,13 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1592
1804
|
}
|
|
1593
1805
|
}
|
|
1594
1806
|
}
|
|
1595
|
-
|
|
1596
|
-
|
|
1807
|
+
if (contexts.length === 0)
|
|
1808
|
+
return undefined;
|
|
1809
|
+
return { ...belowMembers(below), ...buildHookContextField(withBelowContext(contexts, below)) };
|
|
1597
1810
|
};
|
|
1598
1811
|
}
|
|
1599
1812
|
if (postFailure) {
|
|
1600
|
-
translators.PostToolUseFailure = async (e,
|
|
1813
|
+
translators.PostToolUseFailure = async (e, halt, below) => {
|
|
1601
1814
|
const toolName = e.tool;
|
|
1602
1815
|
const f = e.failure;
|
|
1603
1816
|
const contexts = [];
|
|
@@ -1611,7 +1824,7 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1611
1824
|
is_interrupt: f.isInterrupt,
|
|
1612
1825
|
};
|
|
1613
1826
|
const ledger = createUnrunLedger(ctx, "PostToolUseFailure", toolName);
|
|
1614
|
-
const singles = await runEventLanes("PostToolUseFailure", postFailure, toolName, payload, ctx, onceFired,
|
|
1827
|
+
const singles = await runEventLanes("PostToolUseFailure", postFailure, toolName, payload, ctx, onceFired, halt, ledger);
|
|
1615
1828
|
for (const ran of singles) {
|
|
1616
1829
|
const { entry, run } = ran;
|
|
1617
1830
|
if (run.code === 2) {
|
|
@@ -1640,12 +1853,13 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1640
1853
|
contexts.push(clipDataText(h.additionalContext, MAX_HOOK_FEEDBACK_CHARS));
|
|
1641
1854
|
}
|
|
1642
1855
|
}
|
|
1643
|
-
|
|
1644
|
-
|
|
1856
|
+
if (contexts.length === 0)
|
|
1857
|
+
return undefined;
|
|
1858
|
+
return { ...belowMembers(below), ...buildHookContextField(withBelowContext(contexts, below)) };
|
|
1645
1859
|
};
|
|
1646
1860
|
}
|
|
1647
1861
|
if (postBatch) {
|
|
1648
|
-
translators.PostToolBatch = async (e,
|
|
1862
|
+
translators.PostToolBatch = async (e, halt, below) => {
|
|
1649
1863
|
const contexts = [];
|
|
1650
1864
|
const payload = {
|
|
1651
1865
|
...basePayload(ctx, e.permissionMode),
|
|
@@ -1658,7 +1872,7 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1658
1872
|
})),
|
|
1659
1873
|
};
|
|
1660
1874
|
const ledger = createUnrunLedger(ctx, "PostToolBatch", undefined);
|
|
1661
|
-
const singles = await runEventLanes("PostToolBatch", ignoreMatchers(postBatch), "", payload, ctx, onceFired,
|
|
1875
|
+
const singles = await runEventLanes("PostToolBatch", ignoreMatchers(postBatch), "", payload, ctx, onceFired, halt, ledger);
|
|
1662
1876
|
for (const ran of singles) {
|
|
1663
1877
|
const { entry, run } = ran;
|
|
1664
1878
|
if (run.code === 2) {
|
|
@@ -1683,16 +1897,17 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1683
1897
|
if (out.hookSpecificOutput !== undefined)
|
|
1684
1898
|
ctx.logger.warn("hook_result_unsupported", { event: "PostToolBatch" });
|
|
1685
1899
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
1900
|
+
if (contexts.length === 0)
|
|
1901
|
+
return undefined;
|
|
1902
|
+
return { ...belowMembers(below), ...buildHookContextField(withBelowContext(contexts, below)) };
|
|
1688
1903
|
};
|
|
1689
1904
|
}
|
|
1690
1905
|
if (promptSubmit) {
|
|
1691
|
-
translators.UserPromptSubmit = async (e,
|
|
1906
|
+
translators.UserPromptSubmit = async (e, halt, below) => {
|
|
1692
1907
|
const contexts = [];
|
|
1693
1908
|
const payload = { ...basePayload(ctx, e.permissionMode), hook_event_name: "UserPromptSubmit", prompt: clipDataText(e.prompt, MAX_TOOL_INPUT_CHARS) };
|
|
1694
1909
|
const ledger = createUnrunLedger(ctx, "UserPromptSubmit", undefined);
|
|
1695
|
-
const singles = await runEventLanes("UserPromptSubmit", ignoreMatchers(promptSubmit), "", payload, ctx, onceFired,
|
|
1910
|
+
const singles = await runEventLanes("UserPromptSubmit", ignoreMatchers(promptSubmit), "", payload, ctx, onceFired, halt, ledger);
|
|
1696
1911
|
for (const ran of singles) {
|
|
1697
1912
|
const { entry, run } = ran;
|
|
1698
1913
|
if (run.code === 2) {
|
|
@@ -1723,12 +1938,13 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1723
1938
|
contexts.push(clipDataText(h.additionalContext, MAX_HOOK_FEEDBACK_CHARS));
|
|
1724
1939
|
}
|
|
1725
1940
|
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1941
|
+
if (contexts.length === 0)
|
|
1942
|
+
return undefined;
|
|
1943
|
+
return { ...belowMembers(below), ...buildHookContextField(withBelowContext(contexts, below)) };
|
|
1728
1944
|
};
|
|
1729
1945
|
}
|
|
1730
1946
|
if (stopEntries) {
|
|
1731
|
-
translators.Stop = async (sctx,
|
|
1947
|
+
translators.Stop = async (sctx, halt, below) => {
|
|
1732
1948
|
const payload = { ...basePayload(ctx, sctx.permissionMode), hook_event_name: "Stop", stop_hook_active: sctx.stopHookActive };
|
|
1733
1949
|
let llmExtra;
|
|
1734
1950
|
const wantsPrompt = stopEntries.some((g) => g.hooks.some((h) => h.type === "prompt"));
|
|
@@ -1766,7 +1982,7 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1766
1982
|
const effectiveGroups = llmExtra || !wantsPrompt
|
|
1767
1983
|
? stopGroups
|
|
1768
1984
|
: stopGroups.map((g) => ({ ...g, hooks: g.hooks.filter((h) => h.type !== "prompt") })).filter((g) => g.hooks.length > 0);
|
|
1769
|
-
const singles = await runEventLanes("Stop", effectiveGroups, "", payload, ctx, onceFired,
|
|
1985
|
+
const singles = await runEventLanes("Stop", effectiveGroups, "", payload, ctx, onceFired, halt, ledger, llmExtra);
|
|
1770
1986
|
let block;
|
|
1771
1987
|
let sawContinueFalse = false;
|
|
1772
1988
|
const contexts = [];
|
|
@@ -1821,13 +2037,14 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1821
2037
|
if (block === undefined && contexts.length === 0)
|
|
1822
2038
|
return undefined;
|
|
1823
2039
|
return {
|
|
2040
|
+
...belowMembers(below),
|
|
1824
2041
|
...(block !== undefined ? { block } : {}),
|
|
1825
|
-
...buildHookContextField(contexts),
|
|
2042
|
+
...buildHookContextField(withBelowContext(contexts, below)),
|
|
1826
2043
|
};
|
|
1827
2044
|
};
|
|
1828
2045
|
}
|
|
1829
2046
|
if (stopFailureEntries) {
|
|
1830
|
-
translators.StopFailure = async (fctx,
|
|
2047
|
+
translators.StopFailure = async (fctx, halt) => {
|
|
1831
2048
|
const turnError = toAssistantTurnError(fctx.errorKind);
|
|
1832
2049
|
const payload = {
|
|
1833
2050
|
...basePayload(ctx, undefined),
|
|
@@ -1835,11 +2052,11 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1835
2052
|
error: turnError,
|
|
1836
2053
|
...(fctx.error ? { error_details: clipDataText(redactSecrets(fctx.error), MAX_HOOK_FEEDBACK_CHARS) } : {}),
|
|
1837
2054
|
};
|
|
1838
|
-
await runObserveOnlyEvent("StopFailure", stopFailureEntries, turnError, payload, ctx, onceFired,
|
|
2055
|
+
await runObserveOnlyEvent("StopFailure", stopFailureEntries, turnError, payload, ctx, onceFired, halt);
|
|
1839
2056
|
};
|
|
1840
2057
|
}
|
|
1841
2058
|
if (preCompactEntries) {
|
|
1842
|
-
translators.PreCompact = async (pctx,
|
|
2059
|
+
translators.PreCompact = async (pctx, halt, below) => {
|
|
1843
2060
|
const payload = {
|
|
1844
2061
|
...basePayload(ctx, undefined),
|
|
1845
2062
|
hook_event_name: "PreCompact",
|
|
@@ -1847,11 +2064,11 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1847
2064
|
custom_instructions: pctx.customInstructions ?? null,
|
|
1848
2065
|
};
|
|
1849
2066
|
const ledger = createUnrunLedger(ctx, "PreCompact", undefined);
|
|
1850
|
-
const singles = await runEventLanes("PreCompact", preCompactEntries, pctx.trigger, payload, ctx, onceFired,
|
|
2067
|
+
const singles = await runEventLanes("PreCompact", preCompactEntries, pctx.trigger, payload, ctx, onceFired, halt, ledger);
|
|
1851
2068
|
for (const ran of singles) {
|
|
1852
2069
|
const { entry, run } = ran;
|
|
1853
2070
|
if (run.code === 2) {
|
|
1854
|
-
return { block: clipDataText(run.stderr.trim(), MAX_HOOK_FEEDBACK_CHARS) || "blocked by a PreCompact hook" };
|
|
2071
|
+
return { ...belowMembers(below), block: clipDataText(run.stderr.trim(), MAX_HOOK_FEEDBACK_CHARS) || "blocked by a PreCompact hook" };
|
|
1855
2072
|
}
|
|
1856
2073
|
if (run.code !== 0) {
|
|
1857
2074
|
noteExitNonzero(ctx, ledger, "PreCompact", ran);
|
|
@@ -1866,31 +2083,31 @@ export function createSettingsHooksRegistration(config, ctx, budgetMs) {
|
|
|
1866
2083
|
ctx.logger.warn("hook_continue_false_unsupported", { event: "PreCompact" });
|
|
1867
2084
|
}
|
|
1868
2085
|
if (out.decision === "block") {
|
|
1869
|
-
return { block: clipDataText(typeof out.reason === "string" ? out.reason : "blocked by a PreCompact hook", MAX_HOOK_FEEDBACK_CHARS) };
|
|
2086
|
+
return { ...belowMembers(below), block: clipDataText(typeof out.reason === "string" ? out.reason : "blocked by a PreCompact hook", MAX_HOOK_FEEDBACK_CHARS) };
|
|
1870
2087
|
}
|
|
1871
2088
|
}
|
|
1872
2089
|
return undefined;
|
|
1873
2090
|
};
|
|
1874
2091
|
}
|
|
1875
2092
|
if (postCompactEntries) {
|
|
1876
|
-
translators.PostCompact = async (cctx,
|
|
2093
|
+
translators.PostCompact = async (cctx, halt) => {
|
|
1877
2094
|
const payload = {
|
|
1878
2095
|
...basePayload(ctx, undefined),
|
|
1879
2096
|
hook_event_name: "PostCompact",
|
|
1880
2097
|
trigger: cctx.trigger,
|
|
1881
2098
|
compact_summary: clipDataText(cctx.summary, MAX_TOOL_RESPONSE_CHARS),
|
|
1882
2099
|
};
|
|
1883
|
-
await runObserveOnlyEvent("PostCompact", postCompactEntries, cctx.trigger, payload, ctx, onceFired,
|
|
2100
|
+
await runObserveOnlyEvent("PostCompact", postCompactEntries, cctx.trigger, payload, ctx, onceFired, halt);
|
|
1884
2101
|
};
|
|
1885
2102
|
}
|
|
1886
2103
|
const events = Object.keys(translators);
|
|
1887
2104
|
return {
|
|
1888
|
-
name:
|
|
2105
|
+
name: SETTINGS_HOOKS_REGISTRATIONS[lane],
|
|
1889
2106
|
uses: { events: events.flatMap((event) => SETTINGS_HOOK_STATIONS[event]), calls: [] },
|
|
1890
2107
|
budgetMs,
|
|
1891
2108
|
register(on) {
|
|
1892
2109
|
for (const event of events)
|
|
1893
|
-
hangTranslator(on, event, translators);
|
|
2110
|
+
hangTranslator(on, event, translators, lane);
|
|
1894
2111
|
},
|
|
1895
2112
|
};
|
|
1896
2113
|
}
|