@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
|
@@ -1,20 +1,86 @@
|
|
|
1
1
|
export const ENGINE_LEASE_TICK_MS = 15_000;
|
|
2
|
+
export const ENGINE_LANES = ["handover", "orphan", "awaitingFirstAttach", "owned"];
|
|
3
|
+
export const ENGINE_LANE_TABLE = Object.freeze({
|
|
4
|
+
handover: { countsBackgroundShells: true, sighupYieldsToLease: true },
|
|
5
|
+
orphan: { countsBackgroundShells: false, sighupYieldsToLease: false },
|
|
6
|
+
awaitingFirstAttach: { countsBackgroundShells: false, sighupYieldsToLease: false },
|
|
7
|
+
owned: { countsBackgroundShells: false, sighupYieldsToLease: false },
|
|
8
|
+
});
|
|
9
|
+
export function deriveEngineLane(s) {
|
|
10
|
+
if (s.handedOver)
|
|
11
|
+
return "handover";
|
|
12
|
+
if (s.orphaned)
|
|
13
|
+
return "orphan";
|
|
14
|
+
return s.touched ? "owned" : "awaitingFirstAttach";
|
|
15
|
+
}
|
|
16
|
+
export function laneWork(lane, s, reader) {
|
|
17
|
+
const row = lane === undefined ? undefined : ENGINE_LANE_TABLE[lane];
|
|
18
|
+
switch (reader) {
|
|
19
|
+
case "sighup":
|
|
20
|
+
if (row?.sighupYieldsToLease === true)
|
|
21
|
+
return true;
|
|
22
|
+
break;
|
|
23
|
+
case "lease":
|
|
24
|
+
break;
|
|
25
|
+
default: {
|
|
26
|
+
const unhandled = reader;
|
|
27
|
+
throw new Error(`laneWork: unknown reader ${String(unhandled)}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return s.attached > 0 || s.inflight > 0 || (row?.countsBackgroundShells === true && s.backgroundShells() > 0);
|
|
31
|
+
}
|
|
32
|
+
export const HANDOVER_REASONS = ["background_exit"];
|
|
33
|
+
export const backgroundShellsUnreachable = () => {
|
|
34
|
+
throw new Error("engine lease: background-shell count read on a lane table row that counts it, but no handover lane is armed (invariant broken)");
|
|
35
|
+
};
|
|
2
36
|
export function createEngineLeaseTracker(deps) {
|
|
3
37
|
const orphanLane = deps.orphan;
|
|
4
|
-
const
|
|
38
|
+
const handoverLane = deps.handover;
|
|
39
|
+
const shortestLingerMs = Math.min(deps.lingerMs, orphanLane?.lingerMs ?? Number.POSITIVE_INFINITY, handoverLane?.lingerMs ?? Number.POSITIVE_INFINITY);
|
|
5
40
|
const tickMs = deps.tickMs ?? Math.min(ENGINE_LEASE_TICK_MS, shortestLingerMs);
|
|
6
|
-
const
|
|
41
|
+
const firstAttachWindowMs = Math.max(deps.lingerMs, deps.firstAttach.windowMs);
|
|
7
42
|
const now = deps.now ?? (() => performance.now());
|
|
8
43
|
let stopped = false;
|
|
9
|
-
|
|
44
|
+
const armedAt = now();
|
|
45
|
+
const armedEpochMs = Date.now();
|
|
46
|
+
const epochOf = (mono) => Math.round(armedEpochMs + (mono - armedAt));
|
|
47
|
+
let idleSince = armedAt;
|
|
48
|
+
let handedOver = null;
|
|
49
|
+
const backgroundShells = handoverLane?.backgroundShells ?? backgroundShellsUnreachable;
|
|
50
|
+
function windowOf(lane) {
|
|
51
|
+
switch (lane) {
|
|
52
|
+
case "handover":
|
|
53
|
+
return handoverLane?.lingerMs ?? Number.POSITIVE_INFINITY;
|
|
54
|
+
case "orphan":
|
|
55
|
+
return orphanLane?.lingerMs ?? Number.POSITIVE_INFINITY;
|
|
56
|
+
case "awaitingFirstAttach":
|
|
57
|
+
return firstAttachWindowMs;
|
|
58
|
+
case "owned":
|
|
59
|
+
return deps.lingerMs;
|
|
60
|
+
default: {
|
|
61
|
+
const unhandled = lane;
|
|
62
|
+
throw new Error(`engine lease: unknown lane ${String(unhandled)}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function laneNow() {
|
|
67
|
+
const orphaned = orphanLane ? orphanLane.read() : null;
|
|
68
|
+
const lane = deriveEngineLane({ handedOver: handedOver !== null, orphaned: orphaned !== null, touched: deps.firstAttach.firstTouchAt() !== null });
|
|
69
|
+
return { lane, orphaned };
|
|
70
|
+
}
|
|
10
71
|
deps.log("engine_lease_armed", {
|
|
11
72
|
lingerMs: Number.isFinite(deps.lingerMs) ? deps.lingerMs : null,
|
|
12
73
|
orphanLingerMs: orphanLane?.lingerMs ?? null,
|
|
74
|
+
firstAttachMs: deps.firstAttach.windowMs,
|
|
75
|
+
firstAttachWindowMs: Number.isFinite(firstAttachWindowMs) ? firstAttachWindowMs : null,
|
|
76
|
+
handoverLingerMs: handoverLane?.lingerMs ?? null,
|
|
13
77
|
tickMs,
|
|
14
78
|
note: "engine self-exits once NO shell session is attached AND no run is in flight AND no HTTP touch, " +
|
|
15
|
-
"continuously for the linger window — via the same graceful drain as SIGTERM. The window is " +
|
|
16
|
-
"
|
|
17
|
-
"once SEMA_PARENT_PID's process is gone
|
|
79
|
+
"continuously for the linger window — via the same graceful drain as SIGTERM. The window is chosen by lane: " +
|
|
80
|
+
"ENGINE_HANDOVER_LINGER_MS after POST /v1/engine/handover (until the next POST /v1/engine/attach; running background " +
|
|
81
|
+
"shells count as work only there); ENGINE_ORPHAN_LINGER_MS once SEMA_PARENT_PID's process is gone; otherwise " +
|
|
82
|
+
"max(ENGINE_LINGER_MS, ENGINE_FIRST_ATTACH_MS) until the first non-probe HTTP request ever arrives, and ENGINE_LINGER_MS after it. " +
|
|
83
|
+
"Set ENGINE_AUTO_EXIT=false and unset SEMA_PARENT_PID to disable.",
|
|
18
84
|
});
|
|
19
85
|
const timer = setInterval(() => {
|
|
20
86
|
if (stopped)
|
|
@@ -25,7 +91,8 @@ export function createEngineLeaseTracker(deps) {
|
|
|
25
91
|
}
|
|
26
92
|
const attached = deps.attachedStreams();
|
|
27
93
|
const inflight = deps.inflight();
|
|
28
|
-
|
|
94
|
+
const { lane, orphaned } = laneNow();
|
|
95
|
+
if (laneWork(lane, { attached, inflight, backgroundShells }, "lease")) {
|
|
29
96
|
idleSince = null;
|
|
30
97
|
return;
|
|
31
98
|
}
|
|
@@ -34,8 +101,7 @@ export function createEngineLeaseTracker(deps) {
|
|
|
34
101
|
idleSince = t;
|
|
35
102
|
return;
|
|
36
103
|
}
|
|
37
|
-
const
|
|
38
|
-
const linger = orphanLane && orphaned !== null ? orphanLane.lingerMs : deps.lingerMs;
|
|
104
|
+
const linger = windowOf(lane);
|
|
39
105
|
if (t - deps.lastActivityAt() < linger)
|
|
40
106
|
return;
|
|
41
107
|
if (t - deps.lastAttachEndedAt() < linger)
|
|
@@ -43,22 +109,17 @@ export function createEngineLeaseTracker(deps) {
|
|
|
43
109
|
if (t - idleSince < linger)
|
|
44
110
|
return;
|
|
45
111
|
stop();
|
|
112
|
+
const orphanedNow = lane === "orphan" ? orphaned : null;
|
|
46
113
|
const report = {
|
|
47
|
-
|
|
48
|
-
reason:
|
|
114
|
+
lane,
|
|
115
|
+
reason: orphanedNow?.reason ?? "idle",
|
|
49
116
|
idleForMs: Math.round(t - Math.max(idleSince, deps.lastActivityAt(), deps.lastAttachEndedAt())),
|
|
50
117
|
attached,
|
|
51
118
|
inflight,
|
|
52
119
|
lingerMs: linger,
|
|
53
|
-
...(
|
|
120
|
+
...(orphanedNow !== null ? { orphanedForMs: Math.round(t - orphanedNow.since) } : {}),
|
|
54
121
|
};
|
|
55
|
-
deps.log("engine_lease_auto_exit", {
|
|
56
|
-
...report,
|
|
57
|
-
note: orphaned !== null
|
|
58
|
-
? "the parent that spawned this engine is gone AND nobody is attached, no run is in flight and no HTTP touch " +
|
|
59
|
-
"for the full orphan linger window — entering the SIGTERM graceful drain path (exit code 0; see exit-last.json)"
|
|
60
|
-
: "no attached shell session, no in-flight run and no HTTP touch for the full linger window — entering the SIGTERM graceful drain path",
|
|
61
|
-
});
|
|
122
|
+
deps.log("engine_lease_auto_exit", { ...report, note: AUTO_EXIT_NOTES[lane] });
|
|
62
123
|
deps.selfExit(report);
|
|
63
124
|
}, tickMs);
|
|
64
125
|
timer.unref?.();
|
|
@@ -68,11 +129,76 @@ export function createEngineLeaseTracker(deps) {
|
|
|
68
129
|
stopped = true;
|
|
69
130
|
clearInterval(timer);
|
|
70
131
|
}
|
|
132
|
+
function firstAttachFacts() {
|
|
133
|
+
if (!Number.isFinite(firstAttachWindowMs))
|
|
134
|
+
return undefined;
|
|
135
|
+
const touchedAt = deps.firstAttach.firstTouchAt();
|
|
136
|
+
return Object.freeze({ windowMs: firstAttachWindowMs, since: armedEpochMs, attachedAt: touchedAt === null ? null : epochOf(touchedAt) });
|
|
137
|
+
}
|
|
138
|
+
function handoverFacts() {
|
|
139
|
+
if (handedOver === null || handoverLane === undefined)
|
|
140
|
+
return undefined;
|
|
141
|
+
return Object.freeze({
|
|
142
|
+
since: epochOf(handedOver.since),
|
|
143
|
+
reason: handedOver.reason,
|
|
144
|
+
lingerMs: handoverLane.lingerMs,
|
|
145
|
+
backgroundShells: handoverLane.backgroundShells(),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const door = handoverLane === undefined
|
|
149
|
+
? undefined
|
|
150
|
+
: {
|
|
151
|
+
get observing() {
|
|
152
|
+
return !stopped;
|
|
153
|
+
},
|
|
154
|
+
enter(reason) {
|
|
155
|
+
if (handedOver === null) {
|
|
156
|
+
const from = laneNow().lane;
|
|
157
|
+
handedOver = { since: now(), reason };
|
|
158
|
+
deps.log("engine_lane_changed", { from, to: "handover", reason: "handover_requested", since: epochOf(handedOver.since) });
|
|
159
|
+
}
|
|
160
|
+
const facts = handoverFacts();
|
|
161
|
+
if (facts === undefined)
|
|
162
|
+
throw new Error("engine lease: handover facts absent right after entering the handover lane (invariant broken)");
|
|
163
|
+
return { lane: "handover", since: facts.since, lingerMs: facts.lingerMs, backgroundShells: facts.backgroundShells };
|
|
164
|
+
},
|
|
165
|
+
attach() {
|
|
166
|
+
if (handedOver === null)
|
|
167
|
+
return { lane: laneNow().lane };
|
|
168
|
+
const t = now();
|
|
169
|
+
const handedOverForMs = Math.round(t - handedOver.since);
|
|
170
|
+
handedOver = null;
|
|
171
|
+
const to = laneNow().lane;
|
|
172
|
+
deps.log("engine_lane_changed", { from: "handover", to, reason: "attached", since: epochOf(t) });
|
|
173
|
+
return { lane: to, handedOverForMs };
|
|
174
|
+
},
|
|
175
|
+
};
|
|
71
176
|
return {
|
|
72
177
|
stop,
|
|
73
178
|
get facts() {
|
|
74
|
-
|
|
179
|
+
if (stopped || !Number.isFinite(shortestLingerMs))
|
|
180
|
+
return undefined;
|
|
181
|
+
const firstAttach = firstAttachFacts();
|
|
182
|
+
const handover = handoverFacts();
|
|
183
|
+
return Object.freeze({
|
|
184
|
+
lingerMs: shortestLingerMs,
|
|
185
|
+
tickMs,
|
|
186
|
+
lane: laneNow().lane,
|
|
187
|
+
...(firstAttach !== undefined ? { firstAttach } : {}),
|
|
188
|
+
...(handover !== undefined ? { handover } : {}),
|
|
189
|
+
});
|
|
75
190
|
},
|
|
191
|
+
lane: () => (stopped ? undefined : laneNow().lane),
|
|
192
|
+
...(door !== undefined ? { handover: door } : {}),
|
|
76
193
|
};
|
|
77
194
|
}
|
|
195
|
+
const AUTO_EXIT_NOTES = {
|
|
196
|
+
handover: "this engine was handed over to the background (POST /v1/engine/handover) and nobody attached, no run and no background shell " +
|
|
197
|
+
"was running and no HTTP touch for the full ENGINE_HANDOVER_LINGER_MS window — entering the SIGTERM graceful drain path",
|
|
198
|
+
orphan: "the parent that spawned this engine is gone AND nobody is attached, no run is in flight and no HTTP touch " +
|
|
199
|
+
"for the full orphan linger window — entering the SIGTERM graceful drain path (exit code 0; see exit-last.json)",
|
|
200
|
+
awaitingFirstAttach: "no non-probe HTTP request EVER reached this engine within the first-attach window (max(ENGINE_LINGER_MS, ENGINE_FIRST_ATTACH_MS)) " +
|
|
201
|
+
"and nothing is attached or in flight — entering the SIGTERM graceful drain path",
|
|
202
|
+
owned: "no attached shell session, no in-flight run and no HTTP touch for the full linger window — entering the SIGTERM graceful drain path",
|
|
203
|
+
};
|
|
78
204
|
//# sourceMappingURL=engine-lease.js.map
|
|
@@ -20,6 +20,7 @@ import { type TaskAttachmentStore } from "../plugins/task-attachment-store.js";
|
|
|
20
20
|
import type { TaskInheritEnv } from "../request-value-closure.js";
|
|
21
21
|
import { type HeadRead } from "../session-incarnation.js";
|
|
22
22
|
import type { SpaceReclaimer } from "../workspace-space.js";
|
|
23
|
+
import { type SessionResidentCounts } from "../session-resident-background.js";
|
|
23
24
|
/**
|
|
24
25
|
* per-session 的 **cwd / shellEnv 登记簿**(TOC cwd seam + R-survey shellEnv seam)+ 它们的**租户围栏**。
|
|
25
26
|
*
|
|
@@ -55,6 +56,9 @@ export interface SessionScopedRegistries {
|
|
|
55
56
|
* 请求放宽凭据面)。
|
|
56
57
|
*/
|
|
57
58
|
setSessionInheritEnv(sid: string, policy: TaskInheritEnv | undefined): void;
|
|
59
|
+
/** 本进程见过的会话 → 属主(围栏表本身,只读视图;`null` = 匿名)。**会话驻留后台行的枚举源**:`session-resident-background.ts` 逐会话问注册表
|
|
60
|
+
* (core 的访问元组要带 scope,没有进程总数的读口),读的就是这一张 —— 每次提交写一笔、`dropSession` 清一笔、LRU 有界,不另立第二张会话名册。 */
|
|
61
|
+
readonly sessionOwners: ReadonlyMap<string, string | null>;
|
|
58
62
|
/** 记录/对拍本会话的主人。与上次不同 ⇒ **先清两张表**再记新主(换主即清)。`null` = 匿名会话,
|
|
59
63
|
* 它同样是一个**稳定**的身份值(不是「未知」),所以匿名重入不会被判成换主。 */
|
|
60
64
|
fenceSessionOwner(sid: string, owner: string | null): void;
|
|
@@ -171,6 +175,7 @@ export declare function createExecutionEnv(ctx: ExecutionEnvCtx): {
|
|
|
171
175
|
setSessionInheritEnv: (sid: string, policy: TaskInheritEnv | undefined) => void;
|
|
172
176
|
fenceSessionOwner: (sid: string, owner: string | null) => void;
|
|
173
177
|
dropSessionScoped: (sid: string) => void;
|
|
178
|
+
sessionResidentBackground: () => SessionResidentCounts;
|
|
174
179
|
executionEnvFactory: import("@sema-agent/core").ExecutionEnvFactory | undefined;
|
|
175
180
|
worktreeReap: (() => Promise<void>) | undefined;
|
|
176
181
|
sendUserFileTaskEnvs: TaskEnvRegistry | undefined;
|
|
@@ -26,6 +26,7 @@ import { withRemoteScratchpad } from "../plugins/remote-scratchpad.js";
|
|
|
26
26
|
import { FileSchedulerBackend, defaultSchedulerStorePath } from "../plugins/scheduler-support.js";
|
|
27
27
|
import * as facts from "../capability-facts.js";
|
|
28
28
|
import { materializeAttachmentsInto } from "../plugins/task-attachment-store.js";
|
|
29
|
+
import { createAttachmentMaterializingFactory } from "../execution-env-purpose.js";
|
|
29
30
|
import { reapOrphanWorktrees, withWorktreeIsolation } from "../plugins/worktree-isolation.js";
|
|
30
31
|
import { derivePkgSourceEnv } from "../sandbox-pkg-source.js";
|
|
31
32
|
import { effectiveHostWorkspace } from "../task-cwd.js";
|
|
@@ -37,9 +38,10 @@ import { workspaceSnapshotTimeoutOf } from "../workspace-space.js";
|
|
|
37
38
|
import { sayEgressPolicyReading } from "../sandbox-egress-policy.js";
|
|
38
39
|
import { sandboxPathEnvSlots, withSandboxPathEnvSlot } from "./deferred-sandbox-path-env.js";
|
|
39
40
|
import { createLaneHomeAbsenceReporter } from "../lane-home-probe.js";
|
|
40
|
-
import {
|
|
41
|
+
import { buildLaneHomeBootNote, buildLaneHomeClaim, capabilitiesFor, executionLaneOf, laneBindsReads, laneIsolated, laneSandboxReadFloorState } from "../execution-lane-caps.js";
|
|
41
42
|
import { warnReadDenyFloorUnkept } from "./execution-lane.js";
|
|
42
43
|
import { seatText } from "../trace/exit-seat.js";
|
|
44
|
+
import { countSessionResidentBackground } from "../session-resident-background.js";
|
|
43
45
|
function boundedSessionSetter(map, maxEntries) {
|
|
44
46
|
return (sid, value) => {
|
|
45
47
|
map.delete(sid);
|
|
@@ -79,6 +81,7 @@ export function createSessionScopedRegistries(maxEntries = MAX_CWD_SESSIONS) {
|
|
|
79
81
|
return {
|
|
80
82
|
perSessionCwd,
|
|
81
83
|
perSessionShellEnv,
|
|
84
|
+
sessionOwners: owners,
|
|
82
85
|
setSessionCwd: (sid, cwd) => {
|
|
83
86
|
perSessionCwd.set(sid, cwd);
|
|
84
87
|
touch(sid, undefined);
|
|
@@ -218,14 +221,7 @@ function persistNeedsOccupant(persist) {
|
|
|
218
221
|
return false;
|
|
219
222
|
}
|
|
220
223
|
}
|
|
221
|
-
|
|
222
|
-
if (!DEPLOYMENT_SHAPE_PREDICATES.singleMachineDefaultLane.holds({ remoteExec }))
|
|
223
|
-
return "non-isolated target — actions are permanent; rely on the policy gate + HITL (design/61 §5)";
|
|
224
|
-
const sandboxLanes = REMOTE_EXEC_PROVIDER_WORDS.filter((lane) => laneIsolated(lane)).join("|");
|
|
225
|
-
return ("hands run on this machine with no OS sandbox (single-machine default: REMOTE_EXEC unset, CONFIG_PROVIDER=local) — actions are permanent, " +
|
|
226
|
-
"and the Bash read boundary is a text-level friction floor, not a safety boundary; the safety boundary is a sandbox lane: " +
|
|
227
|
-
`set REMOTE_EXEC=${sandboxLanes} (see docs/DEPLOY-PREREQS.md, section "S-281")`);
|
|
228
|
-
}
|
|
224
|
+
const REAL_SYSTEM_NOTE = "non-isolated target — actions are permanent; rely on the policy gate + HITL (design/61 §5)";
|
|
229
225
|
function hostWorktreeUnwrappedRefusal() {
|
|
230
226
|
return Object.assign(new Error('isolation "worktree" was requested but cannot be honored: this host lane has no worktree wrapper active ' +
|
|
231
227
|
"(WORKTREE_ISOLATION_ENABLED=true together with WORKTREE_REPO_ROOT=<a git repository> turns it on; a repo root that does not exist or has no `.git` leaves it off — see the boot log), " +
|
|
@@ -249,7 +245,7 @@ export function createExecutionEnv(ctx) {
|
|
|
249
245
|
let sandboxGitPlane = createSandboxGitPlaneSeat((r) => sandboxGitPlanePremisesOf(config, r), {});
|
|
250
246
|
let worktreeReap;
|
|
251
247
|
const workspaceFillOf = taskAttachmentStore === undefined ? undefined : (fctx) => createWorkspaceFill({ store: taskAttachmentStore, sessionId: fctx.sessionId, seeds: ownsItsWorkspace(fctx), logger });
|
|
252
|
-
if (config.remoteExec
|
|
248
|
+
if (config.remoteExec.provider === "e2b") {
|
|
253
249
|
executionEnvFactory = e2bExecutionEnvFactory({
|
|
254
250
|
apiKey: config.remoteExec.apiKey,
|
|
255
251
|
...homeDirSeat, ...homeAbsentNotice,
|
|
@@ -265,7 +261,7 @@ export function createExecutionEnv(ctx) {
|
|
|
265
261
|
metrics,
|
|
266
262
|
}, ctx.laneDeps?.e2b);
|
|
267
263
|
}
|
|
268
|
-
else if (config.remoteExec
|
|
264
|
+
else if (config.remoteExec.provider === "k8s") {
|
|
269
265
|
const k8sCfg = {
|
|
270
266
|
image: config.remoteExec.image,
|
|
271
267
|
...homeDirSeat, ...homeAbsentNotice,
|
|
@@ -308,7 +304,7 @@ export function createExecutionEnv(ctx) {
|
|
|
308
304
|
.then((gaps) => sayEgressPolicyReading(gaps, logger, k8sCfg.namespace))
|
|
309
305
|
.catch((e) => logger.error("sandbox_egress_policy_check_crashed", { error: seatText("operator-log-text", e instanceof Error ? e.message : String(e)), effect: "the sandbox egress self-check did not complete this boot — the lane runs; the egress policy was not read" }));
|
|
310
306
|
}
|
|
311
|
-
else if (config.remoteExec
|
|
307
|
+
else if (config.remoteExec.provider === "ssh") {
|
|
312
308
|
executionEnvFactory = sshExecutionEnvFactory({
|
|
313
309
|
host: config.remoteExec.host,
|
|
314
310
|
username: config.remoteExec.username,
|
|
@@ -317,9 +313,10 @@ export function createExecutionEnv(ctx) {
|
|
|
317
313
|
...(config.remoteExec.port != null ? { port: config.remoteExec.port } : {}),
|
|
318
314
|
...(config.remoteExec.mountPath ? { mountPath: config.remoteExec.mountPath } : {}),
|
|
319
315
|
...(config.remoteExec.hostKey !== undefined ? { hostKey: config.remoteExec.hostKey } : {}),
|
|
316
|
+
logger,
|
|
320
317
|
});
|
|
321
318
|
}
|
|
322
|
-
else if (config.remoteExec
|
|
319
|
+
else if (config.remoteExec.provider === "device") {
|
|
323
320
|
if (ctx.deviceLane) {
|
|
324
321
|
executionEnvFactory = deviceExecutionEnvFactory({
|
|
325
322
|
hub: ctx.deviceLane.hub,
|
|
@@ -334,15 +331,16 @@ export function createExecutionEnv(ctx) {
|
|
|
334
331
|
});
|
|
335
332
|
}
|
|
336
333
|
}
|
|
337
|
-
else if (config.remoteExec
|
|
334
|
+
else if (config.remoteExec.provider === "adb") {
|
|
338
335
|
executionEnvFactory = adbExecutionEnvFactory({
|
|
339
336
|
serial: config.remoteExec.serial,
|
|
340
337
|
...homeDirSeat, ...homeAbsentNotice,
|
|
341
338
|
...(config.remoteExec.adbPath ? { adbPath: config.remoteExec.adbPath } : {}),
|
|
342
339
|
...(config.remoteExec.mountPath ? { mountPath: config.remoteExec.mountPath } : {}),
|
|
340
|
+
logger,
|
|
343
341
|
});
|
|
344
342
|
}
|
|
345
|
-
else if (config.remoteExec
|
|
343
|
+
else if (config.remoteExec.provider === "host") {
|
|
346
344
|
{
|
|
347
345
|
const schedulerStorePath = config.schedulerStorePath ?? defaultSchedulerStorePath(config);
|
|
348
346
|
const hostScheduler = facts.hostScheduler(config).available
|
|
@@ -384,7 +382,7 @@ export function createExecutionEnv(ctx) {
|
|
|
384
382
|
};
|
|
385
383
|
}
|
|
386
384
|
}
|
|
387
|
-
else if (config.remoteExec
|
|
385
|
+
else if (config.remoteExec.provider === "local-docker") {
|
|
388
386
|
executionEnvFactory = localDockerExecutionEnvFactory({
|
|
389
387
|
image: config.remoteExec.image,
|
|
390
388
|
...(config.remoteExec.mountPath ? { mountPath: config.remoteExec.mountPath } : {}),
|
|
@@ -400,6 +398,7 @@ export function createExecutionEnv(ctx) {
|
|
|
400
398
|
...(config.remoteExec.env ? { env: config.remoteExec.env } : {}),
|
|
401
399
|
sandboxReadFloor: config.sandboxReadFloor,
|
|
402
400
|
...(workspaceFillOf !== undefined ? { workspaceFillOf } : {}),
|
|
401
|
+
logger,
|
|
403
402
|
}, ctx.laneDeps?.localDocker);
|
|
404
403
|
}
|
|
405
404
|
if (executionEnvFactory && DEPLOYMENT_SHAPE_PREDICATES.remoteScratchpad.holds(config)) {
|
|
@@ -408,15 +407,15 @@ export function createExecutionEnv(ctx) {
|
|
|
408
407
|
if (config.worktreeIsolation && executionEnvFactory) {
|
|
409
408
|
const wtRoot = config.worktreeIsolation.repoRoot;
|
|
410
409
|
const wtValid = existsSync(wtRoot) && existsSync(join(wtRoot, ".git"));
|
|
411
|
-
if (config.remoteExec
|
|
410
|
+
if (config.remoteExec.provider === "host" && !wtValid) {
|
|
412
411
|
logger.error("worktree_isolation_disabled_bad_repo_root", {
|
|
413
412
|
repoRoot: wtRoot,
|
|
414
413
|
reason: existsSync(wtRoot) ? "not a git repository (no .git)" : "path does not exist",
|
|
415
414
|
note: "WORKTREE_ISOLATION_ENABLED is set but WORKTREE_REPO_ROOT is not a usable git repo — isolation left OFF; isolation:'worktree' workflow agents are refused (isolation.unavailable) until it is fixed. Fix the path or unset the flag.",
|
|
416
415
|
});
|
|
417
416
|
}
|
|
418
|
-
else if (config.remoteExec
|
|
419
|
-
const gitBaseEnv = new RemoteHostExecutionEnv({ workspaceDir: config.worktreeIsolation.repoRoot, inheritEnv: "all", backgroundShell: config.requirePrincipal !== true });
|
|
417
|
+
else if (config.remoteExec.provider === "host") {
|
|
418
|
+
const gitBaseEnv = new RemoteHostExecutionEnv({ workspaceDir: config.worktreeIsolation.repoRoot, inheritEnv: "all", backgroundShell: config.requirePrincipal !== true, gitBase: true });
|
|
420
419
|
executionEnvFactory = withWorktreeIsolation(executionEnvFactory, {
|
|
421
420
|
repoRoot: config.worktreeIsolation.repoRoot,
|
|
422
421
|
...(config.worktreeIsolation.allowedRoots ? { allowedRoots: config.worktreeIsolation.allowedRoots } : {}),
|
|
@@ -442,7 +441,7 @@ export function createExecutionEnv(ctx) {
|
|
|
442
441
|
}
|
|
443
442
|
else {
|
|
444
443
|
logger.warn("worktree_isolation_unsupported_lane", {
|
|
445
|
-
provider: config.remoteExec
|
|
444
|
+
provider: config.remoteExec.provider,
|
|
446
445
|
note: "git-worktree isolation v1 covers only the 'host' lane — isolation:'worktree' is a no-op here (the base env is used). Worktree-rooted remote envs are a follow-on.",
|
|
447
446
|
});
|
|
448
447
|
}
|
|
@@ -454,37 +453,30 @@ export function createExecutionEnv(ctx) {
|
|
|
454
453
|
executionEnvFactory = registry.wrapFactory(executionEnvFactory);
|
|
455
454
|
return registry;
|
|
456
455
|
})();
|
|
457
|
-
if (taskAttachmentStore && executionEnvFactory && !laneIsolated(
|
|
458
|
-
const inner = executionEnvFactory;
|
|
456
|
+
if (taskAttachmentStore && executionEnvFactory && !laneIsolated(config.remoteExec.provider)) {
|
|
459
457
|
const attStore = taskAttachmentStore;
|
|
460
|
-
executionEnvFactory =
|
|
461
|
-
const env = await inner(ctx);
|
|
462
|
-
const n = await materializeAttachmentsInto(env, attStore, ctx.sessionId);
|
|
463
|
-
if (n > 0)
|
|
464
|
-
logger.info("attachments_materialized", { sessionId: ctx.sessionId, count: n });
|
|
465
|
-
return env;
|
|
466
|
-
};
|
|
458
|
+
executionEnvFactory = createAttachmentMaterializingFactory(executionEnvFactory, (env, ctx) => materializeAttachmentsInto(env, attStore, ctx.sessionId), (sessionId, count) => logger.info("attachments_materialized", { sessionId, count }));
|
|
467
459
|
}
|
|
468
460
|
if (executionEnvFactory && DEPLOYMENT_SHAPE_PREDICATES.sandboxPathAdjudication.holds(config)) {
|
|
469
461
|
executionEnvFactory = withSandboxPathEnvSlot(executionEnvFactory, sandboxPathEnvSlots, logger);
|
|
470
462
|
}
|
|
471
463
|
if (executionEnvFactory) {
|
|
472
|
-
const isolated = laneIsolated(
|
|
464
|
+
const isolated = laneIsolated(config.remoteExec.provider);
|
|
473
465
|
const suspendable = DEPLOYMENT_SHAPE_PREDICATES.suspendableHands.holds(config);
|
|
474
466
|
const keepsReadFloor = capabilitiesFor(config).keepsReadFloor;
|
|
475
467
|
const sandboxReadFloor = laneSandboxReadFloorState(config);
|
|
476
468
|
const sandboxEnvKeys = [...sandboxEnvNamesInForce(config)].sort();
|
|
477
469
|
logger.info("remote_exec_enabled", { provider: config.remoteExec.provider, isolated, suspendable, keepsReadFloor, ...(sandboxReadFloor !== undefined ? { sandboxReadFloor } : {}), ...(sandboxEnvKeys.length > 0 ? { sandboxEnvKeys } : {}) });
|
|
478
470
|
if (!isolated)
|
|
479
|
-
logger.warn("remote_exec_real_system", { provider: config.remoteExec.provider, note:
|
|
471
|
+
logger.warn("remote_exec_real_system", { provider: config.remoteExec.provider, note: REAL_SYSTEM_NOTE });
|
|
480
472
|
const homeLine = buildLaneHomeBootNote(config);
|
|
481
473
|
if (homeLine !== undefined)
|
|
482
474
|
logger.warn(homeLine.event, { provider: config.remoteExec.provider, note: homeLine.note });
|
|
483
|
-
if (
|
|
475
|
+
if (config.remoteExec.provider === "host")
|
|
484
476
|
logger.info("task_inherit_env_honored", { honored: taskInheritEnvRefusal(config) === undefined });
|
|
485
477
|
}
|
|
486
478
|
warnReadDenyFloorUnkept(config, logger);
|
|
487
|
-
const lspProvider = config.remoteExec
|
|
479
|
+
const lspProvider = config.remoteExec.provider;
|
|
488
480
|
const lspLane = executionLaneOf(lspProvider);
|
|
489
481
|
const lspLaneBindsReads = lspLane !== undefined && laneBindsReads(lspLane);
|
|
490
482
|
if ((lspProvider === "e2b" || lspProvider === "k8s") && config.lspEnabled && !lspLaneBindsReads) {
|
|
@@ -506,10 +498,42 @@ export function createExecutionEnv(ctx) {
|
|
|
506
498
|
: undefined;
|
|
507
499
|
if (lspManager)
|
|
508
500
|
logger.info("lsp_enabled", { provider: lspProvider });
|
|
501
|
+
const unreadableReported = new Set();
|
|
502
|
+
let stallReapFailureReported = false;
|
|
503
|
+
const stallMs = config.backgroundAgentStallMs;
|
|
504
|
+
const sessionResidentBackground = () => countSessionResidentBackground(sessionScoped.sessionOwners, {
|
|
505
|
+
onUnreadable: (sessionId, err) => {
|
|
506
|
+
if (unreadableReported.has(sessionId))
|
|
507
|
+
return;
|
|
508
|
+
unreadableReported.add(sessionId);
|
|
509
|
+
logger.warn("session_resident_background_unreadable", { sessionId, counted: 1, err: seatText("operator-log-text", String(err)) });
|
|
510
|
+
},
|
|
511
|
+
stall: {
|
|
512
|
+
stallMs,
|
|
513
|
+
report: (outcome) => {
|
|
514
|
+
switch (outcome.kind) {
|
|
515
|
+
case "reaped":
|
|
516
|
+
logger.warn("background_agent_stall_reaped", { reaped: outcome.count, stallMs });
|
|
517
|
+
return;
|
|
518
|
+
case "failed":
|
|
519
|
+
if (stallReapFailureReported)
|
|
520
|
+
return;
|
|
521
|
+
stallReapFailureReported = true;
|
|
522
|
+
logger.warn("background_agent_stall_reap_failed", { stallMs, err: seatText("operator-log-text", String(outcome.err)) });
|
|
523
|
+
return;
|
|
524
|
+
default: {
|
|
525
|
+
const unhandled = outcome;
|
|
526
|
+
throw new Error(`background agent stall reap: unknown outcome ${String(unhandled)}`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
});
|
|
509
532
|
return {
|
|
510
533
|
perTaskImage, sessionEnvSelection, perSessionCwd, setSessionCwd, setSessionShellEnv, setSessionInheritEnv,
|
|
511
534
|
fenceSessionOwner: sessionScoped.fenceSessionOwner,
|
|
512
535
|
dropSessionScoped: sessionScoped.dropSession,
|
|
536
|
+
sessionResidentBackground,
|
|
513
537
|
executionEnvFactory, worktreeReap, sendUserFileTaskEnvs, lspManager, spaceReclaimer, sandboxSecretSeatGate, sandboxPodReaper, sandboxGitPlane,
|
|
514
538
|
};
|
|
515
539
|
}
|
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
import { type CrossInvariantView } from "../config-invariants.js";
|
|
2
|
-
import type { ServiceConfig } from "../config-types.js";
|
|
3
|
-
import {
|
|
2
|
+
import type { LoadedConfig, ServiceConfig } from "../config-types.js";
|
|
3
|
+
import { type EntryDeclarableLane } from "../config.js";
|
|
4
|
+
import { buildReadDenyFloorUnkeptNote, type ExecutionLane } from "../execution-lane-caps.js";
|
|
4
5
|
import type { Logger } from "../observability/logger.js";
|
|
5
|
-
/** `config.d/remote-exec.json` 能写进来的车道(文件契约里不带密钥名、可 1:1
|
|
6
|
-
export declare const FILE_SOURCED_LANES: readonly ["host", "local-docker"];
|
|
6
|
+
/** `config.d/remote-exec.json` 能写进来的车道(文件契约里不带密钥名、可 1:1 映射的三条 —— `none` 零字段;e2b/k8s/ssh/adb 要 NAME→值解析,尚未接线)。 */
|
|
7
|
+
export declare const FILE_SOURCED_LANES: readonly ["host", "local-docker", "none"];
|
|
7
8
|
export type FileSourcedLane = (typeof FILE_SOURCED_LANES)[number];
|
|
9
|
+
/** 判点的拒句(一行;两条照旧行为的修法逐字在句中:7.146.0 非 local 未设 ⇒ `REMOTE_EXEC=none`,local 未设 ⇒ `REMOTE_EXEC=host`)。
|
|
10
|
+
* 车道词取自车道表(隔离与否读能力表的唯一布尔口 `laneIsolated`),不手抄清单。 */
|
|
11
|
+
export declare const EXECUTION_LANE_UNDECLARED_REFUSAL: string;
|
|
12
|
+
/**
|
|
13
|
+
* 一个**入口程序**(装配者)对车道说的两句话 —— 合成步对它们只有一条律:声明的先后 = 文件 > env > 入口自身,**在场的最高者终结选择**(文件在场 ⇒ 车道就是文件那一条,
|
|
14
|
+
* 解析不了即拒,不许 env / 入口顶上;文件缺席 ⇒ env;二者都缺席 ⇒ 入口自身);最终车道必须是入口跑得了的,否则拒(起任何任务之前)。
|
|
15
|
+
*/
|
|
16
|
+
export interface ExecutionLaneEntry {
|
|
17
|
+
/** 拒句点名的入口(`run-local` / `the HTTP service` …)。 */
|
|
18
|
+
readonly name: string;
|
|
19
|
+
/**
|
|
20
|
+
* 本入口跑得了的车道(闭集;HTTP 服务 = 全部八条,`run-local` = host 与 none —— 它没有容器 / 远程执行器,`none` 不要执行器)。
|
|
21
|
+
* 能从 `config.d/remote-exec.json` 取的 = 它 ∩ {@link FILE_SOURCED_LANES}(隔离车道 e2b / k8s / ssh / adb 要 NAME→值解析,文件腿尚未接线)。
|
|
22
|
+
*/
|
|
23
|
+
readonly runs: readonly ExecutionLane[];
|
|
24
|
+
/** 入口自身的声明(入口 = 部署者,如 run-local ⇒ `host`):文件与 env 都没点名时才填,排在二者之后;缺席 = 本入口不替部署声明。不写进程 env。 */
|
|
25
|
+
readonly declares?: EntryDeclarableLane;
|
|
26
|
+
}
|
|
27
|
+
/** HTTP 服务这只入口:跑得了全部车道、不替部署声明(它的部署者是运维:声明只来自 env / 文件)。 */
|
|
28
|
+
export declare const HTTP_SERVICE_LANE_ENTRY: ExecutionLaneEntry;
|
|
8
29
|
export interface ExecutionLaneComposeCtx {
|
|
9
30
|
/** 本入口的配置源形态(`configSourceKind` 的答案 —— 与它真建的 provider 同一只判据)。
|
|
10
31
|
* 只有 `local` 才读文件;`undefined` = 纯 env 部署。 */
|
|
@@ -12,12 +33,19 @@ export interface ExecutionLaneComposeCtx {
|
|
|
12
33
|
/** 配置根(与配置源同一个根,不分裂)。 */
|
|
13
34
|
readonly localRoot: string;
|
|
14
35
|
readonly logger: Logger;
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
readonly drivesFromFile: readonly FileSourcedLane[];
|
|
36
|
+
/** 装配这台部署的入口程序(跑得了哪些车道、自己声明哪一条)。 */
|
|
37
|
+
readonly entry: ExecutionLaneEntry;
|
|
18
38
|
}
|
|
19
|
-
/**
|
|
20
|
-
|
|
39
|
+
/**
|
|
40
|
+
* 合成 + 判:raw 配置 → composed 配置(**同一个对象**就地覆写并收窄型,交回给装配链往下传)。没有声明 / 坏文件 / 不变量不过 ⇒ 抛(拒启)。
|
|
41
|
+
*/
|
|
42
|
+
export declare function composeExecutionLane(loaded: LoadedConfig, ctx: ExecutionLaneComposeCtx): Promise<ServiceConfig>;
|
|
43
|
+
/**
|
|
44
|
+
* 合成步的**同步尾**(文件覆写之后的一切,恰此一份):入口声明补缺席位 → 入口跑不跑得了 → 判点 → 读面按最终车道派生 + 恰一行通告 → 车道交叉不变量 →
|
|
45
|
+
* `exec_lane_selected {lane}`。{@link composeExecutionLane} 在文件腿之后调它;没有文件腿的纯 env 形(`bench`、测试夹具)直接调它 —— 同一份合成,不是第二个判官。
|
|
46
|
+
* `entry` 缺席 = 这次装配没有入口程序替部署说话(嵌入方 / 脚本:车道只来自 env,跑得了的不设限)。
|
|
47
|
+
*/
|
|
48
|
+
export declare function composeDeclaredExecutionLane(loaded: LoadedConfig, logger: Pick<Logger, "info">, entry?: ExecutionLaneEntry): ServiceConfig;
|
|
21
49
|
/**
|
|
22
50
|
* **宿主车道的 shell 起服解析**(一次)—— 宿主车道(`provider === "host"`)的命令用本机 shell 起(win32 = Git Bash),起服时把它解析落定,
|
|
23
51
|
* 找不到就在这里响亮地说一次(`warn host_shell_unavailable { err, remedy }`),而不是等第一个用户的第一条 Bash 撞上。
|