@sema-agent/server 7.148.0 → 7.150.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +551 -9
- package/README.md +17 -2
- package/README.zh-CN.md +5 -1
- package/USAGE.md +126 -68
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +33 -33
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/approval-leg-origin.d.ts +33 -0
- package/dist/approval-leg-origin.js +4 -0
- package/dist/audit.d.ts +1 -1
- package/dist/audit.js +2 -2
- package/dist/auth-bridge.d.ts +9 -1
- package/dist/auth-bridge.js +4 -4
- package/dist/auth-keys.d.ts +10 -1
- package/dist/auth-keys.js +8 -0
- package/dist/background-session-reap.d.ts +1 -1
- package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
- package/dist/boot/deferred-sandbox-path-env.js +1 -2
- package/dist/boot/execution-env.js +10 -11
- package/dist/boot/execution-lane.d.ts +6 -4
- package/dist/boot/execution-lane.js +3 -1
- package/dist/boot/org-delivery.d.ts +3 -2
- package/dist/boot/parked-revive-gate.d.ts +5 -5
- package/dist/boot/parked-revive-gate.js +2 -3
- package/dist/boot/resolve-spec.d.ts +4 -5
- package/dist/boot/resolve-spec.js +66 -28
- package/dist/boot/retention-lane.d.ts +1 -1
- package/dist/boot/runner-deps.js +3 -1
- package/dist/boot/session-faces.js +21 -21
- package/dist/boot/stage-01-config.d.ts +1 -0
- package/dist/boot/stage-01-config.js +4 -3
- package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
- package/dist/boot/stage-02-web-admin-pull.js +10 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +4 -4
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +1 -0
- package/dist/boot/stage-06-runners.d.ts +2 -1
- package/dist/boot/stage-06-runners.js +1 -2
- package/dist/boot/stage-07-capability-layer.d.ts +2 -1
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +2 -1
- package/dist/boot/stage-09-leader.d.ts +2 -1
- package/dist/boot/stage-10-http-server.d.ts +35 -2
- package/dist/boot/stage-10-http-server.js +9 -4
- package/dist/boot/stores.d.ts +3 -0
- package/dist/boot/stores.js +22 -19
- package/dist/boot/web-admin.d.ts +1 -1
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/capability-facts.d.ts +70 -60
- package/dist/capability-facts.js +45 -49
- package/dist/card-settle-stored-scope.d.ts +56 -0
- package/dist/card-settle-stored-scope.js +27 -0
- package/dist/card-void.d.ts +7 -12
- package/dist/card-void.js +2 -7
- package/dist/config-catalog.d.ts +12 -0
- package/dist/config-catalog.js +37 -33
- package/dist/config-invariants.d.ts +5 -4
- package/dist/config-invariants.js +4 -4
- package/dist/config-types.d.ts +49 -16
- package/dist/config.d.ts +6 -5
- package/dist/config.js +35 -24
- package/dist/context-window-notice.d.ts +31 -0
- package/dist/context-window-notice.js +28 -0
- package/dist/deployment-governance.d.ts +7 -28
- package/dist/deployment-governance.js +1 -14
- package/dist/deployment-shape.d.ts +20 -11
- package/dist/deployment-shape.js +1 -0
- package/dist/device-ws-hub.d.ts +1 -0
- package/dist/device-ws-hub.js +64 -28
- package/dist/device-ws-protocol.d.ts +20 -6
- package/dist/device-ws-protocol.js +9 -2
- package/dist/engine-identity.d.ts +7 -1
- package/dist/engine-identity.js +5 -0
- package/dist/env-name-allowlist-knobs.d.ts +6 -5
- package/dist/env-name-allowlist-knobs.js +5 -0
- package/dist/execution-lane-caps.d.ts +20 -5
- package/dist/execution-lane-caps.js +18 -4
- package/dist/field-conflict.d.ts +2 -2
- package/dist/fleet/engine-signal.d.ts +14 -1
- package/dist/fleet/engine-signal.js +5 -1
- package/dist/fleet/fleet-reconciler.d.ts +1 -1
- package/dist/fleet/serving-lease.d.ts +18 -3
- package/dist/fleet/serving-lease.js +81 -35
- package/dist/fleet-client.d.ts +13 -11
- package/dist/fleet-client.js +53 -33
- package/dist/history-anchor.js +1 -1
- package/dist/hitl-delivery.d.ts +1 -3
- package/dist/hitl-delivery.js +1 -1
- package/dist/hooks/command-spawn.d.ts +7 -2
- package/dist/hooks/command-spawn.js +13 -6
- package/dist/hooks/hook-runner.d.ts +5 -0
- package/dist/hooks/hook-runner.js +11 -7
- package/dist/http/active-run-conflict.d.ts +3 -7
- package/dist/http/active-run-conflict.js +2 -2
- package/dist/http/admission.js +5 -5
- package/dist/http/approval-sweeps.js +5 -3
- package/dist/http/caller-trust.d.ts +2 -2
- package/dist/http/caller-trust.js +3 -3
- package/dist/http/checkpoint-ctx.d.ts +18 -63
- package/dist/http/checkpoint-ctx.js +1 -24
- package/dist/http/claim-heartbeat.d.ts +6 -6
- package/dist/http/claim-heartbeat.js +6 -6
- package/dist/http/claimed-run.d.ts +16 -9
- package/dist/http/claimed-run.js +8 -0
- package/dist/http/dispatch.js +7 -5
- package/dist/http/durable-run-leg.d.ts +8 -1
- package/dist/http/durable-run-leg.js +34 -15
- package/dist/http/execution-context.d.ts +115 -14
- package/dist/http/execution-context.js +130 -40
- package/dist/http/owner-access.d.ts +246 -21
- package/dist/http/owner-access.js +186 -8
- package/dist/http/principal-gate.d.ts +18 -11
- package/dist/http/principal-gate.js +3 -1
- package/dist/http/readiness.d.ts +4 -2
- package/dist/http/resume-ctx-read.d.ts +14 -3
- package/dist/http/resume-ctx-read.js +24 -3
- package/dist/http/resume-legs.d.ts +35 -3
- package/dist/http/resume-legs.js +262 -106
- package/dist/http/route-ctx.d.ts +29 -11
- package/dist/http/routes/a2a-serve.js +17 -14
- package/dist/http/routes/approvals-assistant.d.ts +3 -2
- package/dist/http/routes/approvals-assistant.js +11 -11
- package/dist/http/routes/capabilities.js +5 -4
- package/dist/http/routes/config-catalog.d.ts +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -1
- package/dist/http/routes/diagnostics.js +30 -5
- package/dist/http/routes/mcp-reconnect.js +2 -2
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +2 -3
- package/dist/http/routes/principal-inbox.d.ts +3 -1
- package/dist/http/routes/principal-inbox.js +3 -2
- package/dist/http/routes/projects.d.ts +1 -0
- package/dist/http/routes/projects.js +5 -3
- package/dist/http/routes/retention-ops.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.js +2 -2
- package/dist/http/routes/runs.d.ts +1 -1
- package/dist/http/routes/runs.js +33 -37
- package/dist/http/routes/session-memory-sync.js +4 -5
- package/dist/http/routes/session-sync.d.ts +24 -5
- package/dist/http/routes/session-sync.js +45 -37
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions.d.ts +1 -1
- package/dist/http/routes/sessions.js +18 -44
- package/dist/http/routes/tasks.js +136 -101
- package/dist/http/routes/trace-usage.js +14 -14
- package/dist/http/routes/workflows.d.ts +1 -0
- package/dist/http/routes/workflows.js +20 -38
- package/dist/http/server.d.ts +12 -9
- package/dist/http/server.js +35 -6
- package/dist/http/stored-body-replay.d.ts +3 -24
- package/dist/http/stored-body-replay.js +2 -12
- package/dist/http/submit-ctx.d.ts +2 -2
- package/dist/http/submit-ctx.js +5 -5
- package/dist/http/submit-replay.d.ts +15 -2
- package/dist/http/submit-replay.js +10 -4
- package/dist/http/wire-types.d.ts +6 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/leader/endpoint.js +9 -8
- package/dist/leg-facts.d.ts +13 -2
- package/dist/leg-facts.js +2 -0
- package/dist/local-store-declaration.d.ts +54 -0
- package/dist/local-store-declaration.js +59 -0
- package/dist/mcp-assertion-header.d.ts +1 -1
- package/dist/mcp-assertion-header.js +1 -1
- package/dist/mcp-authorization-face.d.ts +18 -14
- package/dist/mcp-authorization-face.js +3 -2
- package/dist/memory-project-layer.js +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +4 -4
- package/dist/memory-store-refusal.d.ts +1 -1
- package/dist/memory-sync-client.js +2 -2
- package/dist/model-context-window.d.ts +50 -11
- package/dist/model-context-window.js +20 -1
- package/dist/model-entry-refusal.d.ts +5 -4
- package/dist/model-entry-refusal.js +17 -3
- package/dist/model-reach-scope.d.ts +2 -1
- package/dist/observability/corrupt-read-seat.d.ts +1 -1
- package/dist/observability/fail-open.d.ts +19 -7
- package/dist/observability/fail-open.js +19 -7
- package/dist/observability/logger.d.ts +7 -0
- package/dist/observability/logger.js +3 -1
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +1 -1
- package/dist/observability/secret-env-scrub.js +1 -1
- package/dist/org-delivery-gate.d.ts +31 -6
- package/dist/org-delivery-gate.js +7 -4
- package/dist/owner-faces.d.ts +30 -5
- package/dist/owner-faces.js +2 -0
- package/dist/park-row-commit.d.ts +67 -0
- package/dist/park-row-commit.js +76 -0
- package/dist/parked-decide.d.ts +9 -7
- package/dist/parked-decide.js +25 -6
- package/dist/peer-directory.d.ts +1 -1
- package/dist/peer-seat.d.ts +1 -1
- package/dist/peer-seat.js +52 -59
- package/dist/plugins/adoption-log-sql.d.ts +3 -3
- package/dist/plugins/adoption-log-sql.js +19 -19
- package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
- package/dist/plugins/approval-ask-store-sql.js +3 -5
- package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
- package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
- package/dist/plugins/approval-exemption-skill-m46.d.ts +0 -6
- package/dist/plugins/approval-exemption-skill-m46.js +6 -10
- package/dist/plugins/approval-exemption-store.d.ts +26 -1
- package/dist/plugins/approval-exemption-store.js +28 -19
- package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -3
- package/dist/plugins/ensure-schema-lock-policy.js +75 -9
- package/dist/plugins/exec-path-law.d.ts +34 -0
- package/dist/plugins/exec-path-law.js +13 -0
- package/dist/plugins/file-run-store.d.ts +22 -9
- package/dist/plugins/file-run-store.js +51 -23
- package/dist/plugins/local-checkpoint-store.d.ts +8 -1
- package/dist/plugins/local-checkpoint-store.js +5 -1
- package/dist/plugins/local-data-root-admission.d.ts +52 -0
- package/dist/plugins/local-data-root-admission.js +100 -0
- package/dist/plugins/local-engine-high-water.d.ts +28 -7
- package/dist/plugins/local-engine-high-water.js +27 -22
- package/dist/plugins/local-session-store.d.ts +134 -49
- package/dist/plugins/local-session-store.js +149 -108
- package/dist/plugins/memory-run-store.d.ts +14 -9
- package/dist/plugins/memory-run-store.js +48 -22
- package/dist/plugins/one-time-migration-marker.d.ts +19 -3
- package/dist/plugins/one-time-migration-marker.js +7 -1
- package/dist/plugins/one-time-migrations.d.ts +110 -59
- package/dist/plugins/one-time-migrations.js +224 -136
- package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
- package/dist/plugins/org-delivery-quota-store.js +29 -14
- package/dist/plugins/permission-rule-store-sql.d.ts +1 -1
- package/dist/plugins/permission-rule-store-sql.js +5 -9
- package/dist/plugins/pg-pool.d.ts +1 -1
- package/dist/plugins/pg-pool.js +14 -12
- package/dist/plugins/posix-shell-fs.js +4 -2
- package/dist/plugins/remote-env-adb.js +3 -2
- package/dist/plugins/remote-env-device.d.ts +3 -2
- package/dist/plugins/remote-env-device.js +8 -6
- package/dist/plugins/remote-env-e2b.d.ts +4 -8
- package/dist/plugins/remote-env-e2b.js +21 -15
- package/dist/plugins/remote-env-host.d.ts +51 -10
- package/dist/plugins/remote-env-host.js +97 -31
- package/dist/plugins/remote-env-k8s.js +7 -4
- package/dist/plugins/remote-env-local-docker.d.ts +6 -3
- package/dist/plugins/remote-env-local-docker.js +11 -25
- package/dist/plugins/remote-env-ssh.js +4 -3
- package/dist/plugins/remote-shell.d.ts +3 -1
- package/dist/plugins/remote-shell.js +3 -3
- package/dist/plugins/replica-registration.d.ts +35 -0
- package/dist/plugins/replica-registration.js +48 -0
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/run-store-sql.d.ts +27 -8
- package/dist/plugins/run-store-sql.js +46 -18
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +8 -12
- package/dist/plugins/scenario-prefix-switch-m42.js +9 -6
- package/dist/plugins/schema-consistency.d.ts +91 -5
- package/dist/plugins/schema-consistency.js +189 -7
- package/dist/plugins/schema-drift-sentence.d.ts +6 -5
- package/dist/plugins/schema-drift-sentence.js +13 -5
- package/dist/plugins/serving-requirement.d.ts +49 -0
- package/dist/plugins/serving-requirement.js +53 -0
- package/dist/plugins/session-placement.d.ts +5 -4
- package/dist/plugins/shell-sentinel-owner-m48.d.ts +22 -0
- package/dist/plugins/shell-sentinel-owner-m48.js +6 -0
- package/dist/plugins/stop-then-start-sentence.d.ts +55 -0
- package/dist/plugins/stop-then-start-sentence.js +51 -0
- package/dist/plugins/store-backend.d.ts +23 -9
- package/dist/plugins/store-backend.js +58 -24
- package/dist/plugins/store-contracts.d.ts +45 -5
- package/dist/plugins/store-contracts.js +19 -0
- package/dist/plugins/tidb-pool.d.ts +11 -6
- package/dist/plugins/tidb-pool.js +22 -12
- package/dist/plugins/workspace-custody.d.ts +44 -0
- package/dist/plugins/workspace-custody.js +47 -0
- package/dist/process-instance.d.ts +3 -0
- package/dist/process-instance.js +7 -0
- package/dist/project-context.d.ts +66 -0
- package/dist/project-context.js +70 -0
- package/dist/project-identity.d.ts +8 -7
- package/dist/project-identity.js +40 -17
- package/dist/project-layer-key.d.ts +13 -2
- package/dist/project-layer-key.js +9 -1
- package/dist/question.d.ts +9 -21
- package/dist/question.js +28 -19
- package/dist/read-deny-floor-notice.d.ts +36 -0
- package/dist/read-deny-floor-notice.js +40 -0
- package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
- package/dist/request-declaration-disposition.js +39 -0
- package/dist/run-cancel-card.d.ts +28 -0
- package/dist/run-cancel-card.js +20 -0
- package/dist/run-cancel-context.d.ts +10 -0
- package/dist/run-cancel-context.js +1 -0
- package/dist/run-local.js +28 -13
- package/dist/runs.d.ts +31 -7
- package/dist/runs.js +34 -16
- package/dist/security.d.ts +3 -3
- package/dist/server-secret-env.d.ts +34 -20
- package/dist/server-secret-env.js +61 -18
- package/dist/session-agent.d.ts +4 -12
- package/dist/session-agent.js +0 -6
- package/dist/session-children-retire.d.ts +6 -3
- package/dist/session-children-retire.js +2 -5
- package/dist/session-incarnation.d.ts +18 -4
- package/dist/session-incarnation.js +4 -1
- package/dist/session-notify-door.d.ts +4 -4
- package/dist/session-notify-door.js +5 -6
- package/dist/session-rules-seat.d.ts +4 -3
- package/dist/session-rules-seat.js +3 -11
- package/dist/session-sync.js +3 -3
- package/dist/session-workspace.d.ts +3 -3
- package/dist/store-admission.d.ts +33 -0
- package/dist/store-admission.js +49 -0
- package/dist/store-posture.d.ts +5 -0
- package/dist/store-posture.js +18 -1
- package/dist/stores-sql/checkpoint-store-sql.d.ts +22 -3
- package/dist/stores-sql/checkpoint-store-sql.js +109 -23
- package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
- package/dist/stores-sql/file-history-store-sql.js +3 -5
- package/dist/stores-sql/retention-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.js +4 -6
- package/dist/task-a2a.d.ts +3 -8
- package/dist/task-a2a.js +25 -27
- package/dist/task-cwd.d.ts +27 -10
- package/dist/task-cwd.js +3 -0
- package/dist/task-mcp.d.ts +3 -3
- package/dist/task-mcp.js +1 -1
- package/dist/task-settings.d.ts +13 -0
- package/dist/task-settings.js +14 -7
- package/dist/terminal.d.ts +2 -0
- package/dist/terminal.js +21 -6
- package/dist/tool-approval.d.ts +50 -41
- package/dist/tool-approval.js +96 -73
- package/dist/trace/engine-notice-wire.d.ts +38 -21
- package/dist/trace/engine-notice-wire.js +25 -4
- package/dist/trace/ledger-sink.d.ts +3 -1
- package/dist/trace/project.d.ts +8 -5
- package/dist/trace/project.js +11 -9
- package/dist/trace/projection-table.d.ts +33 -2
- package/dist/trace/redact.d.ts +12 -0
- package/dist/trace/redact.js +5 -1
- package/dist/trace/self-minted-wire.d.ts +268 -56
- package/dist/trace/self-minted-wire.js +110 -40
- package/dist/trace/tool-card.d.ts +3 -0
- package/dist/trace/tool-card.js +24 -0
- package/dist/trace/tool-output-media.d.ts +2 -1
- package/dist/unattended-permission-mode.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.js +22 -9
- package/dist/web-admin-client/effective-keys.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
- package/docs/error-text-lexicon.json +317 -0
- package/package.json +4 -2
- package/scripts/embed-minimal.mjs +183 -0
- package/wire-changes/server@7.149.0.json +372 -0
- package/wire-changes/server@7.150.0.json +335 -0
- package/dist/plain-string-record.js +0 -70
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 执行环境工厂的工作区托管表 —— 「这只工作区现在归谁持有 / 能不能删」的唯一答者。local-docker(容器)与 host(本服务铸的临时目录)两条车道的工厂都经它铸环境、读它判删。
|
|
3
|
+
*
|
|
4
|
+
* ## 律
|
|
5
|
+
* 引擎 park-only 契约:`suspendable:false` 的远端环境断言工作区活过环境对象 —— 续跑由工厂重连到**同一只**工作区;引擎在续跑赢下之后丢掉停驻腿的环境、不销毁它
|
|
6
|
+
* (销毁了就是拆掉续跑腿正在用的工作区)。所以:
|
|
7
|
+
* · **一只身份一只工作区**(身份 = 工厂上下文的 `taskId ?? sessionId`,与引擎交给工具的宿主任务号同一读法):同一身份还有一只没开始收场的环境 ⇒ 交回那一只
|
|
8
|
+
* (停驻腿的环境引擎还拿着,续跑腿拿到的就是它);那一只正在收场 ⇒ 等它的收场落定再铸,它欠着的账交给新的这只;没有 ⇒ 铸一只入表。
|
|
9
|
+
* 同一身份同一时刻只有一条腿(会话锁 + run 认领),交回同一只不会让两条腿同时用它。
|
|
10
|
+
* · **工作区的生命归持有它的环境,不归铸它的那一只**:表里每一只成员自己说它此刻持有哪只工作区;一只工作区能不能删 = 表里除问者之外还有没有成员持有它
|
|
11
|
+
* ({@link WorkspaceCustody.holder})—— 没有才删,有就把删交给最后放手的那一只。
|
|
12
|
+
*
|
|
13
|
+
* ## 两条车道怎么读同一张表
|
|
14
|
+
* · local-docker:工作区 = 身份派生名的那只容器;一只身份同一时刻只有一只没收场的成员(上面的交回律),容器名又由身份派生 ⇒ 不可能有第二个持有者,成员收场即删
|
|
15
|
+
* (读 {@link WorkspaceCustody.envOf};欠账 = 删除没证实的那只容器,交给同一身份的下一只)。
|
|
16
|
+
* · host:工作区 = 本服务铸的临时根;委派子代按父的工作目录落根 ⇒ 子代的环境与父的共持那只根(铸时问 {@link WorkspaceCustody.holder}「谁持有包含这一处的根」),
|
|
17
|
+
* 收场放手时问同一只「还有谁持有它」—— 父收场时子代还停驻在里面 ⇒ 父不删,子代最后一条腿收场时删。
|
|
18
|
+
*
|
|
19
|
+
* ## 出表
|
|
20
|
+
* 成员收场落定、什么都不欠、什么都不持有({@link CustodyMember.settled})⇒ 出表;没收场的成员(停驻腿的环境)一直在表里(停驻中被店层取消 / 过期的,引擎不销毁它,它持有的工作区
|
|
21
|
+
* 活到进程退出 —— local-docker 的容器由回收器按 run 行收,host 的目录由退出边界收)。
|
|
22
|
+
*/
|
|
23
|
+
/** 表里的一只成员(一只执行环境)向表说的四件事。 */
|
|
24
|
+
export interface CustodyMember<Leftover> {
|
|
25
|
+
/** 收场的进度:没开始 ⇒ `undefined`(可以交回给同一身份的下一条腿);开始了 ⇒ 那一次收场(落定之后同一身份才铸新的)。 */
|
|
26
|
+
readonly releasing: Promise<void> | undefined;
|
|
27
|
+
/** 收场已落定。 */
|
|
28
|
+
readonly released: boolean;
|
|
29
|
+
/** 收场之后仍欠着、要交给同一身份下一只环境的账(local-docker:删除没证实的那只容器);不欠 ⇒ `undefined`。 */
|
|
30
|
+
readonly leftover: Leftover | undefined;
|
|
31
|
+
/** 什么都不再欠、不再持有 —— 出表。 */
|
|
32
|
+
readonly settled: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface WorkspaceCustody<E extends CustodyMember<L>, L> {
|
|
35
|
+
/**
|
|
36
|
+
* 这只身份的环境:还有一只没开始收场的 ⇒ 交回它;正在收场的 ⇒ 等它落定(再看一遍);都落定了 ⇒ 拿走欠账、铸一只入表。
|
|
37
|
+
* `mint` 抛 ⇒ 什么都不入表,原样抛。
|
|
38
|
+
*/
|
|
39
|
+
envOf(identity: string, mint: (leftover: L | undefined) => E): Promise<E>;
|
|
40
|
+
/** 表里一只满足 `holds` 的成员(没有 ⇒ `undefined`)—— 「这只工作区现在还有谁持有」的唯一读法:问者把自己排除在 `holds` 里。 */
|
|
41
|
+
holder(holds: (member: E) => boolean): E | undefined;
|
|
42
|
+
}
|
|
43
|
+
export declare function createWorkspaceCustody<E extends CustodyMember<L>, L = never>(): WorkspaceCustody<E, L>;
|
|
44
|
+
//# sourceMappingURL=workspace-custody.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export function createWorkspaceCustody() {
|
|
2
|
+
const members = new Map();
|
|
3
|
+
const sweep = () => {
|
|
4
|
+
for (const member of members.keys())
|
|
5
|
+
if (member.settled)
|
|
6
|
+
members.delete(member);
|
|
7
|
+
};
|
|
8
|
+
return {
|
|
9
|
+
async envOf(identity, mint) {
|
|
10
|
+
for (;;) {
|
|
11
|
+
sweep();
|
|
12
|
+
const closing = [];
|
|
13
|
+
const owing = [];
|
|
14
|
+
for (const [member, id] of members) {
|
|
15
|
+
if (id !== identity)
|
|
16
|
+
continue;
|
|
17
|
+
if (member.releasing === undefined)
|
|
18
|
+
return member;
|
|
19
|
+
if (!member.released)
|
|
20
|
+
closing.push(member.releasing);
|
|
21
|
+
else if (member.leftover !== undefined)
|
|
22
|
+
owing.push(member);
|
|
23
|
+
}
|
|
24
|
+
if (closing.length > 0) {
|
|
25
|
+
await Promise.allSettled(closing);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
let leftover;
|
|
29
|
+
for (const member of owing) {
|
|
30
|
+
leftover = member.leftover;
|
|
31
|
+
members.delete(member);
|
|
32
|
+
}
|
|
33
|
+
const env = mint(leftover);
|
|
34
|
+
members.set(env, identity);
|
|
35
|
+
return env;
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
holder(holds) {
|
|
39
|
+
sweep();
|
|
40
|
+
for (const member of members.keys())
|
|
41
|
+
if (holds(member))
|
|
42
|
+
return member;
|
|
43
|
+
return undefined;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=workspace-custody.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type ProjectRegistration } from "@sema-agent/settings-schema";
|
|
2
|
+
import type { ListFilter } from "./owner-faces.js";
|
|
3
|
+
/** 进简报 / 进 `cwd` 的登记字段(闭集)。每一格的写角色不低于 `prompts` 域(`test/project-context-inheritance.test.ts` 按 settings-schema 的 `writeRoleFor` 钉);身份字段永不在表里。 */
|
|
4
|
+
export declare const PROJECT_BRIEF_FIELDS: readonly ["goal", "notes", "contextItems"];
|
|
5
|
+
/** 进简报 / 进 `cwd` 的登记字段。 */
|
|
6
|
+
export type ProjectBriefField = (typeof PROJECT_BRIEF_FIELDS)[number];
|
|
7
|
+
/** 内容证明:配置中心写口在一次按写权表判过内容字段的写之后盖的时刻(写角色同为 publisher)。本服务只读在不在。 */
|
|
8
|
+
export declare const PROJECT_CONTENT_ATTESTATION_FIELD: "contentAttestedAt";
|
|
9
|
+
/** 继承判读得到的登记成员(类型上只够得着表内字段 + 可见性的属主键 + 内容证明)。 */
|
|
10
|
+
export type ProjectContextSource = Pick<ProjectRegistration, ProjectBriefField | "registeredBy" | typeof PROJECT_CONTENT_ATTESTATION_FIELD>;
|
|
11
|
+
/** 一条 run 继承到的项目上下文(冻结记录;随 run 落进续跑重建输入,续跑腿原样重放)。 */
|
|
12
|
+
export interface ProjectContext {
|
|
13
|
+
/** 判出它的项目(登记簿键,小写)。 */
|
|
14
|
+
readonly projectId: string;
|
|
15
|
+
/** 继承的工作目录:第一条 `folder` 条目 —— 只在射程内(`callerOwnsWorkerDisk`)且请求没带 `cwd` 时在场。 */
|
|
16
|
+
readonly cwd?: string;
|
|
17
|
+
/** 项目简报(系统提示追加折叠的第二段);`projectMemory: "disabled"` 或无可列内容 ⇒ 缺席。 */
|
|
18
|
+
readonly brief?: string;
|
|
19
|
+
}
|
|
20
|
+
/** 继承判的输入(全部是新提交腿此刻读得到的事实)。 */
|
|
21
|
+
export interface ProjectContextInput {
|
|
22
|
+
/** 这条腿的项目(`legProjectIdOf`);缺席 ⇒ 不继承。 */
|
|
23
|
+
readonly projectId: string | undefined;
|
|
24
|
+
/** 本部署的登记簿(`capability-facts.ts` `projectRegistry` 的见证);缺席 = 本部署没有登记簿 ⇒ 不继承。 */
|
|
25
|
+
readonly registry: Readonly<Record<string, ProjectContextSource>> | undefined;
|
|
26
|
+
/** 提交者在读面上的视域(读面同一只推导);缺席 = 没有请求 ⇒ 不继承。 */
|
|
27
|
+
readonly viewer: ListFilter | undefined;
|
|
28
|
+
/** 部署形表行 `callerOwnsWorkerDisk`:路径条目(继承 `cwd`、简报的路径两段)只在它成立时兑现。 */
|
|
29
|
+
readonly pathsHonored: boolean;
|
|
30
|
+
/** 请求体带了 `cwd`(非空串)⇒ 显式值赢,不填。 */
|
|
31
|
+
readonly requestCwd: boolean;
|
|
32
|
+
/** 请求 `projectMemory: "disabled"` ⇒ 不出简报。 */
|
|
33
|
+
readonly projectMemoryOff: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** 继承判的答案:继承到的上下文,外加射程外未列的路径条目数(server 侧 warn 只计数,不记路径)。 */
|
|
36
|
+
export interface ProjectContextDecision {
|
|
37
|
+
readonly context: ProjectContext | undefined;
|
|
38
|
+
readonly withheldPathItems: number;
|
|
39
|
+
}
|
|
40
|
+
/** 一只登记对这位观者可见吗(读面与继承同调的唯一判):全舰队视域看全部;否则只看 `registeredBy` 恰是自己的。看不见与不存在同答。 */
|
|
41
|
+
export declare function projectVisible(registration: Pick<ProjectRegistration, "registeredBy">, viewer: ListFilter): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* 项目简报的唯一铸点(纯串;各段缺席不出,全缺席 ⇒ 无简报)。只读 {@link PROJECT_BRIEF_FIELDS};路径两段只在 `pathsHonored` 时出,且只列名字 ——
|
|
44
|
+
* `file` 条目未附上,段名写明「相关时去读」。首行点名来源与项目 id(不带 `displayName`:身份字段不进提示)。
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildProjectBrief(projectId: string, registration: Pick<ProjectRegistration, ProjectBriefField>, pathsHonored: boolean): string | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* 继承判(唯一;调用点只有装配器头部的新提交腿)。项目缺席 / 本部署无登记簿 / 没有观者 / 未登记 / 看不见 / 无内容证明 ⇒ 不继承(不拒,与随机 id 同答)。
|
|
49
|
+
*/
|
|
50
|
+
export declare function projectContextOf(input: ProjectContextInput): ProjectContextDecision;
|
|
51
|
+
/** 续跑重建输入里冻结的那一份的读数:缺席(没继承 / 7.150.0 之前写下的行)· 读得出 · 形不对。 */
|
|
52
|
+
export type FrozenProjectContextReading = {
|
|
53
|
+
readonly kind: "absent";
|
|
54
|
+
} | {
|
|
55
|
+
readonly kind: "ok";
|
|
56
|
+
readonly context: ProjectContext;
|
|
57
|
+
} | {
|
|
58
|
+
readonly kind: "malformed";
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* 冻结的那一份的**唯一读法**(读者 = `http/resume-ctx-read.ts` 的续跑上下文读口)。服务端写下的形是闭集(本文件 {@link projectContextOf} 铸、`buildCheckpointCtx` 原样落盘):
|
|
62
|
+
* 自有键只在 `projectId` / `cwd` / `brief` 里;`projectId` = 规范小写 UUID;`cwd` 在场 = 宿主绝对路径;`brief` 在场 = 非空串、不超过系统提示追加折叠的帽;
|
|
63
|
+
* `cwd` 与 `brief` 至少其一(都缺的不会被冻结)。别的形 ⇒ `malformed`(读口答坏体:续跑不重放它、不登记它的 `cwd`)。
|
|
64
|
+
*/
|
|
65
|
+
export declare function frozenProjectContextOf(stored: unknown): FrozenProjectContextReading;
|
|
66
|
+
//# sourceMappingURL=project-context.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { PROJECT_ID_REGEX } from "@sema-agent/core";
|
|
2
|
+
import { isAbsoluteHostPath } from "@sema-agent/settings-schema";
|
|
3
|
+
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS } from "./task-settings.js";
|
|
4
|
+
export const PROJECT_BRIEF_FIELDS = ["goal", "notes", "contextItems"];
|
|
5
|
+
export const PROJECT_CONTENT_ATTESTATION_FIELD = "contentAttestedAt";
|
|
6
|
+
export function projectVisible(registration, viewer) {
|
|
7
|
+
return viewer.fleetWide || registration.registeredBy === viewer.gateOwner;
|
|
8
|
+
}
|
|
9
|
+
export function buildProjectBrief(projectId, registration, pathsHonored) {
|
|
10
|
+
const sections = [];
|
|
11
|
+
if (registration.goal !== undefined && registration.goal.length > 0)
|
|
12
|
+
sections.push(`## Goal\n${registration.goal}`);
|
|
13
|
+
if (registration.notes !== undefined && registration.notes.length > 0)
|
|
14
|
+
sections.push(`## Notes\n${registration.notes}`);
|
|
15
|
+
const items = registration.contextItems ?? [];
|
|
16
|
+
const notes = items.flatMap((item) => (item.kind === "note" && item.value.length > 0 ? [item.value] : []));
|
|
17
|
+
if (notes.length > 0)
|
|
18
|
+
sections.push(`## Project notes\n${notes.join("\n\n")}`);
|
|
19
|
+
if (pathsHonored) {
|
|
20
|
+
const folders = items.flatMap((item) => (item.kind === "folder" ? [`- ${item.value}`] : []));
|
|
21
|
+
if (folders.length > 0)
|
|
22
|
+
sections.push(`## Project folders\n${folders.join("\n")}`);
|
|
23
|
+
const files = items.flatMap((item) => (item.kind === "file" ? [`- ${item.value}`] : []));
|
|
24
|
+
if (files.length > 0)
|
|
25
|
+
sections.push(`## Project files (not attached — read them when relevant)\n${files.join("\n")}`);
|
|
26
|
+
}
|
|
27
|
+
if (sections.length === 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
return [`# Project context (from this deployment's project registry; project ${projectId})`, ...sections].join("\n\n");
|
|
30
|
+
}
|
|
31
|
+
export function projectContextOf(input) {
|
|
32
|
+
const { projectId, registry, viewer } = input;
|
|
33
|
+
if (projectId === undefined || registry === undefined || viewer === undefined)
|
|
34
|
+
return { context: undefined, withheldPathItems: 0 };
|
|
35
|
+
const registration = Object.hasOwn(registry, projectId) ? registry[projectId] : undefined;
|
|
36
|
+
if (registration === undefined || !projectVisible(registration, viewer))
|
|
37
|
+
return { context: undefined, withheldPathItems: 0 };
|
|
38
|
+
if (registration[PROJECT_CONTENT_ATTESTATION_FIELD] === undefined)
|
|
39
|
+
return { context: undefined, withheldPathItems: 0 };
|
|
40
|
+
const items = registration.contextItems ?? [];
|
|
41
|
+
const withheldPathItems = input.pathsHonored ? 0 : items.filter((item) => item.kind !== "note").length;
|
|
42
|
+
const firstFolder = input.pathsHonored ? items.find((item) => item.kind === "folder")?.value : undefined;
|
|
43
|
+
const cwd = input.requestCwd ? undefined : firstFolder;
|
|
44
|
+
const brief = input.projectMemoryOff ? undefined : buildProjectBrief(projectId, registration, input.pathsHonored);
|
|
45
|
+
if (cwd === undefined && brief === undefined)
|
|
46
|
+
return { context: undefined, withheldPathItems };
|
|
47
|
+
return { context: { projectId, ...(cwd !== undefined ? { cwd } : {}), ...(brief !== undefined ? { brief } : {}) }, withheldPathItems };
|
|
48
|
+
}
|
|
49
|
+
export function frozenProjectContextOf(stored) {
|
|
50
|
+
if (stored === undefined)
|
|
51
|
+
return { kind: "absent" };
|
|
52
|
+
if (typeof stored !== "object" || stored === null || Array.isArray(stored))
|
|
53
|
+
return { kind: "malformed" };
|
|
54
|
+
if (!Object.keys(stored).every((k) => k === "projectId" || k === "cwd" || k === "brief"))
|
|
55
|
+
return { kind: "malformed" };
|
|
56
|
+
const own = (k) => (Object.hasOwn(stored, k) ? stored[k] : undefined);
|
|
57
|
+
const projectId = own("projectId");
|
|
58
|
+
const cwd = own("cwd");
|
|
59
|
+
const brief = own("brief");
|
|
60
|
+
if (typeof projectId !== "string" || !PROJECT_ID_REGEX.test(projectId) || projectId !== projectId.toLowerCase())
|
|
61
|
+
return { kind: "malformed" };
|
|
62
|
+
if (cwd !== undefined && (typeof cwd !== "string" || !isAbsoluteHostPath(cwd)))
|
|
63
|
+
return { kind: "malformed" };
|
|
64
|
+
if (brief !== undefined && (typeof brief !== "string" || brief.length === 0 || brief.length > MAX_SETTINGS_OUTPUT_STYLE_CHARS))
|
|
65
|
+
return { kind: "malformed" };
|
|
66
|
+
if (cwd === undefined && brief === undefined)
|
|
67
|
+
return { kind: "malformed" };
|
|
68
|
+
return { kind: "ok", context: { projectId, ...(cwd !== undefined ? { cwd } : {}), ...(brief !== undefined ? { brief } : {}) } };
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=project-context.js.map
|
|
@@ -25,16 +25,17 @@ export declare function assertProjectLayerOwnable(scope: string, envName: string
|
|
|
25
25
|
*/
|
|
26
26
|
export declare function normalizeGitRemoteUrl(url: string): string | undefined;
|
|
27
27
|
/**
|
|
28
|
-
* 任务根 → 项目层身份(
|
|
29
|
-
*
|
|
28
|
+
* 任务根 → 项目层身份(文件头「项目身份一条律」):按档序(`project-layer-key.ts` `PROJECT_IDENTITY_TIERS`)取本检出有的各档 ——
|
|
29
|
+
* 每一档 = 它的身份文本的 {@link digest24}(`m`:记号里的项目 id;`r`:归一后的网络 origin;`p`:canonical 项目根,恒有)。
|
|
30
|
+
* 链首 = 当前档,其余 = `lowerTiers`(折叠来源)。缺席 = 找根的缺席词(`bare` / `unreadable`);git 配置在而读不了、或记号读不出 ⇒ `unreadable`。
|
|
30
31
|
*/
|
|
31
|
-
export declare function projectLayerIdentityOf(taskRoot: string): {
|
|
32
|
+
export declare function projectLayerIdentityOf(taskRoot: string): Promise<{
|
|
32
33
|
readonly root: string;
|
|
33
34
|
readonly identity: ProjectLayerIdentity;
|
|
34
35
|
readonly lowerTiers: readonly ProjectLayerIdentity[];
|
|
35
36
|
} | {
|
|
36
37
|
readonly absent: ProjectIdentityAbsentReason;
|
|
37
|
-
}
|
|
38
|
+
}>;
|
|
38
39
|
/** 一个任务的项目层:core 声明席 + 本 checkout 要折进来的低档 / 退役层(按序,`memory-project-layer.ts` 折)。 */
|
|
39
40
|
export interface ProjectLayerOfTask {
|
|
40
41
|
readonly key: MemoryProjectKey;
|
|
@@ -43,11 +44,11 @@ export interface ProjectLayerOfTask {
|
|
|
43
44
|
/**
|
|
44
45
|
* 任务根 → 项目层([ref]:`userrepo:<属主段>/<档>.<摘要>`,属主 = `owner`,调用方由 `projectLayerOwnerOf(全局层)` 求得)。
|
|
45
46
|
* `foldFrom` = 同一 checkout 能推出的旧层:退役 `repo:<同一路径哈希>`(一次性迁移,窗见 `plugins/one-time-migrations.ts` 登记表 M25)
|
|
46
|
-
* +
|
|
47
|
+
* + 当前档之下的每一档(常设律:`m` 档折 `r` / `p`,`r` 档折 `p`)。
|
|
47
48
|
* `taskRoot === undefined` = 调用方**此刻不知道**任务根在本机哪里(根在沙箱/设备盘上;或 host 车道会话没登记 cwd、手落在 factory 现铸的
|
|
48
49
|
* 临时目录)⇒ `unreadable` —— 「我看不到」,不是「这里没有项目」。
|
|
49
50
|
*/
|
|
50
|
-
export declare function projectLayerForTaskRoot(taskRoot: string | undefined, owner: string): ProjectLayerOfTask
|
|
51
|
+
export declare function projectLayerForTaskRoot(taskRoot: string | undefined, owner: string): Promise<ProjectLayerOfTask>;
|
|
51
52
|
/** {@link projectLayerForTaskRoot} 的声明席投影(core `TaskSpec.memory.projectKey`)。 */
|
|
52
|
-
export declare function projectKeyForTaskRoot(taskRoot: string | undefined, owner: string): MemoryProjectKey
|
|
53
|
+
export declare function projectKeyForTaskRoot(taskRoot: string | undefined, owner: string): Promise<MemoryProjectKey>;
|
|
53
54
|
//# sourceMappingURL=project-identity.d.ts.map
|
package/dist/project-identity.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { readFileSync, realpathSync, statSync } from "node:fs";
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
|
-
import { encodeScopeSegment } from "@sema-agent/core";
|
|
5
|
-
import {
|
|
4
|
+
import { encodeScopeSegment, NodeExecutionEnv, resolveProjectId } from "@sema-agent/core";
|
|
5
|
+
import { errorCodeOf } from "./observability/err-text.js";
|
|
6
|
+
import { recordFailOpen } from "./observability/fail-open.js";
|
|
7
|
+
import { mintProjectLayerKey, PROJECT_IDENTITY_TIERS, PROJECT_LAYER_KEY_PREFIX, projectLayerOwnerOf } from "./project-layer-key.js";
|
|
6
8
|
const UNREADABLE = { absent: "unreadable" };
|
|
7
9
|
const BARE = { absent: "bare" };
|
|
8
10
|
function isMissing(err) {
|
|
@@ -87,10 +89,7 @@ function canonicalTaskRoot(taskRoot) {
|
|
|
87
89
|
p = parent;
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
|
-
function locateProject(
|
|
91
|
-
const root = canonicalTaskRoot(taskRoot);
|
|
92
|
-
if (root === "unreadable")
|
|
93
|
-
return UNREADABLE;
|
|
92
|
+
function locateProject(root) {
|
|
94
93
|
let sawBareLayout = false;
|
|
95
94
|
for (let dir = root.existing;;) {
|
|
96
95
|
const dotGit = statFact(join(dir, ".git"));
|
|
@@ -240,23 +239,47 @@ export function normalizeGitRemoteUrl(url) {
|
|
|
240
239
|
function cleanRemotePath(p) {
|
|
241
240
|
return p.replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "").replace(/\.git$/, "");
|
|
242
241
|
}
|
|
243
|
-
|
|
244
|
-
|
|
242
|
+
async function markerFact(canonicalTaskRoot) {
|
|
243
|
+
try {
|
|
244
|
+
const marker = await resolveProjectId(new NodeExecutionEnv({ cwd: canonicalTaskRoot }), canonicalTaskRoot);
|
|
245
|
+
return marker === null ? "missing" : { ok: marker.projectId };
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
recordFailOpen("server.memory.project-marker-unreadable", `code=${errorCodeOf(err) ?? "-"}`);
|
|
249
|
+
return "unreadable";
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
export async function projectLayerIdentityOf(taskRoot) {
|
|
253
|
+
const canonical = canonicalTaskRoot(taskRoot);
|
|
254
|
+
if (canonical === "unreadable")
|
|
255
|
+
return UNREADABLE;
|
|
256
|
+
const located = locateProject(canonical);
|
|
245
257
|
if (!("root" in located))
|
|
246
258
|
return located;
|
|
247
|
-
const path = { tier: "p", digest: digest24(located.root) };
|
|
248
259
|
const origin = located.gitConfig !== undefined ? readOriginRemoteUrl(located.gitConfig) : "missing";
|
|
249
260
|
if (origin === "unreadable")
|
|
250
261
|
return UNREADABLE;
|
|
251
|
-
const
|
|
252
|
-
if (
|
|
253
|
-
return
|
|
254
|
-
|
|
262
|
+
const marker = await markerFact(canonical.canonical);
|
|
263
|
+
if (marker === "unreadable")
|
|
264
|
+
return UNREADABLE;
|
|
265
|
+
const identityText = {
|
|
266
|
+
m: marker === "missing" ? undefined : marker.ok,
|
|
267
|
+
r: origin === "missing" || origin.ok === undefined ? undefined : normalizeGitRemoteUrl(origin.ok),
|
|
268
|
+
p: located.root,
|
|
269
|
+
};
|
|
270
|
+
const chain = PROJECT_IDENTITY_TIERS.flatMap((tier) => {
|
|
271
|
+
const text = identityText[tier];
|
|
272
|
+
return text === undefined ? [] : [{ tier, digest: digest24(text) }];
|
|
273
|
+
});
|
|
274
|
+
const [identity, ...lowerTiers] = chain;
|
|
275
|
+
if (identity === undefined)
|
|
276
|
+
return UNREADABLE;
|
|
277
|
+
return { root: located.root, identity, lowerTiers };
|
|
255
278
|
}
|
|
256
|
-
export function projectLayerForTaskRoot(taskRoot, owner) {
|
|
279
|
+
export async function projectLayerForTaskRoot(taskRoot, owner) {
|
|
257
280
|
if (taskRoot === undefined)
|
|
258
281
|
return { key: UNREADABLE, foldFrom: [] };
|
|
259
|
-
const id = projectLayerIdentityOf(taskRoot);
|
|
282
|
+
const id = await projectLayerIdentityOf(taskRoot);
|
|
260
283
|
if (!("identity" in id))
|
|
261
284
|
return { key: { absent: id.absent }, foldFrom: [] };
|
|
262
285
|
return {
|
|
@@ -264,7 +287,7 @@ export function projectLayerForTaskRoot(taskRoot, owner) {
|
|
|
264
287
|
foldFrom: [mintRepoScopeKey(id.root), ...id.lowerTiers.map((tier) => mintProjectLayerKey(owner, tier))],
|
|
265
288
|
};
|
|
266
289
|
}
|
|
267
|
-
export function projectKeyForTaskRoot(taskRoot, owner) {
|
|
268
|
-
return projectLayerForTaskRoot(taskRoot, owner).key;
|
|
290
|
+
export async function projectKeyForTaskRoot(taskRoot, owner) {
|
|
291
|
+
return (await projectLayerForTaskRoot(taskRoot, owner)).key;
|
|
269
292
|
}
|
|
270
293
|
//# sourceMappingURL=project-identity.js.map
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
/** 项目层键的前缀(改名链的前缀词表也读它)。 */
|
|
2
2
|
export declare const PROJECT_LAYER_KEY_PREFIX = "userrepo:";
|
|
3
|
-
/** 身份档:`r` = 仓库身份(origin 远端,跨设备),`p` = 项目根路径(本机)。 */
|
|
4
|
-
export type ProjectIdentityTier = "r" | "p";
|
|
3
|
+
/** 身份档:`m` = 检出的记号(项目 id),`r` = 仓库身份(origin 远端,跨设备),`p` = 项目根路径(本机)。 */
|
|
4
|
+
export type ProjectIdentityTier = "m" | "r" | "p";
|
|
5
|
+
/**
|
|
6
|
+
* **档序**(高 → 低)—— 唯一成文处。两只读者:
|
|
7
|
+
* · 一只检出的档链(`project-identity.ts` `projectLayerIdentityOf`):按本序取它有的各档,链首 = 当前档,其余 = 折叠来源(低档);
|
|
8
|
+
* · 同步轮的层序({@link compareProjectLayersForSync}):低档先于高档。
|
|
9
|
+
*/
|
|
10
|
+
export declare const PROJECT_IDENTITY_TIERS: readonly ProjectIdentityTier[];
|
|
11
|
+
/**
|
|
12
|
+
* 同步轮里两只项目层键的先后:**低档在前**,同档按键序。折叠把条目从低档搬到高档、id 保留 ⇒ 同一轮先在源层推删除、再在高档推新增,
|
|
13
|
+
* 中心那一侧同一个 id 不会先撞上源层里的自己。不是项目层的键(调用方本不该给)排在最后。
|
|
14
|
+
*/
|
|
15
|
+
export declare function compareProjectLayersForSync(a: string, b: string): number;
|
|
5
16
|
/** 一个项目的身份段(档 + 24 位小写十六进制摘要)。 */
|
|
6
17
|
export interface ProjectLayerIdentity {
|
|
7
18
|
readonly tier: ProjectIdentityTier;
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { decodeScopeSegment, encodeScopeSegment, parseScopeKey } from "@sema-agent/core";
|
|
2
2
|
export const PROJECT_LAYER_KEY_PREFIX = "userrepo:";
|
|
3
|
+
export const PROJECT_IDENTITY_TIERS = ["m", "r", "p"];
|
|
4
|
+
export function compareProjectLayersForSync(a, b) {
|
|
5
|
+
const rank = (key) => {
|
|
6
|
+
const tier = parseProjectLayerKey(key)?.identity.tier;
|
|
7
|
+
return tier === undefined ? -1 : PROJECT_IDENTITY_TIERS.indexOf(tier);
|
|
8
|
+
};
|
|
9
|
+
return rank(b) - rank(a) || (a < b ? -1 : a > b ? 1 : 0);
|
|
10
|
+
}
|
|
3
11
|
const DIGEST = /^[0-9a-f]{24}$/;
|
|
4
|
-
const KEY = /^userrepo:((?:[A-Za-z0-9._-]|%[0-9A-F]{2})+)\/([
|
|
12
|
+
const KEY = /^userrepo:((?:[A-Za-z0-9._-]|%[0-9A-F]{2})+)\/([mrp])\.([0-9a-f]{24})$/;
|
|
5
13
|
export function projectLayerOwnerOf(globalScope) {
|
|
6
14
|
const parsed = parseScopeKey(globalScope);
|
|
7
15
|
return parsed.kind === "user" ? parsed.principal : globalScope;
|
package/dist/question.d.ts
CHANGED
|
@@ -9,15 +9,15 @@ export interface QuestionFrame {
|
|
|
9
9
|
/** "question" only: the model's structured questions, secret-redacted. UNTRUSTED-for-display (shell renders; never
|
|
10
10
|
* re-feeds a model). Absent on an over-cap payload (the ask then reports unavailable — no frame is emitted at all). */
|
|
11
11
|
questions?: AskQuestion[];
|
|
12
|
-
/** "question_complete" only: whether the human answered (`answered`) or the ask
|
|
13
|
-
* (`unanswered` —
|
|
12
|
+
/** "question_complete" only: whether the human answered (`answered`) or the ask closed without an answer
|
|
13
|
+
* (`unanswered` — window elapsed / abort / leg-exit sweep). Cosmetic dialog-dismiss. */
|
|
14
14
|
outcome?: "answered" | "unanswered";
|
|
15
15
|
/** Server-signed emit timestamp (additive, [ref]§五→[ref]). Server-minted side-frame ⇒ no core-stream
|
|
16
16
|
* `eventId` to carry (absence is honest); this is the frame's own time coordinate. */
|
|
17
17
|
serverNowMs?: number;
|
|
18
18
|
/** "question" only ([ref]③, additive, the `tool_approval` window-trio precedent from [ref]): the
|
|
19
|
-
* coordinator's OWN answer window (`QUESTION_TTL_MS`, default 5min) — at expiry the ask
|
|
20
|
-
*
|
|
19
|
+
* coordinator's OWN answer window (`QUESTION_TTL_MS`, default 5min) — at expiry the ask closes ("unanswered"); core hears
|
|
20
|
+
* a delivery failure when a live face presented the card, `unavailable` otherwise ({@link seamOutcomeOf}). Minted from the SAME `ttlMs` the
|
|
21
21
|
* `setTimeout` below actually arms (single source; a shell countdown rendered from these can never
|
|
22
22
|
* disagree with the release). Relative + absolute + `serverNowMs` above = clock-skew-proof trio. */
|
|
23
23
|
expiresInMs?: number;
|
|
@@ -30,9 +30,7 @@ export interface QuestionFrame {
|
|
|
30
30
|
* awaiting a human when the run aborts.
|
|
31
31
|
*
|
|
32
32
|
* The ledger carries the CARD, not the answer door: `POST /v1/questions/:id/respond` stays live-only (this replica's
|
|
33
|
-
* memory, `ttlMs`), so a replayed card is answerable only until its own `expiresAtMs` and only on this replica.
|
|
34
|
-
* The adjudication probe ({@link QuestionCoordinator.hasLiveContext}) reads the same transport's `reachable()` — one
|
|
35
|
-
* reading of "can this leg's face show a card right now?". */
|
|
33
|
+
* memory, `ttlMs`), so a replayed card is answerable only until its own `expiresAtMs` and only on this replica. */
|
|
36
34
|
export interface QuestionRunContext {
|
|
37
35
|
taskId: string;
|
|
38
36
|
owner: string | null;
|
|
@@ -73,12 +71,11 @@ export declare class QuestionCoordinator {
|
|
|
73
71
|
private readonly throttle;
|
|
74
72
|
constructor(throttle?: QuestionThrottle);
|
|
75
73
|
/** Run `fn` with the per-run question context ambient. On exit, release any still-pending question for this run (a
|
|
76
|
-
* live-only question cannot outlive its leg →
|
|
74
|
+
* live-only question cannot outlive its leg → released) and drop the run's counters (no leak). */
|
|
77
75
|
runWithContext<T>(ctx: QuestionRunContext, fn: () => Promise<T>): Promise<T>;
|
|
78
|
-
/** `RunnerDeps.onQuestion`. core calls this when the agent's AskUserQuestion tool fires;
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* run never hangs). Arrow property so it can be passed as `onQuestion: coordinator.question` with `this` bound. */
|
|
76
|
+
/** `RunnerDeps.onQuestion`. core calls this when the agent's AskUserQuestion tool fires; what it settles to is what core
|
|
77
|
+
* acts on — a {@link QuestionAnswer} ONLY when a human really answered; a THROW when a live face presented the card and its
|
|
78
|
+
* window elapsed unanswered; `{kind:"unavailable"}` otherwise ({@link seamOutcomeOf}). Either way the run never hangs. Arrow property so it can be passed as `onQuestion: coordinator.question` with `this` bound. */
|
|
82
79
|
question: (req: AskQuestionRequest, signal?: AbortSignal) => Promise<OnQuestionOutcome>;
|
|
83
80
|
/** `POST /v1/questions/:id/respond` — resolve a parked question with the shell's answer. Owner-gated with a 404 (no
|
|
84
81
|
* existence oracle): a non-owner AND an unknown id (answered / expired / wrong replica) both get 404. Returns the
|
|
@@ -89,15 +86,6 @@ export declare class QuestionCoordinator {
|
|
|
89
86
|
};
|
|
90
87
|
/** Test/observability hook: number of currently-parked questions. */
|
|
91
88
|
pendingCount(): number;
|
|
92
|
-
/** [ref] ([ref] 案二):durable 部署上的 AskUserQuestion 判决探针——本调用点是否处在某条**投递得到人的**
|
|
93
|
-
* 活流腿上(runWithContext 包裹的 bg/SSE 腿=true;sync /v1/tasks、verify/cascade、durable resume 驱动=false)。
|
|
94
|
-
* resolve-spec 的 durable question policy 用它在**判决时**分腿:有活流 ⇒ allow(问活人),无 ⇒
|
|
95
|
-
* ask(durable park)。ALS 让这个判断天然 per-leg,policy 组装期不必预知腿别。
|
|
96
|
-
* 🔴 判据是「投递面此刻可达」而不只是「ALS 在场」(复审 A1):detach 腿断连后 run 仍在本作用域里跑,
|
|
97
|
-
* 只判 ALS 会把一条谁也收不到的问题判成 allow;park 才是它该走的腿。读的是本腿传输的 `reachable()`
|
|
98
|
-
* (有直写面 ⇒ 直写此刻活着;只有账本 ⇒ 恒真,后台腿的 durable events tail 语义;谓词读不出 ⇒ 假,判决面 fail-closed 到 park)。
|
|
99
|
-
* 只服务判决面:判决放行之后的开帧投递只读传输律(有账本的腿上死直写 = 卡在账本里,送到)。 */
|
|
100
|
-
hasLiveContext(): boolean;
|
|
101
89
|
private countersFor;
|
|
102
90
|
}
|
|
103
91
|
//# sourceMappingURL=question.d.ts.map
|
package/dist/question.js
CHANGED
|
@@ -12,6 +12,20 @@ export const DEFAULT_QUESTION_THROTTLE = {
|
|
|
12
12
|
ttlMs: 5 * 60_000,
|
|
13
13
|
};
|
|
14
14
|
const UNAVAILABLE = Object.freeze({ kind: "unavailable" });
|
|
15
|
+
const RELEASED = Object.freeze({ kind: "released" });
|
|
16
|
+
const WINDOW_ELAPSED = Object.freeze({ kind: "window_elapsed" });
|
|
17
|
+
function seamOutcomeOf(settlement, presentedLive, ttlMs) {
|
|
18
|
+
switch (settlement.kind) {
|
|
19
|
+
case "answered":
|
|
20
|
+
return settlement.answer;
|
|
21
|
+
case "released":
|
|
22
|
+
return UNAVAILABLE;
|
|
23
|
+
case "window_elapsed":
|
|
24
|
+
if (!presentedLive)
|
|
25
|
+
return UNAVAILABLE;
|
|
26
|
+
throw new Error(`the question was shown on this run's live stream and nobody answered it within its ${ttlMs / 1000}s answer window`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
15
29
|
export function parseQuestionResponse(body) {
|
|
16
30
|
if (body === null || typeof body !== "object" || Array.isArray(body))
|
|
17
31
|
return { ok: false, error: "body must be an object" };
|
|
@@ -61,7 +75,7 @@ export class QuestionCoordinator {
|
|
|
61
75
|
finally {
|
|
62
76
|
for (const [, p] of [...this.pending]) {
|
|
63
77
|
if (p.taskId === ctx.taskId)
|
|
64
|
-
p.settle(
|
|
78
|
+
p.settle(RELEASED);
|
|
65
79
|
}
|
|
66
80
|
this.counters.delete(ctx.taskId);
|
|
67
81
|
}
|
|
@@ -83,9 +97,9 @@ export class QuestionCoordinator {
|
|
|
83
97
|
rc.inflight += 1;
|
|
84
98
|
let done = false;
|
|
85
99
|
let timer;
|
|
86
|
-
let
|
|
87
|
-
const
|
|
88
|
-
const settle = (
|
|
100
|
+
let resolveSettled;
|
|
101
|
+
const settledP = new Promise((resolve) => { resolveSettled = resolve; });
|
|
102
|
+
const settle = (s) => {
|
|
89
103
|
if (done)
|
|
90
104
|
return;
|
|
91
105
|
done = true;
|
|
@@ -95,12 +109,10 @@ export class QuestionCoordinator {
|
|
|
95
109
|
ctx.abortSignal?.removeEventListener("abort", onAbort);
|
|
96
110
|
this.pending.delete(id);
|
|
97
111
|
rc.inflight = Math.max(0, rc.inflight - 1);
|
|
98
|
-
|
|
99
|
-
resolveAnswer(a);
|
|
112
|
+
resolveSettled(s);
|
|
100
113
|
};
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
timer = setTimeout(() => settle(UNAVAILABLE, "unanswered"), this.throttle.ttlMs);
|
|
114
|
+
const onAbort = () => settle(RELEASED);
|
|
115
|
+
timer = setTimeout(() => settle(WINDOW_ELAPSED), this.throttle.ttlMs);
|
|
104
116
|
timer.unref?.();
|
|
105
117
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
106
118
|
ctx.abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
@@ -108,7 +120,7 @@ export class QuestionCoordinator {
|
|
|
108
120
|
const now = Date.now();
|
|
109
121
|
const race = createHitlOpenRace({
|
|
110
122
|
targets: [{ transport: ctx.transport, frame: { type: "question", questionId: id, questions, serverNowMs: now, expiresInMs: this.throttle.ttlMs, expiresAtMs: now + this.throttle.ttlMs } }],
|
|
111
|
-
settled:
|
|
123
|
+
settled: settledP,
|
|
112
124
|
});
|
|
113
125
|
const phase = await race.phase;
|
|
114
126
|
if (phase === "delivered") {
|
|
@@ -116,7 +128,7 @@ export class QuestionCoordinator {
|
|
|
116
128
|
}
|
|
117
129
|
else if (phase === "undelivered") {
|
|
118
130
|
recordFailOpen("server.question.open-frame-undelivered", `question=${id}`);
|
|
119
|
-
settle(
|
|
131
|
+
settle(RELEASED);
|
|
120
132
|
}
|
|
121
133
|
else {
|
|
122
134
|
void race.landing.then((late) => {
|
|
@@ -126,9 +138,10 @@ export class QuestionCoordinator {
|
|
|
126
138
|
recordFailOpen("server.question.open-frame-undelivered", `question=${id}`);
|
|
127
139
|
});
|
|
128
140
|
}
|
|
129
|
-
const
|
|
130
|
-
race.close({ type: "question_complete", questionId: id, outcome:
|
|
131
|
-
|
|
141
|
+
const settlement = await settledP;
|
|
142
|
+
race.close({ type: "question_complete", questionId: id, outcome: settlement.kind === "answered" ? "answered" : "unanswered", serverNowMs: Date.now() });
|
|
143
|
+
const presentedLive = settlement.kind === "window_elapsed" && phase === "delivered" && (await race.acceptedLive(0));
|
|
144
|
+
return seamOutcomeOf(settlement, presentedLive, this.throttle.ttlMs);
|
|
132
145
|
};
|
|
133
146
|
respond(id, principal, body) {
|
|
134
147
|
const parsed = parseQuestionResponse(body);
|
|
@@ -138,16 +151,12 @@ export class QuestionCoordinator {
|
|
|
138
151
|
if (!entry || !keyAdmits("ask.answer", { subject: recoveredSubject(principal), operator: false }, ownerCellOf(entry.owner))) {
|
|
139
152
|
return { status: 404, body: refusalBody({ errorCode: "question.not_pending", message: "no pending question for this id (answered, expired, or not on this replica)" }) };
|
|
140
153
|
}
|
|
141
|
-
entry.settle(parsed.value
|
|
154
|
+
entry.settle({ kind: "answered", answer: parsed.value });
|
|
142
155
|
return { status: 200, body: { questionId: id, delivery: "applied" } };
|
|
143
156
|
}
|
|
144
157
|
pendingCount() {
|
|
145
158
|
return this.pending.size;
|
|
146
159
|
}
|
|
147
|
-
hasLiveContext() {
|
|
148
|
-
const ctx = this.als.getStore();
|
|
149
|
-
return ctx !== undefined && ctx.transport.reachable();
|
|
150
|
-
}
|
|
151
160
|
countersFor(taskId) {
|
|
152
161
|
let rc = this.counters.get(taskId);
|
|
153
162
|
if (!rc) {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** 引擎的码(`tools/fs/read-floor.js` 的字面量;包根不导出)—— 通告与拒起同一个词。 */
|
|
2
|
+
export declare const READ_DENY_FLOOR_UNKEPT = "config.read_deny_floor_unkept";
|
|
3
|
+
/** 终局结果上的那一格(`PersistedResultSideKeys.readDenyFloor`):在场 ⇔ 这条 run 照跑了、它的读拒行不按字节守。 */
|
|
4
|
+
export interface ReadDenyFloorSummary {
|
|
5
|
+
readonly code: typeof READ_DENY_FLOOR_UNKEPT;
|
|
6
|
+
readonly message: string;
|
|
7
|
+
}
|
|
8
|
+
/** 呈现只读部署的执行车道(活读,不拷值)。 */
|
|
9
|
+
interface ReadDenyFloorDeployment {
|
|
10
|
+
readonly remoteExec: {
|
|
11
|
+
readonly provider: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/** 进程入口装一次(`boot/execution-lane.ts` 的 `settleReadDenyFloor`);再装 = 换成新的部署(同进程只有一个部署;测试按需各装)。 */
|
|
15
|
+
export declare function installReadDenyFloorDeployment(deployment: ReadDenyFloorDeployment): void;
|
|
16
|
+
/** 记录的上限:一条 run 从准备期(通告)到出口封口(结果)之间,本进程里又有这么多条照跑的 run 起步,它的那一条才会被挤掉。 */
|
|
17
|
+
export declare const RAN_RUNS_CAP = 10000;
|
|
18
|
+
/**
|
|
19
|
+
* 转发体的第一步(每条引擎通告都过,先于日志与分流):{@link READ_DENY_FLOOR_UNKEPT} ⇒ `message` 换成本仓那一句(`ran`),记下 `detail.runId`,
|
|
20
|
+
* 归属 = 这条 run 所在的腿(见顶注「帧投到哪条流」):顶层 `sessionId`(分流器先读它)= 腿的会话,投递键 `taskId` = 腿的 run;腿没有 run 资源 / 作用域外 ⇒ 引擎填的原样、无投递键;
|
|
21
|
+
* 其余码 / 没装部署 ⇒ 同一引用原样。`detail` 不动(引擎的 `rowCount` / `sessionId` / `runId`)。
|
|
22
|
+
*/
|
|
23
|
+
export declare function presentEngineNotice<N extends {
|
|
24
|
+
readonly code: string;
|
|
25
|
+
readonly message: string;
|
|
26
|
+
readonly detail?: Record<string, unknown>;
|
|
27
|
+
readonly sessionId?: unknown;
|
|
28
|
+
}>(notice: N): N & {
|
|
29
|
+
readonly taskId?: string;
|
|
30
|
+
};
|
|
31
|
+
/** 失败终局的对外句(`terminal.ts` 两只出口属主读它):{@link READ_DENY_FLOOR_UNKEPT} ⇒ 本仓那一句(`refused`);别的码 / 没装部署 ⇒ `undefined`(句子不归这里)。 */
|
|
32
|
+
export declare function readDenyFloorRefusalMessage(code: string): string | undefined;
|
|
33
|
+
/** 一条结果(按它的 `runId`)该带的那一格;这条 run 没有被呈现过的通告 ⇒ `undefined`。 */
|
|
34
|
+
export declare function readDenyFloorSummaryOf(runId: unknown): ReadDenyFloorSummary | undefined;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=read-deny-floor-notice.d.ts.map
|