@sema-agent/server 7.146.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 +384 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +170 -66
- package/deploy/facts/lanes.json +7 -0
- package/deploy/sema-up/README.md +2 -2
- package/deploy/sema-up/chart/templates/_helpers.tpl +22 -0
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -4
- package/deploy/sema-up/chart/templates/server.yaml +5 -4
- package/deploy/sema-up/chart/values.yaml +1 -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 +4 -3
- package/dist/approval-card.d.ts +4 -4
- 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/device-lane.js +5 -5
- package/dist/boot/engine-lease.d.ts +12 -11
- package/dist/boot/execution-env.d.ts +2 -1
- package/dist/boot/execution-env.js +45 -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 +1 -1
- package/dist/boot/exit-last.js +2 -1
- package/dist/boot/governance-seams.d.ts +2 -2
- 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 +13 -24
- package/dist/boot/memory-consolidation.js +0 -13
- 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.js +1 -1
- package/dist/boot/resolve-spec.js +91 -111
- package/dist/boot/retention-lane.d.ts +4 -42
- package/dist/boot/session-faces.d.ts +1 -0
- package/dist/boot/session-faces.js +8 -2
- package/dist/boot/stage-01-config.d.ts +1 -1
- package/dist/boot/stage-01-config.js +6 -2
- 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 +1 -1
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.js +3 -3
- package/dist/boot/stage-06-runners.d.ts +3 -3
- package/dist/boot/stage-06-runners.js +1 -3
- package/dist/boot/stage-07-capability-layer.d.ts +3 -3
- package/dist/boot/stage-07-capability-layer.js +4 -9
- package/dist/boot/stage-08-reapers.d.ts +3 -3
- package/dist/boot/stage-09-leader.d.ts +4 -3
- package/dist/boot/stage-10-http-server.d.ts +4 -3
- package/dist/boot/stage-10-http-server.js +2 -1
- package/dist/boot/stores.d.ts +9 -0
- package/dist/boot/stores.js +18 -7
- package/dist/boot/web-admin.js +3 -3
- package/dist/capabilities/center-plugins.d.ts +2 -0
- package/dist/capabilities/center-plugins.js +6 -2
- package/dist/capabilities/composite-members.d.ts +1 -0
- package/dist/capabilities/composite-members.js +1 -0
- 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 +32 -18
- 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 +38 -0
- package/dist/capability-facts.js +34 -1
- package/dist/center-credential-renewer.js +3 -2
- package/dist/config-catalog.js +6 -5
- 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.js +2 -0
- package/dist/config-types.d.ts +190 -186
- package/dist/config.d.ts +68 -5
- package/dist/config.js +80 -54
- package/dist/degenerate-instrument.js +1 -1
- package/dist/deploy-facts.js +3 -2
- package/dist/deployment-shape.d.ts +21 -12
- package/dist/deployment-shape.js +24 -6
- package/dist/env-facts.d.ts +7 -5
- package/dist/env-facts.js +10 -6
- 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 +17 -10
- package/dist/fleet/drain-entry.js +1 -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/command-spawn.d.ts +1 -4
- package/dist/hooks/command-spawn.js +0 -11
- package/dist/hooks/hook-runner.d.ts +52 -8
- package/dist/hooks/hook-runner.js +160 -36
- package/dist/http/approval-sweeps.d.ts +2 -5
- package/dist/http/approval-sweeps.js +3 -2
- package/dist/http/checkpoint-ctx.d.ts +17 -0
- package/dist/http/checkpoint-ctx.js +6 -0
- package/dist/http/claim-latency.d.ts +10 -0
- package/dist/http/claim-latency.js +12 -0
- package/dist/http/dispatch.js +1 -1
- package/dist/http/durable-run-leg.js +2 -1
- package/dist/http/execution-context.d.ts +3 -1
- package/dist/http/execution-context.js +4 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
- package/dist/http/memory-compliance-failure-reply.js +3 -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 +3 -14
- package/dist/http/principal-gate.js +0 -13
- package/dist/http/request-guards.d.ts +1 -1
- package/dist/http/resume-legs.d.ts +7 -10
- package/dist/http/resume-legs.js +79 -50
- 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 +32 -16
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/approvals-assistant.js +5 -25
- package/dist/http/routes/capabilities.js +8 -2
- package/dist/http/routes/devices.js +1 -1
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +2 -1
- 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 +8 -8
- package/dist/http/routes/session-sync.js +27 -10
- package/dist/http/routes/sessions.d.ts +7 -1
- package/dist/http/routes/sessions.js +104 -2
- package/dist/http/routes/tasks.js +9 -3
- package/dist/http/server.d.ts +24 -10
- package/dist/http/server.js +17 -6
- package/dist/http/stored-body-replay.d.ts +5 -3
- package/dist/http/stored-body-replay.js +10 -1
- package/dist/http/wire-types.d.ts +19 -10
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/instruction-source-notice.d.ts +1 -1
- 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-scope.d.ts +2 -2
- package/dist/memory-scope.js +1 -1
- 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/observability/fail-open.d.ts +17 -9
- package/dist/observability/fail-open.js +17 -9
- 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/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/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 +9 -4
- 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 +1 -1
- 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 -12
- package/dist/plugins/file-run-store.js +63 -63
- package/dist/plugins/imported-layer-root-retirement.d.ts +17 -8
- 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 +5 -0
- package/dist/plugins/local-checkpoint-store.js +11 -7
- 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 +91 -25
- package/dist/plugins/local-session-store.js +124 -45
- 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 +4 -3
- package/dist/plugins/memory-engine-pg.js +4 -4
- package/dist/plugins/memory-engine-sql-apply.d.ts +13 -4
- package/dist/plugins/memory-engine-sql-apply.js +1 -1
- package/dist/plugins/memory-engine-tidb.d.ts +8 -5
- package/dist/plugins/memory-engine-tidb.js +7 -8
- package/dist/plugins/one-time-migrations.d.ts +145 -35
- package/dist/plugins/one-time-migrations.js +272 -120
- 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 +8 -8
- package/dist/plugins/permission-rule-store-file.js +14 -2
- package/dist/plugins/pg-pool.d.ts +2 -1
- package/dist/plugins/pg-pool.js +11 -4
- 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/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 +11 -1
- 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 +1 -1
- 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/store-backend.d.ts +35 -8
- package/dist/plugins/store-backend.js +184 -62
- package/dist/plugins/store-contracts.d.ts +9 -2
- package/dist/plugins/store-contracts.js +1 -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 +19 -31
- package/dist/plugins/user-input-ledger-sql.d.ts +1 -1
- 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 +41 -13
- package/dist/rules-consent.js +101 -53
- package/dist/run-local.js +17 -13
- package/dist/runs.d.ts +48 -30
- package/dist/runs.js +26 -16
- package/dist/runtime-caps-resolver.d.ts +15 -8
- package/dist/runtime-caps-resolver.js +14 -10
- package/dist/sealed-key-layout.d.ts +2 -3
- package/dist/sealed-key-layout.js +0 -1
- package/dist/sealed-key.js +7 -5
- package/dist/security.d.ts +5 -3
- 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-resident-background.d.ts +85 -32
- package/dist/session-resident-background.js +75 -7
- package/dist/session-row.d.ts +1 -1
- package/dist/session-sync-kernel.d.ts +1 -1
- package/dist/session-workspace.d.ts +11 -2
- package/dist/session-workspace.js +5 -0
- package/dist/sighup-idle.d.ts +1 -1
- package/dist/store-posture.d.ts +2 -1
- package/dist/stored-request-hooks-m20.d.ts +3 -2
- package/dist/stored-request-hooks-m20.js +19 -14
- package/dist/stores-sql/blob-index-sql.d.ts +148 -0
- package/dist/stores-sql/blob-index-sql.js +154 -0
- package/dist/stores-sql/checkpoint-store-sql.d.ts +1 -1
- package/dist/stores-sql/checkpoint-store-sql.js +11 -27
- 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}/pg-session-storage.d.ts +23 -76
- package/dist/{plugins → stores-sql}/pg-session-storage.js +80 -209
- 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}/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 +32 -122
- package/dist/{plugins → stores-sql}/tidb-session-store.js +71 -171
- package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.js +1 -1
- package/dist/stores-sql/windowed-session-storage.d.ts +56 -0
- package/dist/stores-sql/windowed-session-storage.js +61 -0
- 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 +9 -4
- package/dist/task-settings.js +17 -6
- package/dist/tool-approval.d.ts +21 -8
- package/dist/tool-approval.js +10 -6
- package/dist/trace/core-keyset-guard.d.ts +9 -3
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.d.ts +23 -1
- package/dist/trace/exit-seat.js +3 -0
- package/dist/trace/project.d.ts +33 -16
- package/dist/trace/project.js +12 -9
- package/dist/trace/redact.d.ts +1 -1
- package/dist/trace/redact.js +61 -73
- package/dist/trace/self-minted-wire.d.ts +263 -10
- package/dist/trace/self-minted-wire.js +90 -2
- 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 +38 -2
- package/dist/trace/wire-projection-faces.js +26 -1
- package/dist/unread-request-members.d.ts +66 -0
- package/dist/unread-request-members.js +41 -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 +13 -12
- package/scripts/drain-check-local.mjs +2 -28
- package/scripts/drain-check.mysql.sql +2 -18
- package/scripts/drain-check.pg.sql +2 -18
- package/wire-changes/server@7.147.0.json +813 -0
|
@@ -20,7 +20,7 @@ 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
22
|
import { HOOK_PHASES, HOOK_SOURCE_VOCABULARY_SENTENCE, hookLaneOf, hookPhaseOf, readHookGroupSource } from "./hook-source.js";
|
|
23
|
-
import { commandLineChars, composeHookEnvLayer, hookPluginChars, planCommandSpawn,
|
|
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;
|
|
@@ -235,7 +235,6 @@ const HOOKS_WIRE_KEY_SHAPE = {
|
|
|
235
235
|
source: `source must be one of ${HOOK_SOURCE_VOCABULARY_SENTENCE}`,
|
|
236
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, " +
|
|
237
237
|
"options an object of string values whose keys are identifiers ([A-Za-z_][A-Za-z0-9_]*); nothing else",
|
|
238
|
-
args: "args must be an array of strings without NUL bytes (exec form: command is the executable, args its arguments)",
|
|
239
238
|
};
|
|
240
239
|
function hooksCapIssue(limit, event, path, count) {
|
|
241
240
|
const c = HOOKS_CAPS[limit];
|
|
@@ -432,16 +431,12 @@ export function parseHooksConfig(raw) {
|
|
|
432
431
|
return hooksRefused({ reason: "unsupported_entry_type", path: [...at, "type"], type: contractEntry.type });
|
|
433
432
|
let entry = contractEntry;
|
|
434
433
|
if (entry.type === "command") {
|
|
435
|
-
const
|
|
436
|
-
const rawArgs = rawMember(rawEntry, "args");
|
|
437
|
-
const rawPlugin = rawMember(rawEntry, "plugin");
|
|
438
|
-
const args = rawArgs === undefined ? undefined : readHookArgs(rawArgs);
|
|
439
|
-
if (rawArgs !== undefined && args === undefined)
|
|
440
|
-
return hooksRefused({ reason: "wire_key_invalid", path: [...at, "args"], key: "args" });
|
|
434
|
+
const rawPlugin = rawMember(rawMember(rawMember(rawGroup, "hooks"), entryIndex), "plugin");
|
|
441
435
|
const plugin = rawPlugin === undefined ? undefined : readHookPlugin(rawPlugin);
|
|
442
436
|
if (rawPlugin !== undefined && plugin === undefined)
|
|
443
437
|
return hooksRefused({ reason: "wire_key_invalid", path: [...at, "plugin"], key: "plugin" });
|
|
444
|
-
|
|
438
|
+
if (plugin !== undefined)
|
|
439
|
+
entry = { ...entry, plugin };
|
|
445
440
|
const line = commandLineChars(entry);
|
|
446
441
|
if (line > MAX_HOOK_COMMAND_CHARS)
|
|
447
442
|
return hooksRefused(hooksCapIssue("command", event, [...at, "command"], line));
|
|
@@ -600,7 +595,7 @@ function noteMatcherOverBudget(ctx, args) {
|
|
|
600
595
|
detail: `${what} \`${expression}\` did not finish within ${budgetMs} ms on \`${value}\`; treated as selected (${count} ${count === 1 ? "entry" : "entries"})`,
|
|
601
596
|
});
|
|
602
597
|
}
|
|
603
|
-
function matcherGroupSelected(ctx, event, group, value, toolName) {
|
|
598
|
+
function matcherGroupSelected(ctx, event, group, value, toolName, entries) {
|
|
604
599
|
const answer = hookMatcherMatches(group.matcher, value);
|
|
605
600
|
switch (answer) {
|
|
606
601
|
case true:
|
|
@@ -611,7 +606,7 @@ function matcherGroupSelected(ctx, event, group, value, toolName) {
|
|
|
611
606
|
ctx.logger.warn("hook_matcher_invalid", { event, matcher: echoCallerText(group.matcher ?? "", { maxChars: MATCHER_ECHO_CHARS }) });
|
|
612
607
|
return "invalid";
|
|
613
608
|
case "over_budget":
|
|
614
|
-
noteMatcherOverBudget(ctx, { event, scope: "matcher", expression: group.matcher ?? "", value, entries
|
|
609
|
+
noteMatcherOverBudget(ctx, { event, scope: "matcher", expression: group.matcher ?? "", value, entries, ...(toolName !== undefined ? { toolName } : {}) });
|
|
615
610
|
return "over_budget";
|
|
616
611
|
default: {
|
|
617
612
|
const unreachable = answer;
|
|
@@ -654,6 +649,41 @@ export const PERMISSIVE_VERDICT_EVENTS = {
|
|
|
654
649
|
PreModelSwitch: false,
|
|
655
650
|
PostModelSwitch: false,
|
|
656
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
|
+
};
|
|
657
687
|
const DISTRUST_DETAIL = {
|
|
658
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`,
|
|
659
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",
|
|
@@ -800,6 +830,9 @@ function withBelowContext(parts, below) {
|
|
|
800
830
|
const tail = belowString(below, "additionalContext");
|
|
801
831
|
return tail === undefined ? [...parts] : [...parts, tail];
|
|
802
832
|
}
|
|
833
|
+
function alsoAsks(below, own) {
|
|
834
|
+
return below === undefined || below === own ? own : `${below} (a managed PreToolUse hook also asks: ${own})`;
|
|
835
|
+
}
|
|
803
836
|
function readBelowPreToolUse(below) {
|
|
804
837
|
const action = belowString(below, "action");
|
|
805
838
|
const message = belowString(below, "message") ?? "a PreToolUse hook requires approval";
|
|
@@ -1190,18 +1223,39 @@ function boundedToolInputPayload(payload) {
|
|
|
1190
1223
|
return bounded === p.tool_input ? payload : { ...p, tool_input: bounded };
|
|
1191
1224
|
}
|
|
1192
1225
|
function readHookStdout(ran, event, ctx, payload, ledger) {
|
|
1193
|
-
const
|
|
1194
|
-
const out = parseHookStdout(run.stdout);
|
|
1226
|
+
const out = parseHookStdout(ran.run.stdout);
|
|
1195
1227
|
if (out !== undefined)
|
|
1196
1228
|
return out;
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
ctx.logger.warn("hook_bad_json_output", { event, type: entry.type, preview: seatText("operator-log-text", t) });
|
|
1200
|
-
noteHookFailure(ctx, { event, entry, reason: "bad_json", exitCode: run.code, stderr: run.stderr, toolName: toolNameOf(payload) });
|
|
1201
|
-
ledger.unrun(ran.source, entry, "bad_json");
|
|
1202
|
-
}
|
|
1229
|
+
if (stdoutVerdictUnreadable(ran.run.stdout))
|
|
1230
|
+
noteBadJson(ctx, ledger, event, ran, toolNameOf(payload));
|
|
1203
1231
|
return undefined;
|
|
1204
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
|
+
}
|
|
1205
1259
|
function parseHookStdout(stdout) {
|
|
1206
1260
|
const t = stdout.trim();
|
|
1207
1261
|
if (!t.startsWith("{"))
|
|
@@ -1234,12 +1288,68 @@ function runRunnableEntry(entry, payload, ctx, event, llmExtra, halt) {
|
|
|
1234
1288
|
function isSyncEntry(entry) {
|
|
1235
1289
|
return !(entry.type === "command" && (entry.async === true || entry.asyncRewake === true));
|
|
1236
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
|
+
}
|
|
1237
1345
|
async function runMatchingEntries(event, groups, matchValue, payload, ctx, onceFired, halt, deadlineAt, ledger, llmExtra) {
|
|
1238
1346
|
const results = [];
|
|
1239
1347
|
let llmEntries = 0;
|
|
1240
1348
|
const toolName = toolNameOf(payload);
|
|
1241
1349
|
const inputTruncated = boundedToolInputPayload(payload) !== payload;
|
|
1242
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;
|
|
1243
1353
|
const settle = async (entry, onceKey) => {
|
|
1244
1354
|
if (halt.signal.aborted)
|
|
1245
1355
|
return { kind: "halted" };
|
|
@@ -1288,8 +1398,9 @@ async function runMatchingEntries(event, groups, matchValue, payload, ctx, onceF
|
|
|
1288
1398
|
void fireAsyncCommandHook(entry, payload, ctx, event, halt.signal);
|
|
1289
1399
|
return { kind: "passed" };
|
|
1290
1400
|
}
|
|
1291
|
-
if (Date.now() > deadlineAt) {
|
|
1292
|
-
|
|
1401
|
+
if (laneSpent || Date.now() > deadlineAt) {
|
|
1402
|
+
if (!laneSpent)
|
|
1403
|
+
ctx.logger.warn("hook_event_deadline_exceeded", { event, budgetSec: MAX_HOOK_EVENT_TOTAL_SECONDS });
|
|
1293
1404
|
return { kind: "unrun", reason: "budget_exhausted" };
|
|
1294
1405
|
}
|
|
1295
1406
|
if (entry.once)
|
|
@@ -1327,19 +1438,35 @@ async function runMatchingEntries(event, groups, matchValue, payload, ctx, onceF
|
|
|
1327
1438
|
return ifOverBudget ? { kind: "ran", run, overBudget: true } : { kind: "ran", run };
|
|
1328
1439
|
};
|
|
1329
1440
|
for (const { group, index } of groups) {
|
|
1330
|
-
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);
|
|
1331
1445
|
if (selected === "invalid") {
|
|
1332
|
-
for (const entry of
|
|
1333
|
-
if (isSyncEntry(entry))
|
|
1446
|
+
for (const entry of pending) {
|
|
1447
|
+
if (!isSyncEntry(entry))
|
|
1448
|
+
continue;
|
|
1449
|
+
if (!laneSpent)
|
|
1334
1450
|
ledger.unrun(group.source, entry, "matcher_invalid");
|
|
1451
|
+
if (blocksOnFailure(entry, event))
|
|
1452
|
+
results.push(blockedBy(entry, group.source, "matcher_invalid"));
|
|
1453
|
+
}
|
|
1335
1454
|
continue;
|
|
1336
1455
|
}
|
|
1337
1456
|
if (selected === false)
|
|
1338
1457
|
continue;
|
|
1339
1458
|
for (const [ei, entry] of group.hooks.entries()) {
|
|
1459
|
+
if (laneSpent && !blocksOnFailure(entry, event))
|
|
1460
|
+
continue;
|
|
1340
1461
|
const fate = await settle(entry, `${event}:${index}:${ei}`);
|
|
1341
1462
|
switch (fate.kind) {
|
|
1342
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
|
+
}
|
|
1343
1470
|
const distrust = !PERMISSIVE_VERDICT_EVENTS[event]
|
|
1344
1471
|
? undefined
|
|
1345
1472
|
: truncatedFor(entry)
|
|
@@ -1353,9 +1480,12 @@ async function runMatchingEntries(event, groups, matchValue, payload, ctx, onceF
|
|
|
1353
1480
|
break;
|
|
1354
1481
|
}
|
|
1355
1482
|
case "unrun":
|
|
1356
|
-
|
|
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));
|
|
1357
1487
|
if (fate.reason === "budget_exhausted")
|
|
1358
|
-
|
|
1488
|
+
laneSpent = true;
|
|
1359
1489
|
break;
|
|
1360
1490
|
case "passed":
|
|
1361
1491
|
break;
|
|
@@ -1521,7 +1651,7 @@ function createLaneRegistration(lane, config, ctx, budgetMs) {
|
|
|
1521
1651
|
contexts[phase].push(clipDataText(h.additionalContext, MAX_HOOK_FEEDBACK_CHARS));
|
|
1522
1652
|
spoke = true;
|
|
1523
1653
|
}
|
|
1524
|
-
const decision =
|
|
1654
|
+
const decision = preToolUseDecisionOf(out);
|
|
1525
1655
|
if (decision === "deny") {
|
|
1526
1656
|
const reason = clipDataText(typeof h.permissionDecisionReason === "string" ? h.permissionDecisionReason : "denied by a PreToolUse hook", MAX_HOOK_FEEDBACK_CHARS);
|
|
1527
1657
|
return { deny: { action: "deny", message: reason, ...contextField() } };
|
|
@@ -1532,7 +1662,7 @@ function createLaneRegistration(lane, config, ctx, budgetMs) {
|
|
|
1532
1662
|
return { deny: { action: "deny", message: proposesRewrite ? MANAGED_REWRITE_UNSHOWN_REASON : MANAGED_ALLOW_UNJUDGED_REASON, ...contextField() } };
|
|
1533
1663
|
}
|
|
1534
1664
|
if (decision === "ask") {
|
|
1535
|
-
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));
|
|
1536
1666
|
spoke = true;
|
|
1537
1667
|
}
|
|
1538
1668
|
if (decision === "allow" || decision === undefined) {
|
|
@@ -1552,16 +1682,10 @@ function createLaneRegistration(lane, config, ctx, budgetMs) {
|
|
|
1552
1682
|
spoke = true;
|
|
1553
1683
|
}
|
|
1554
1684
|
}
|
|
1555
|
-
else if (decision
|
|
1685
|
+
else if (!PERMISSION_DECISION_WORDS.has(decision)) {
|
|
1556
1686
|
unknownDecision = true;
|
|
1557
1687
|
spoke = true;
|
|
1558
|
-
ctx
|
|
1559
|
-
event: "PreToolUse",
|
|
1560
|
-
decision: seatText("operator-log-text", typeof decision === "string" ? decision : typeof decision),
|
|
1561
|
-
tool: toolName,
|
|
1562
|
-
effect: "hook ruling not honored (closed word set is allow|deny|ask); this call falls back to the engine gate",
|
|
1563
|
-
});
|
|
1564
|
-
ledger.unrun(ran.source, entry, "decision_out_of_set");
|
|
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");
|
|
1565
1689
|
}
|
|
1566
1690
|
}
|
|
1567
1691
|
}
|
|
@@ -6,16 +6,13 @@
|
|
|
6
6
|
* 不是请求流水线。`createWallClockJumpGuard` 建的 `denySweepClockGuard` 是 ① 的私有件,随它同居。
|
|
7
7
|
* ctx 字段 3 只(`deps` / `resumeCheckpoint` / `mayDiscloseRowError`),机器算(closure-free-vars)与手抄一致。
|
|
8
8
|
*/
|
|
9
|
-
import { type ParkedAskRedeemRequest } from "../tool-approval.js";
|
|
9
|
+
import { type ParkedAskRedeemRequest, type ParkedAskRedeemResult } from "../tool-approval.js";
|
|
10
10
|
import type { createResumeLegs } from "./resume-legs.js";
|
|
11
11
|
import type { FlatServiceDeps } from "./server.js";
|
|
12
12
|
export declare function createApprovalSweeps(ctx: {
|
|
13
13
|
deps: FlatServiceDeps;
|
|
14
14
|
} & Pick<ReturnType<typeof createResumeLegs>, "resumeCheckpoint" | "mayDiscloseRowError">): {
|
|
15
15
|
denyExpiredApprovals: (now: number) => Promise<void>;
|
|
16
|
-
redeemParkedAsk: (req: ParkedAskRedeemRequest) => Promise<
|
|
17
|
-
status: number;
|
|
18
|
-
body: Record<string, unknown>;
|
|
19
|
-
}>;
|
|
16
|
+
redeemParkedAsk: (req: ParkedAskRedeemRequest) => Promise<ParkedAskRedeemResult>;
|
|
20
17
|
};
|
|
21
18
|
//# sourceMappingURL=approval-sweeps.d.ts.map
|
|
@@ -231,8 +231,9 @@ export function createApprovalSweeps(ctx) {
|
|
|
231
231
|
...(req.gateBoundInputHash !== null ? { boundInputHash: req.gateBoundInputHash } : {}),
|
|
232
232
|
...(req.updatedInput !== undefined ? { updatedInput: req.updatedInput } : {}),
|
|
233
233
|
};
|
|
234
|
-
const
|
|
235
|
-
|
|
234
|
+
const remember = req.remember === "session" ? { grantedBy: req.principal ?? null, laneCannotRecord: "decide_once" } : undefined;
|
|
235
|
+
const out = await resumeCheckpoint(cp.sessionId, req.decision, req.note, hostDecisionFacts(req.decider, req.principal), req.httpReq, undefined, binding, remember, true, { principal: req.principal, explicitOperator: false });
|
|
236
|
+
return { status: out.status, body: out.body, ...(remember !== undefined ? { rememberApplied: out.rememberApplied === true } : {}) };
|
|
236
237
|
}
|
|
237
238
|
return { denyExpiredApprovals, redeemParkedAsk };
|
|
238
239
|
}
|
|
@@ -157,6 +157,14 @@ export type StoredBodyReplayRefusal =
|
|
|
157
157
|
| {
|
|
158
158
|
readonly kind: "settings_env_credential";
|
|
159
159
|
readonly refusal: Refusal;
|
|
160
|
+
}
|
|
161
|
+
/** 存体里有本版读者读不出、而本部署会采纳的成员(hooks / settings.env / permissions / MCP transport / A2A headers …;判据 `unread-request-members.ts`):`refusal` = 拒点 `resolveSpec` 抛的那只 400
|
|
162
|
+
* (`request.field_invalid`,`field` = 第一个坐标,句子点名全部坐标)的码、句子与机读位,答复折成 409 `resume_blocked_by_policy`;`members` = 判据交回的坐标(键路径与下标,不带值;与拒句同一个上限,截断由问点做),`memberCount` = 总数 —— 运维行读这两格。 */
|
|
163
|
+
| {
|
|
164
|
+
readonly kind: "unread_members";
|
|
165
|
+
readonly refusal: Refusal;
|
|
166
|
+
readonly members: readonly string[];
|
|
167
|
+
readonly memberCount: number;
|
|
160
168
|
} | {
|
|
161
169
|
readonly kind: "permission_mode_reading";
|
|
162
170
|
readonly stored: PermissionMode | undefined;
|
|
@@ -202,6 +210,13 @@ export interface ResumeRefusalAnswer {
|
|
|
202
210
|
* · (`retriable`,卡在才在)。
|
|
203
211
|
*/
|
|
204
212
|
export declare function resumeBlockedByPolicyAnswer(inner: Refusal): ResumeRefusalAnswer;
|
|
213
|
+
/**
|
|
214
|
+
* 续跑腿上认领之前一只 typed 拒的**唯一折法**(`resume-legs.ts` 的两处折叠口同调:装配 `resolveResumeSpec` 与停驻赎回的执行上下文)。一条律:
|
|
215
|
+
* 拒体自报这一拍答不出(`permanence: "transient"` —— 依赖此刻不可用,不是政策:今天 = 中心答不出场景裁定)⇒ 原码原状态原体(同一张卡在依赖恢复后可再决;
|
|
216
|
+
* SLA 拒扫腿按「其余 ≥400」逐拍重试,不把它当确定性拒过期掉);其余 ⇒ {@link resumeBlockedByPolicyAnswer}(准入策略挡住,`blockedBy` = 原码)。
|
|
217
|
+
* 两形之后都由调用方经 {@link resumeRefusalWithCard} 追加 `retriable`(卡仍 pending)。
|
|
218
|
+
*/
|
|
219
|
+
export declare function resumeAssemblyRefusalAnswer(status: number, inner: Refusal): ResumeRefusalAnswer;
|
|
205
220
|
/**
|
|
206
221
|
* 一次续跑的答复是不是 {@link resumeBlockedByPolicyAnswer} 铸的那一只(409 `resume_blocked_by_policy`)—— SLA 拒扫腿据此区分「同一份字节在本版永远被拒」与别的 ≥400
|
|
207
222
|
* (后者可能是瞬时的,照旧逐拍重试)。码的写端与读端住在同一个模块,读端不在别处第二次手拼这个字面。
|
|
@@ -229,6 +244,8 @@ export declare function storedBodyReplayLogFields(refusal: StoredBodyReplayRefus
|
|
|
229
244
|
storedMode?: PermissionMode | null;
|
|
230
245
|
floor?: PermissionMode;
|
|
231
246
|
key?: ReplayRefusedRetiredKey;
|
|
247
|
+
members?: readonly string[];
|
|
248
|
+
memberCount?: number;
|
|
232
249
|
note: string;
|
|
233
250
|
};
|
|
234
251
|
/**
|
|
@@ -97,6 +97,9 @@ export function resumeBlockedByPolicyAnswer(inner) {
|
|
|
97
97
|
body: refusalBody({ errorCode: "resume_blocked_by_policy", message: `resume blocked by an admission policy change: ${inner.message} — nothing was resumed; if the task is still suspended on its approval, cancel it (POST /v1/runs/:id/cancel) to end it without another model turn`, facts: { blockedBy: inner.errorCode, ...inner.facts } }),
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
|
+
export function resumeAssemblyRefusalAnswer(status, inner) {
|
|
101
|
+
return inner.facts?.permanence === "transient" ? { status, body: refusalBody(inner) } : resumeBlockedByPolicyAnswer(inner);
|
|
102
|
+
}
|
|
100
103
|
export function isResumeBlockedByPolicy(answer) {
|
|
101
104
|
return answer.status === 409 && typeof answer.body === "object" && answer.body !== null && answer.body.errorCode === "resume_blocked_by_policy";
|
|
102
105
|
}
|
|
@@ -111,6 +114,8 @@ export function storedBodyReplayLogFields(refusal) {
|
|
|
111
114
|
return { kind: refusal.kind, layer: refusal.entry.layer, note: "this session's stored request carries a tool-list entry the engine reads as empty (a skill disallowedTools, an agent definition's denyTools / allowTools, the request's excludeTools / allowTools, or the rules in settings.permissions.deny / ask: a retired name, a case twin, a spelling no tool name can have, a rule core's grammar cannot compile, or a ~/ path rule on a lane with no home), or a permission list that is malformed or over the entry cap in either direction; it is not replayed — the task stays suspended until someone cancels it and resubmits with the entry fixed" };
|
|
112
115
|
case "settings_env_credential":
|
|
113
116
|
return { kind: refusal.kind, note: "this session's stored request carries credentials in settings.env (a credential variable name of this engine, or a value shaped like a secret) and the one-time window that dropped them on resume has closed; it is not replayed — the task stays suspended until someone cancels it and resubmits with the credentials moved into the engine's environment" };
|
|
117
|
+
case "unread_members":
|
|
118
|
+
return { kind: refusal.kind, members: refusal.members, memberCount: refusal.memberCount, note: "this session's stored request carries members this version's readers cannot take as written (named in the refusal: settings.hooks / settings.env / an MCP server's transport / an A2A peer's headers …) and this deployment would apply them; replaying it would run without them, so it is not replayed — the task stays suspended until someone cancels it and resubmits with those members in the current form" };
|
|
114
119
|
case "permission_mode_reading":
|
|
115
120
|
return { kind: refusal.kind, storedMode: refusal.stored ?? null, floor: refusal.floor, note: "this session's stored request was written before the operator's AUTONOMY plan floor was recorded in the task (earlier versions re-applied it on every resume); replaying it now would run under its submitted permission mode — the task stays suspended until someone cancels it, or AUTONOMY is set to read-only so it can finish with read-only hands" };
|
|
116
121
|
case "retired_request_key":
|
|
@@ -133,6 +138,7 @@ function storedBodyReplayArmAnswer(refusal, readerVersion) {
|
|
|
133
138
|
case "inert_tool_list":
|
|
134
139
|
return resumeBlockedByPolicyAnswer(refusal.entry.extra === undefined ? { errorCode: refusal.entry.code, message: refusal.entry.message } : { errorCode: refusal.entry.code, message: refusal.entry.message, facts: refusal.entry.extra });
|
|
135
140
|
case "settings_env_credential":
|
|
141
|
+
case "unread_members":
|
|
136
142
|
return resumeBlockedByPolicyAnswer(refusal.refusal);
|
|
137
143
|
case "permission_mode_reading": {
|
|
138
144
|
const answer = permissionModeReadingRefusal();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** `step` 标签的闭集:一个往返一个词。 */
|
|
2
|
+
export declare const EXECUTION_CLAIM_STEPS: readonly ["run_claim", "placement_commit"];
|
|
3
|
+
export type ExecutionClaimStep = (typeof EXECUTION_CLAIM_STEPS)[number];
|
|
4
|
+
/** 计时落点:`Metrics` 的 `observe` 子集(deps 上没有指标注册表、或只带计数的桩 ⇒ 不计,行为逐字不变)。 */
|
|
5
|
+
export interface ClaimLatencySink {
|
|
6
|
+
observe?(name: string, value: number, labels?: Record<string, string>): void;
|
|
7
|
+
}
|
|
8
|
+
/** 量一次认领往返(秒)。`claim` 是刚发出的往返(调用点在实参位置发起它,时钟随即起);抛也计、上抛原错。 */
|
|
9
|
+
export declare function timeClaim<T>(sink: ClaimLatencySink | undefined, step: ExecutionClaimStep, claim: Promise<T>): Promise<T>;
|
|
10
|
+
//# sourceMappingURL=claim-latency.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { EXECUTION_CLAIM_METRIC } from "../observability/metrics.js";
|
|
2
|
+
export const EXECUTION_CLAIM_STEPS = ["run_claim", "placement_commit"];
|
|
3
|
+
export async function timeClaim(sink, step, claim) {
|
|
4
|
+
const started = performance.now();
|
|
5
|
+
try {
|
|
6
|
+
return await claim;
|
|
7
|
+
}
|
|
8
|
+
finally {
|
|
9
|
+
sink?.observe?.(EXECUTION_CLAIM_METRIC, (performance.now() - started) / 1000, { step });
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=claim-latency.js.map
|
package/dist/http/dispatch.js
CHANGED
|
@@ -131,7 +131,7 @@ export function createDispatch(ctx) {
|
|
|
131
131
|
const engineSignal = buildEngineSignal(signal);
|
|
132
132
|
const declaredMods = deploymentModPackagesFact(deps);
|
|
133
133
|
const configHash = createHash("sha256")
|
|
134
|
-
.update(JSON.stringify({ model: deps.config.model.id, sessionBackend: deps.config.sessionBackend, dbBackend: deps.config.dbBackend, dataRoot: deps.config.localDataRoot, configProvider: deps.config.configProvider, remoteExec: deps.config.remoteExec
|
|
134
|
+
.update(JSON.stringify({ model: deps.config.model.id, sessionBackend: deps.config.sessionBackend, dbBackend: deps.config.dbBackend, dataRoot: deps.config.localDataRoot, configProvider: deps.config.configProvider, remoteExec: deps.config.remoteExec.provider, port: deps.config.port }))
|
|
135
135
|
.digest("hex")
|
|
136
136
|
.slice(0, 16);
|
|
137
137
|
sendJson(res, 200, {
|
|
@@ -8,6 +8,7 @@ import { declaredApprovalFrameFamilies, resolveStreamApprovalGate } from "../too
|
|
|
8
8
|
import { buildActiveRunConflict } from "./active-run-conflict.js";
|
|
9
9
|
import { commitExecutionContext } from "./execution-context.js";
|
|
10
10
|
import { rootSessionAbsentAnswer } from "./execution-context.js";
|
|
11
|
+
import { timeClaim } from "./claim-latency.js";
|
|
11
12
|
import { commitSessionInheritEnv } from "./session-inherit-env.js";
|
|
12
13
|
import { clearTurnActivity, readTurnActivityMs } from "../turn-activity.js";
|
|
13
14
|
import { recordResourceWindow } from "../resource-window.js";
|
|
@@ -21,7 +22,7 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
|
|
|
21
22
|
const sessionId = args.prepared.spec.sessionId;
|
|
22
23
|
const taskId = args.clientTaskId ?? uuidv7();
|
|
23
24
|
const trustedInternals = await heldWorkflowParkDecisions(deps, sessionId, args.workflowDecision?.carried);
|
|
24
|
-
const created = await runStore.createRun(taskId, sessionId, args.prepared.auth?.principal ?? null, deps.instanceId ?? "default", { ...runMeta(args.prepared, args.source), identity: args.identity, idem: args.idem ?? null });
|
|
25
|
+
const created = await timeClaim(deps.metrics, "run_claim", runStore.createRun(taskId, sessionId, args.prepared.auth?.principal ?? null, deps.instanceId ?? "default", { ...runMeta(args.prepared, args.source), identity: args.identity, idem: args.idem ?? null }));
|
|
25
26
|
if (created.ok)
|
|
26
27
|
clearTurnActivity(taskId);
|
|
27
28
|
if (created.ok)
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
* ④ 只 fresh:设备准入链(体 `deviceId` 只在 device 车道收;准入链三步 + 首绑目标三读,全读;判的是步 ① 那一读)。
|
|
20
20
|
*
|
|
21
21
|
* 入口三形(判别联合,{@link ExecutionEntry}):`fresh`(提交腿:根 = authorizer 交出的 `auth.sessionId`,`judged` = authorizer 判过的头)·
|
|
22
|
-
* `continuation`(续跑家族与停驻赎回:根 = checkpoint / 停驻行的根;不带 `judged` —— 步 ①
|
|
22
|
+
* `continuation`(续跑家族与停驻赎回:根 = checkpoint / 停驻行的根;不带 `judged` —— 步 ① 那一读就是这条执行对根的唯一一读,认领之后由写口按它判;
|
|
23
|
+
* run 外回退口 `POST /v1/sessions/:id/rewind-files` 同形:根 = 路径上的会话,它不认领、不提交放置,只取步 ①–③ 的拒)·
|
|
23
24
|
* `service`(服务 run:没有会话根)。
|
|
24
25
|
* `fresh` / `continuation` 两形型上恒带 `root: string`,`service` 形没有 `root` 键 —— 「按一只缺席的根去读行」在型上不可表达。
|
|
25
26
|
*
|
|
@@ -202,6 +203,7 @@ export interface ExecutionCommitDeps {
|
|
|
202
203
|
} | undefined;
|
|
203
204
|
readonly metrics?: {
|
|
204
205
|
inc(name: string, labels?: Record<string, string>): void;
|
|
206
|
+
observe?(name: string, value: number, labels?: Record<string, string>): void;
|
|
205
207
|
} | undefined;
|
|
206
208
|
}
|
|
207
209
|
/**
|
|
@@ -8,6 +8,7 @@ import { gateExecutionLane } from "../runtime-caps-resolver.js";
|
|
|
8
8
|
import { releaseClaimedRun } from "./claimed-run.js";
|
|
9
9
|
import { sessionIdRetiredFor } from "../deleted-session-ids.js";
|
|
10
10
|
import { refusalBody } from "./refusal-body.js";
|
|
11
|
+
import { timeClaim } from "./claim-latency.js";
|
|
11
12
|
export async function resolveExecutionContext(deps, entry) {
|
|
12
13
|
const lanes = deployedExecutionLanes(deps.config);
|
|
13
14
|
switch (entry.kind) {
|
|
@@ -203,6 +204,9 @@ export async function commitExecutionContext(deps, claim, ctx) {
|
|
|
203
204
|
if (ctx.occupant === undefined || seat === undefined)
|
|
204
205
|
return null;
|
|
205
206
|
const occupant = ctx.occupant;
|
|
207
|
+
return timeClaim(deps.metrics, "placement_commit", commitPlacement(deps, claim, ctx, seat, occupant));
|
|
208
|
+
}
|
|
209
|
+
async function commitPlacement(deps, claim, ctx, seat, occupant) {
|
|
206
210
|
const release = (why) => releaseClaim(deps, claim, why);
|
|
207
211
|
const deadlineMs = placementWriteDeadlineMs(deps.config?.runStaleSec);
|
|
208
212
|
const withDeadline = async (work) => {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* ## 规则(三层,与 operator 面既有层序逐字同序)
|
|
12
12
|
* ① core 的**本口专属**码(闭集穷举 `switch`,见 {@link sentCoreCode})—— 码与状态两面同;文案按受众(下一节,一条规则);
|
|
13
|
-
* ②
|
|
13
|
+
* ② **店形拒**一族(按族表读;成员举例 `memory.target_outside_store` / `memory.scope_home_not_directory` / 引擎 7.65.0 起 `memory.control_plane_links_unsupported`)—— 跨面 store 状态码,409、码原样
|
|
14
14
|
* (判据 `memory-store-refusal.ts` 一只;运维行 `memory_store_form_refused { code, path?, form? }` 两受众同一只铸点,路径只在那里);
|
|
15
15
|
* ③ `ControlPlaneCorruptError` —— typed 500 `internal.memory_control_plane_corrupt`(fail-closed 信号,一码两义;映射本体在 `memory-control-plane-corrupt-reply.ts` 那一只,导出导入 / 外源 / 整理 / 合规各口同调);
|
|
16
16
|
* 其余 ⇒ `false`(未分类,调用方发它自己那句 500 且**不回显**内部文本)。
|
|
@@ -63,6 +63,9 @@ export function sentMemoryComplianceFailure(res, err, audience, logger) {
|
|
|
63
63
|
case "memory.scope_home_not_directory":
|
|
64
64
|
sendError(res, 409, "memory.scope_home_not_directory", "session memory erasure refused — a scope directory in this deployment's memory store is not a real directory (it was replaced by a link or a file), so this erasure did not complete; ask the operator to put the real directory back, then resend with the same requestId");
|
|
65
65
|
return true;
|
|
66
|
+
case "memory.control_plane_links_unsupported":
|
|
67
|
+
sendError(res, 409, "memory.control_plane_links_unsupported", "session memory erasure refused — this deployment's memory control plane is on a file system that cannot make hard links, so its memory store does not open and this erasure changed nothing; ask the operator to move the control plane to a volume that supports hard links, then resend with the same requestId");
|
|
68
|
+
return true;
|
|
66
69
|
default:
|
|
67
70
|
return assertNever(storeForm.code);
|
|
68
71
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* **operator 面 HTTP 映射** + 两受众共用的**运维行** ——
|
|
2
|
+
* **店形拒**一族(`memory.target_outside_store` / `memory.scope_home_not_directory` / `memory.control_plane_links_unsupported`,族表与判据在 `src/memory-store-refusal.ts`)的
|
|
3
|
+
* **operator 面 HTTP 映射** + 两受众共用的**运维行** —— operator 面唯一一处码字面量(会话所有者面的逐码臂在 `http/memory-compliance-failure-reply.ts`;引擎加码 ⇒ 两处各补一臂)。挂在每一个触 store 树的 operator 记忆口的已分类收尾上:
|
|
4
4
|
* `routes/memory-compliance.ts`(出处 / 抹除;经 `http/memory-compliance-failure-reply.ts` 的 operator 受众臂)· `routes/memory-origin.ts`(外源列表 / 清标;**不含**只读控制面的清标账列表 ——
|
|
5
5
|
* core 的 wire 表逐字:只碰控制面的面不判,给它挂一条永远收不到的码路径正是附录 A 活触发门要防的形)·
|
|
6
6
|
* `routes/memory-bundle.ts`(导出 / 导入)。会话所有者的自助抹除口**不走这里的 wire 句**(经 `http/memory-compliance-failure-reply.ts` 的会话所有者受众臂:同码、固定文案),
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* `POST /v1/admin/memory/consolidation/run` 也不走这里:那口把 core 的**任何**有码拒一律收成 409 `memory.consolidation_refused`
|
|
9
9
|
* (码进文案,附录 A 该行),这一族已在那条臂上,不再给它第二个码。
|
|
10
10
|
*
|
|
11
|
-
* ## 为什么 409(
|
|
11
|
+
* ## 为什么 409(一族同一条)
|
|
12
12
|
* core 的读法:operator 要处置的盘形(去掉 store 里那只通到外面的目录链 / 把 scope home 放回真目录),不是可重试的 IO 故障,也不是对调用方的裁决。与
|
|
13
13
|
* `memory.erasure_census_incomplete` 同一类(**当前 store 状态**下这次做不了;状态修好后重发即改判)⇒ 409,不 500(服务器没坏)、
|
|
14
14
|
* 不 4xx 请求族(请求没写错)。抹除面:链先于请求在场 ⇒ core 在提交点之前拒,零删除、零 journal(真 core 格钉在
|
|
@@ -12,6 +12,9 @@ export function sentMemoryStoreRefusal(res, err, logger) {
|
|
|
12
12
|
case "memory.scope_home_not_directory":
|
|
13
13
|
sendError(res, 409, "memory.scope_home_not_directory", refusal.message);
|
|
14
14
|
return true;
|
|
15
|
+
case "memory.control_plane_links_unsupported":
|
|
16
|
+
sendError(res, 409, "memory.control_plane_links_unsupported", refusal.message);
|
|
17
|
+
return true;
|
|
15
18
|
default: {
|
|
16
19
|
const unhandled = refusal.code;
|
|
17
20
|
throw new Error(`unhandled store-form refusal word: ${String(unhandled)}`);
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import type { ServiceConfig } from "../config-types.js";
|
|
17
17
|
import type { Logger } from "../observability/logger.js";
|
|
18
18
|
import type { RunRecord } from "../plugins/store-contracts.js";
|
|
19
|
+
import type { SessionHead } from "@sema-agent/core";
|
|
19
20
|
import { type OwnerAwareSessionStore, type Principal, type SessionCaller, type SessionOwnerCell } from "../security.js";
|
|
20
21
|
import { type IdentityWiring, type OperatorRoster } from "./principal-gate.js";
|
|
21
22
|
/** 调用点挂哪条臂(闭集):`owner` = 只有属主判官;`owner_or_operator` = 属主判官 ∨ 显式运维(`explicitOperatorOk`,唯一读法)。 */
|
|
@@ -46,6 +47,16 @@ export declare function readOwnedRun(config: OwnerAccessConfig, runStore: {
|
|
|
46
47
|
export declare function readOwnedSession(w: IdentityWiring & {
|
|
47
48
|
readonly config: OwnerAccessConfig;
|
|
48
49
|
}, store: Pick<OwnerAwareSessionStore, "placementOf"> & Required<Pick<OwnerAwareSessionStore, "headOf">>, caller: SessionCaller, sessionId: string, arm: OwnerArm): Promise<SessionOwnerCell | undefined>;
|
|
50
|
+
/**
|
|
51
|
+
* 同一只读口,连头一起交回:门过了的调用点还要读这只会话的**内容**(坏档判据 `leafOfAdmittedHead` 读头的叶子;门之前不许读)或头上的属主原值时用它 ——
|
|
52
|
+
* 一次头读、一只判官,与 {@link readOwnedSession} 是同一读(那一只就是本函数取 `cell`)。`undefined` 的读法同上。
|
|
53
|
+
*/
|
|
54
|
+
export declare function readOwnedSessionHead(w: IdentityWiring & {
|
|
55
|
+
readonly config: OwnerAccessConfig;
|
|
56
|
+
}, store: Pick<OwnerAwareSessionStore, "placementOf"> & Required<Pick<OwnerAwareSessionStore, "headOf">>, caller: SessionCaller, sessionId: string, arm: OwnerArm): Promise<{
|
|
57
|
+
readonly cell: SessionOwnerCell;
|
|
58
|
+
readonly head: SessionHead;
|
|
59
|
+
} | undefined>;
|
|
49
60
|
/**
|
|
50
61
|
* 🔴 **会话读面的属主读的第三个结局**:有主 / 无此行 / **属主读不出**(店交出具名拒 {@link SessionOwnerUnreadableError} —— 会话的行事实没有落下,补铸它要读的存档又用不了;
|
|
51
62
|
* 谁也判不了「这是不是你的会话」)。第三个读作第二个:**与「没有这一行」同一个值**(`undefined`),调用点落的就是它们对「不存在 / 非属主」的那一臂 ——
|
|
@@ -32,11 +32,14 @@ export async function readOwnedRun(config, runStore, principal, taskId, arm) {
|
|
|
32
32
|
return row !== undefined && runRowReadable(config, principal, row.owner, arm) ? row : undefined;
|
|
33
33
|
}
|
|
34
34
|
export async function readOwnedSession(w, store, caller, sessionId, arm) {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
return (await readOwnedSessionHead(w, store, caller, sessionId, arm))?.cell;
|
|
36
|
+
}
|
|
37
|
+
export async function readOwnedSessionHead(w, store, caller, sessionId, arm) {
|
|
38
|
+
const head = await store.headOf(sessionId);
|
|
39
|
+
if (head === undefined || head.owner === undefined)
|
|
37
40
|
return undefined;
|
|
38
|
-
const cell = await sessionOwnerCellOf(store, sessionId, owner);
|
|
39
|
-
return operatorArmHolds(w.config, caller.principal, arm) || sessionOwnerReadable(caller, cell, w) ? cell : undefined;
|
|
41
|
+
const cell = await sessionOwnerCellOf(store, sessionId, head.owner);
|
|
42
|
+
return operatorArmHolds(w.config, caller.principal, arm) || sessionOwnerReadable(caller, cell, w) ? { cell, head } : undefined;
|
|
40
43
|
}
|
|
41
44
|
export async function foldOwnerUnreadable(logger, face, sessionId, read) {
|
|
42
45
|
try {
|