@sema-agent/server 7.137.0 → 7.139.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 +600 -142
- package/README.md +7 -3
- package/README.zh-CN.md +6 -3
- package/USAGE.md +132 -41
- package/deploy/sema-up/README.md +1 -1
- package/deploy/sema-up/lib/lanes.json +3 -2
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +23 -16
- package/dist/approval-card.js +1 -0
- package/dist/audit.js +1 -0
- package/dist/background-agent-name.d.ts +5 -0
- package/dist/background-agent-name.js +5 -0
- package/dist/bench/s1/arms.d.ts +1 -1
- package/dist/bench/s1/arms.js +1 -3
- package/dist/bench/s1/run-firm.js +2 -2
- package/dist/boot/budget-tracing.d.ts +1 -12
- package/dist/boot/deployment-mods.d.ts +46 -16
- package/dist/boot/deployment-mods.js +82 -6
- package/dist/boot/device-lane.d.ts +8 -3
- package/dist/boot/device-lane.js +7 -2
- package/dist/boot/execution-env.d.ts +4 -2
- package/dist/boot/execution-env.js +16 -24
- package/dist/boot/execution-lane.d.ts +1 -1
- package/dist/boot/governance-seams.d.ts +12 -18
- package/dist/boot/governance-seams.js +10 -24
- package/dist/boot/leader.d.ts +3 -0
- package/dist/boot/leader.js +1 -0
- package/dist/boot/memory-consolidation.js +2 -2
- package/dist/boot/org-delivery.js +15 -2
- package/dist/boot/resolve-spec.d.ts +4 -1
- package/dist/boot/resolve-spec.js +29 -19
- package/dist/boot/retention-lane.d.ts +14 -8
- package/dist/boot/retention-lane.js +2 -8
- package/dist/boot/runner-deps.d.ts +10 -4
- package/dist/boot/runner-deps.js +2 -0
- package/dist/boot/session-faces.d.ts +0 -3
- package/dist/boot/session-faces.js +15 -8
- package/dist/boot/stage-02-web-admin-pull.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -12
- package/dist/boot/stage-05-execution-env.d.ts +3 -12
- package/dist/boot/stage-05-execution-env.js +8 -5
- package/dist/boot/stage-06-runners.d.ts +3 -12
- package/dist/boot/stage-06-runners.js +5 -3
- package/dist/boot/stage-07-capability-layer.d.ts +3 -12
- package/dist/boot/stage-07-capability-layer.js +2 -2
- package/dist/boot/stage-08-reapers.d.ts +3 -12
- package/dist/boot/stage-09-leader.d.ts +3 -12
- package/dist/boot/stage-09-leader.js +2 -3
- package/dist/boot/stage-10-http-server.d.ts +3 -12
- package/dist/boot/stage-10-http-server.js +5 -3
- package/dist/boot/stores.js +4 -4
- package/dist/boot/web-admin.d.ts +5 -2
- package/dist/boot/web-admin.js +5 -5
- package/dist/brain.js +5 -5
- package/dist/budget.d.ts +17 -15
- package/dist/budget.js +2 -2
- package/dist/capabilities/mcp-probe.d.ts +36 -11
- package/dist/capabilities/mcp-probe.js +44 -5
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +21 -7
- package/dist/capability-facts.js +13 -3
- package/dist/checkpoint-identity.d.ts +13 -6
- package/dist/config-catalog.js +14 -8
- package/dist/config-invariants.d.ts +23 -3
- package/dist/config-invariants.js +42 -0
- package/dist/config-provider.d.ts +19 -24
- package/dist/config-provider.js +27 -12
- package/dist/config-types.d.ts +25 -12
- package/dist/config.d.ts +33 -0
- package/dist/config.js +52 -20
- package/dist/delegated-session-owner.d.ts +2 -2
- package/dist/deployment-shape.d.ts +7 -7
- package/dist/device-enrollment.d.ts +41 -2
- package/dist/device-enrollment.js +27 -12
- package/dist/device-store.d.ts +58 -85
- package/dist/device-store.js +19 -49
- package/dist/device-ws-hub.js +2 -1
- package/dist/env-name-allowlist-knobs.d.ts +5 -4
- package/dist/exec-cut.d.ts +60 -0
- package/dist/exec-cut.js +36 -0
- package/dist/execution-lane-caps.d.ts +19 -0
- package/dist/execution-lane-caps.js +10 -0
- package/dist/execution-placement.d.ts +259 -0
- package/dist/execution-placement.js +199 -0
- package/dist/field-conflict.d.ts +3 -2
- package/dist/field-conflict.js +1 -0
- package/dist/gate-settlement.d.ts +71 -0
- package/dist/gate-settlement.js +40 -0
- package/dist/hooks/classic-stations.d.ts +1 -0
- package/dist/hooks/classic-stations.js +1 -0
- package/dist/hooks/command-spawn.d.ts +2 -2
- package/dist/hooks/hook-runner.js +14 -14
- package/dist/hosted-posture.d.ts +2 -2
- package/dist/http/admission.js +10 -19
- package/dist/http/checkpoint-ctx.d.ts +21 -12
- package/dist/http/checkpoint-ctx.js +4 -2
- package/dist/http/claimed-run.d.ts +1 -1
- package/dist/http/dispatch.d.ts +4 -0
- package/dist/http/dispatch.js +3 -0
- package/dist/http/durable-run-leg.js +12 -6
- package/dist/http/execution-context.d.ts +218 -0
- package/dist/http/execution-context.js +330 -0
- package/dist/http/memory-compliance-failure-reply.js +3 -0
- package/dist/http/resume-legs.d.ts +5 -11
- package/dist/http/resume-legs.js +179 -104
- package/dist/http/route-ctx.d.ts +44 -59
- package/dist/http/route-ctx.js +2 -5
- package/dist/http/routes/a2a-serve.js +10 -4
- package/dist/http/routes/agents-roster.d.ts +1 -1
- package/dist/http/routes/agents-roster.js +5 -1
- package/dist/http/routes/approvals-assistant.js +6 -6
- package/dist/http/routes/background.d.ts +29 -1
- package/dist/http/routes/background.js +29 -2
- package/dist/http/routes/capabilities.js +12 -10
- package/dist/http/routes/devices.d.ts +13 -19
- package/dist/http/routes/devices.js +21 -26
- package/dist/http/routes/mcp-reconnect.d.ts +3 -2
- package/dist/http/routes/mcp-reconnect.js +7 -5
- package/dist/http/routes/memory-consolidation.d.ts +1 -1
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/rules.js +3 -3
- package/dist/http/routes/runs.js +11 -7
- package/dist/http/routes/session-sync.js +110 -15
- package/dist/http/routes/sessions.d.ts +6 -0
- package/dist/http/routes/sessions.js +119 -13
- package/dist/http/routes/side-query.d.ts +12 -4
- package/dist/http/routes/side-query.js +6 -8
- package/dist/http/routes/tasks.js +800 -782
- package/dist/http/server.d.ts +61 -39
- package/dist/http/server.js +11 -0
- package/dist/http/session-inherit-env.d.ts +3 -4
- package/dist/http/session-inherit-env.js +1 -1
- package/dist/http/wire-types.d.ts +22 -18
- package/dist/identity-source.d.ts +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/leader/wire.d.ts +7 -0
- package/dist/leader/wire.js +5 -4
- package/dist/mcp-assertion-header.d.ts +51 -40
- package/dist/mcp-assertion-header.js +10 -11
- package/dist/mcp-authorization-face.d.ts +27 -0
- package/dist/mcp-authorization-face.js +15 -0
- package/dist/memory-operator-faces.d.ts +13 -4
- package/dist/memory-operator-faces.js +1 -17
- package/dist/memory-sync-client.d.ts +53 -14
- package/dist/memory-sync-client.js +187 -92
- package/dist/memory-sync.d.ts +1 -1
- package/dist/memory-sync.js +3 -2
- package/dist/model-route-gap.d.ts +6 -22
- package/dist/model-route-gap.js +7 -5
- package/dist/mods-declaration.d.ts +110 -0
- package/dist/mods-declaration.js +109 -0
- package/dist/observability/cost-taxonomy.d.ts +2 -2
- package/dist/observability/err-text.d.ts +5 -0
- package/dist/observability/err-text.js +9 -0
- package/dist/observability/fail-open.d.ts +17 -1
- package/dist/observability/fail-open.js +17 -1
- package/dist/observability/run-terminal.js +0 -1
- package/dist/observability/secret-env-scrub.d.ts +1 -1
- package/dist/observability/secret-env-scrub.js +1 -1
- package/dist/orchestration/workflow-notify-journal.js +4 -4
- package/dist/org-memory-admission.d.ts +4 -3
- package/dist/org-memory-admission.js +6 -6
- package/dist/parked-decide.d.ts +49 -11
- package/dist/parked-decide.js +25 -26
- package/dist/parked-revive-surface.d.ts +12 -15
- package/dist/parked-revive-surface.js +3 -6
- package/dist/peer-seat.d.ts +156 -0
- package/dist/peer-seat.js +273 -0
- package/dist/permission-rule-vocab.d.ts +7 -7
- package/dist/permission-rule-vocab.js +3 -3
- package/dist/plain-string-record.d.ts +38 -0
- package/dist/plain-string-record.js +38 -0
- package/dist/plugins/approval-ask-store-file.d.ts +7 -0
- package/dist/plugins/approval-ask-store-file.js +11 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
- package/dist/plugins/approval-ask-store-memory.js +12 -0
- package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
- package/dist/plugins/approval-ask-store-sql.js +30 -12
- package/dist/plugins/background-agent-store-sql.js +6 -3
- package/dist/plugins/caching-session-store.d.ts +11 -2
- package/dist/plugins/caching-session-store.js +15 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +21 -25
- package/dist/plugins/checkpoint-store-sql.js +13 -15
- package/dist/plugins/device-store-sql.d.ts +25 -51
- package/dist/plugins/device-store-sql.js +49 -103
- package/dist/plugins/execution-placement-sql.d.ts +151 -0
- package/dist/plugins/execution-placement-sql.js +223 -0
- package/dist/plugins/file-history-store-sql.d.ts +7 -6
- package/dist/plugins/file-history-store-sql.js +8 -7
- package/dist/plugins/file-run-store.d.ts +6 -1
- package/dist/plugins/file-run-store.js +4 -0
- package/dist/plugins/fork-routing-session-store.d.ts +6 -0
- package/dist/plugins/fork-routing-session-store.js +16 -0
- package/dist/plugins/k8s-pod-reaper.d.ts +2 -8
- package/dist/plugins/k8s-pod-reaper.js +3 -3
- package/dist/plugins/local-checkpoint-store.d.ts +1 -11
- package/dist/plugins/local-checkpoint-store.js +0 -1
- package/dist/plugins/local-session-store.d.ts +183 -45
- package/dist/plugins/local-session-store.js +412 -105
- package/dist/plugins/mailbox-store-sql.d.ts +11 -3
- package/dist/plugins/mailbox-store-sql.js +103 -15
- package/dist/plugins/memory-key-guards.d.ts +4 -0
- package/dist/plugins/memory-key-guards.js +1 -0
- package/dist/plugins/memory-run-store.d.ts +1 -1
- package/dist/plugins/memory-sync-store-pg.d.ts +16 -5
- package/dist/plugins/memory-sync-store-tidb.d.ts +3 -3
- package/dist/plugins/one-time-migrations.d.ts +117 -6
- package/dist/plugins/one-time-migrations.js +383 -14
- package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +13 -15
- package/dist/plugins/permission-rule-store-sql.js +34 -29
- package/dist/plugins/pg-pool.d.ts +7 -3
- package/dist/plugins/pg-session-storage.d.ts +41 -22
- package/dist/plugins/pg-session-storage.js +136 -55
- package/dist/plugins/remote-env-adb.d.ts +22 -4
- package/dist/plugins/remote-env-adb.js +70 -62
- package/dist/plugins/remote-env-device.d.ts +32 -12
- package/dist/plugins/remote-env-device.js +18 -17
- package/dist/plugins/remote-env-e2b.d.ts +19 -3
- package/dist/plugins/remote-env-e2b.js +41 -42
- package/dist/plugins/remote-env-file-error.d.ts +2 -0
- package/dist/plugins/remote-env-file-error.js +1 -1
- package/dist/plugins/remote-env-host.d.ts +23 -8
- package/dist/plugins/remote-env-host.js +66 -70
- package/dist/plugins/remote-env-k8s.d.ts +13 -16
- package/dist/plugins/remote-env-k8s.js +61 -54
- package/dist/plugins/remote-env-local-docker.d.ts +30 -5
- package/dist/plugins/remote-env-local-docker.js +46 -59
- package/dist/plugins/remote-env-ssh.d.ts +18 -4
- package/dist/plugins/remote-env-ssh.js +72 -56
- package/dist/plugins/remote-shell.d.ts +100 -3
- package/dist/plugins/remote-shell.js +166 -2
- package/dist/plugins/retention-store-sql.d.ts +23 -8
- package/dist/plugins/retention-store-sql.js +13 -1
- package/dist/plugins/run-store-sql.d.ts +13 -3
- package/dist/plugins/run-store-sql.js +5 -0
- package/dist/plugins/schema-consistency.d.ts +15 -5
- package/dist/plugins/schema-consistency.js +14 -8
- package/dist/plugins/schema-drift-sentence.d.ts +27 -0
- package/dist/plugins/schema-drift-sentence.js +38 -0
- package/dist/plugins/session-fork.d.ts +63 -12
- package/dist/plugins/session-fork.js +38 -21
- package/dist/plugins/session-ownership.d.ts +7 -0
- package/dist/plugins/session-ownership.js +5 -0
- package/dist/plugins/session-row-sql.d.ts +22 -0
- package/dist/plugins/session-row-sql.js +81 -0
- package/dist/plugins/session-store.d.ts +1 -1
- package/dist/plugins/session-workspace-cell.d.ts +17 -0
- package/dist/plugins/session-workspace-cell.js +38 -0
- package/dist/plugins/store-backend.d.ts +1 -1
- package/dist/plugins/store-backend.js +26 -14
- package/dist/plugins/store-contracts.d.ts +25 -5
- package/dist/plugins/tidb-pool.d.ts +12 -7
- package/dist/plugins/tidb-pool.js +15 -3
- package/dist/plugins/tidb-session-store.d.ts +74 -28
- package/dist/plugins/tidb-session-store.js +132 -53
- package/dist/plugins/tool-result-store-sql.d.ts +16 -8
- package/dist/plugins/tool-result-store-sql.js +26 -17
- package/dist/plugins/worktree-isolation.d.ts +3 -2
- package/dist/plugins/worktree-isolation.js +1 -7
- package/dist/project-identity.d.ts +1 -14
- package/dist/project-identity.js +0 -4
- package/dist/project-memory.d.ts +9 -15
- package/dist/project-memory.js +5 -4
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +6 -7
- package/dist/request-value-closure.js +1 -3
- package/dist/rules-consent.js +6 -4
- package/dist/run-local.d.ts +6 -0
- package/dist/run-local.js +36 -26
- package/dist/runs.d.ts +9 -2
- package/dist/runs.js +9 -2
- package/dist/sandbox-git-plane.d.ts +2 -2
- package/dist/security.d.ts +38 -12
- package/dist/security.js +8 -2
- package/dist/server-secret-env.d.ts +15 -8
- package/dist/server-secret-env.js +4 -2
- package/dist/session-children-retire.d.ts +27 -9
- package/dist/session-children-retire.js +9 -5
- package/dist/session-incarnation.d.ts +19 -10
- package/dist/session-incarnation.js +5 -3
- package/dist/session-row.d.ts +114 -5
- package/dist/session-row.js +55 -5
- package/dist/session-titler.d.ts +2 -2
- package/dist/session-workspace.d.ts +130 -0
- package/dist/session-workspace.js +83 -0
- package/dist/spec-fields.d.ts +1 -1
- package/dist/standing-answer-grant.d.ts +2 -13
- package/dist/standing-answer-grant.js +0 -7
- package/dist/task-a2a.d.ts +44 -14
- package/dist/task-a2a.js +46 -20
- package/dist/task-cwd.d.ts +6 -1
- package/dist/task-mcp.d.ts +33 -20
- package/dist/task-mcp.js +46 -22
- package/dist/tool-approval.d.ts +38 -8
- package/dist/tool-approval.js +57 -13
- package/dist/trace/core-keyset-guard.d.ts +33 -18
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.d.ts +20 -1
- package/dist/trace/exit-seat.js +3 -0
- package/dist/trace/extension-call-face.d.ts +33 -0
- package/dist/trace/extension-call-face.js +39 -0
- package/dist/trace/project.d.ts +26 -7
- package/dist/trace/project.js +30 -3
- package/dist/trace/redact.js +42 -13
- package/dist/trace/self-minted-wire.d.ts +240 -12
- package/dist/trace/self-minted-wire.js +100 -4
- package/dist/trace/wire-projection-faces.d.ts +6 -6
- package/dist/trace/wire-projection-faces.js +6 -5
- package/dist/url-refusal.d.ts +1 -1
- package/dist/user-authored-text.d.ts +2 -2
- package/dist/user-authored-text.js +16 -1
- package/dist/web-admin-client/apply-effective.d.ts +17 -3
- package/dist/web-admin-client/apply-effective.js +20 -10
- package/dist/web-admin-client/center-request.d.ts +3 -2
- package/dist/web-admin-client/center-request.js +2 -3
- package/dist/web-admin-client/skills-mcp.d.ts +8 -4
- package/dist/web-admin-client/skills-mcp.js +32 -18
- package/dist/web-admin-client/types.d.ts +11 -0
- package/package.json +3 -3
- package/wire-changes/server@7.138.0.json +446 -0
- package/wire-changes/server@7.139.0.json +348 -0
- package/dist/http/device-placement.d.ts +0 -175
- package/dist/http/device-placement.js +0 -145
- package/dist/http/mcp-remote-text.d.ts +0 -5
- package/dist/http/mcp-remote-text.js +0 -7
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ExecutionError, FileError, RemoteExecutionError, type ExecutionEnv, type ExecutionEnvFactory, type ExecStreamOptions, type FileInfo, type OutputChunk, type RemoteConnectConfig, type RemoteExecutionEnv, type Result, type SessionToken, type SnapshotId, type VmLifecycleOptions, type WorkspaceHandle, type WriteExpectation, type WriteReceipt } from "@sema-agent/core";
|
|
2
2
|
import { type DeviceOwner, type DeviceRejectCode, type DeviceRow, type DeviceStore } from "../device-store.js";
|
|
3
3
|
import type { DeviceEnrollment } from "../device-enrollment.js";
|
|
4
|
+
import { type ExecutionPlacementSeat } from "../execution-placement.js";
|
|
4
5
|
import type { DeviceWsHub } from "../device-ws-hub.js";
|
|
5
6
|
import type { DeviceInstructionKind, DeviceOutcomeErrorCode } from "../device-ws-protocol.js";
|
|
6
7
|
/** `WorkspaceHandle.provider` 与 `REMOTE_EXEC` 词表的那一个词,单点。 */
|
|
@@ -57,6 +58,8 @@ export interface DeviceEnvFactoryDeps {
|
|
|
57
58
|
homeDir?: string;
|
|
58
59
|
hub: DeviceWsHub;
|
|
59
60
|
store: DeviceStore;
|
|
61
|
+
/** 会话店的执行席(绑定的唯一载体与唯一读;装配点交会话店的 `executionPlacement`,与准入链同一只)。工厂只调它的 `read(root)`。 */
|
|
62
|
+
seat: Pick<ExecutionPlacementSeat, "read">;
|
|
60
63
|
enrollment: DeviceEnrollment;
|
|
61
64
|
/** 指令级默认超时(ms;`DEVICE_EXEC_TIMEOUT_MS`,§4.7)。core 的 per-command `timeout` 可收紧它。 */
|
|
62
65
|
execTimeoutMs?: number;
|
|
@@ -104,18 +107,22 @@ export declare class RemoteDeviceExecutionEnv implements RemoteExecutionEnv {
|
|
|
104
107
|
* (RPC 断、传输掉),不是「一个它看不见的命名空间」⇒ 读边界的执行期复核**拒**,而不是让词法判决站着。
|
|
105
108
|
* 缺席是今天每个适配器的读法(整链沉默 ⇒ 词法判决成立、命令照跑),所以这一声明是**收紧**方向。
|
|
106
109
|
* 严格读法 `=== true`(core `canonicalPathIsAuthoritative`);`isSuspendable` 一族同样严格,对本仓的
|
|
107
|
-
* 布尔字面量零影响(
|
|
110
|
+
* 布尔字面量零影响(四键都写死字面 `true`/`false`,不存在「非布尔值被读成缺席」那一形 —— 那一形由
|
|
108
111
|
* core 的 `config.execution_env_capability_invalid` 普查响亮播报,本仓的受众表已登记)。
|
|
109
112
|
* 谁该声明由闭集门看着(`test/execution-lane-caps.test.ts` 的逐 lane 对账:新 lane 不表态 = 编译红)。
|
|
110
113
|
*/
|
|
114
|
+
/** `sharesProcessTable`(core 7.57.0,必填):命令跑在注册设备上(设备执行器),不在本进程所在的进程表。 */
|
|
115
|
+
/** 能力块(引擎 [ref],八键必填、各键只经引擎读器读):`hostLocalPaths` = 这条车道答出的路径是不是本进程所在机器上的文件;`externalContentTarget` = 产出是否来自部署信任边界之外;`io` = 文件与 shell 两面真到达目标;`keepsReadFloor` = 是否守住读拒表的字节地板(本车道不守 ⇒ 读拒表非空的 run 被告知,部署要求地板时准备期拒)。 */
|
|
111
116
|
readonly capabilities: {
|
|
112
|
-
isolation:
|
|
113
|
-
suspendable:
|
|
114
|
-
canonicalPathAuthoritative:
|
|
117
|
+
isolation: false;
|
|
118
|
+
suspendable: false;
|
|
119
|
+
canonicalPathAuthoritative: true;
|
|
120
|
+
sharesProcessTable: false;
|
|
121
|
+
hostLocalPaths: false;
|
|
122
|
+
externalContentTarget: true;
|
|
123
|
+
io: true;
|
|
124
|
+
keepsReadFloor: false;
|
|
115
125
|
};
|
|
116
|
-
readonly externalContentTarget = true;
|
|
117
|
-
/** [ref]:设备路径不在 worker 宿主文件系统上 —— host-path 回执消费方据此拒绝把它当宿主路径呈现。 */
|
|
118
|
-
readonly hostLocalPaths = false;
|
|
119
126
|
cwd: string;
|
|
120
127
|
/** 可选面(presence-typed,见文件头「铁律」段):构造期按设备声明装配,缺席就是缺席。 */
|
|
121
128
|
readLink?: (path: string, abortSignal?: AbortSignal) => Promise<Result<string, FileError>>;
|
|
@@ -330,11 +337,24 @@ export declare class RemoteDeviceExecutionEnv implements RemoteExecutionEnv {
|
|
|
330
337
|
* `DEVICE_ERROR_CODES` 闭集(装配 / 数据错误取 {@link DEVICE_FACTORY_CONFIG_CODES}:placement 根为空 · 有效工作目录非绝对路径),句子里的 `[码]` 与 `.code` 同值。修前抛裸 `Error`,码只在句子里
|
|
331
338
|
* (有效工作目录那一只直到 7.135.0 仍是裸 `Error`;钉:本工厂与 {@link effectiveDeviceCwd} 的函数体里没有 `new Error(`)。
|
|
332
339
|
*
|
|
333
|
-
* 🔴
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
340
|
+
* 🔴 **工厂认设备不认住客**(读口一只 = 会话店执行席 `seat.read(root)`,与解析器 / 换绑门 / 执行放置读面 / 准入链同一只读):按 placement 根读**当前**格,
|
|
341
|
+
* 与这条腿带来的设备句柄 `honor` 比,不同即 typed 拒。判序(一次行读):
|
|
342
|
+
* root = ctx.placementRootSessionId // 空 ⇒ config.placement_root_invalid
|
|
343
|
+
* honor = ctx.resumeWorkspace?.deviceId
|
|
344
|
+
* cell = seat.read(root).cell // 行不在 / 格缺 / mirror / writer(device, ∅) ⇒ device.not_bound(唯一读法 `boundDeviceOf`)
|
|
345
|
+
* honor ≠ undefined ∧ 格上设备 ≠ honor ⇒ device.identity_mismatch(句点名两台,零连接)
|
|
346
|
+
* readDeviceForAdmission(格上设备, 格上属主)
|
|
347
|
+
* 工厂**不**读 `task_run`、不读子代分区行、不按 `ctx.taskId`、不比化身、不比属主(住客谓词只用于写与 judged 复核,不是工厂的读者)。
|
|
348
|
+
* 续跑腿(core 7.54.0 [ref] r3 / [ref],`ExecutionEnvFactoryContext.resumeWorkspace`):行记下了设备 ⇒ 本工厂**只**在那台设备上铸 —— 根此刻绑的不是它(换绑过)
|
|
349
|
+
* ⇒ `device.identity_mismatch`(换绑后赎回的同一个词,零新码);不连另一台、不改道(新设备没有那个工作区)。「绑的就是它但现在不在线」与新跑同形:
|
|
350
|
+
* 构造成功、`connect()` 回 typed `device.offline`(从不连别的设备)。行没记设备(早于该字段的行)⇒ 引擎已命名的「行未记设备 ⇒ 普通路由」臂,按根读当前格。
|
|
351
|
+
* 句柄(`workspaceHandle()`)恒报连接真到达的设备,不抄 `resumeWorkspace`。
|
|
352
|
+
*
|
|
353
|
+
* 🔴 **过渡期(条件句)**:新派后代腿的放置句柄(引擎在 `ExecutionEnvFactoryContext` 上的放置工作区字段,`honor` 的第二来源)**只在本仓所基的引擎版本带它时**
|
|
354
|
+
* 进判序;本仓今天所基的引擎上下文没有这个字段 ⇒ `honor` 只取续跑腿的 `resumeWorkspace`,新派后代腿按根读当前格。本仓**不**注入任何替代见证、不造任何行
|
|
355
|
+
* (修在源头)。敞口(在那个字段到货之前成立):活过父 turn 的后台子代,在根换绑之后新派的腿会落到根当前设备 —— 换绑半边由换绑门的子代活性判关闭(归换绑门那一车),
|
|
356
|
+
* 删除半边另有票。字段到货后的形:`honor = ctx.resumeWorkspace?.deviceId ?? <放置句柄>.deviceId`,放置句柄的 provider 不是本车道 ⇒ 装配拒(`config.placement_root_invalid` 同级)。
|
|
357
|
+
* 本仓任何文本不得宣称工厂「一律按住客定址」。
|
|
338
358
|
*/
|
|
339
359
|
export declare function deviceExecutionEnvFactory(deps: DeviceEnvFactoryDeps): ExecutionEnvFactory;
|
|
340
360
|
/**
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { posix } from "node:path";
|
|
2
2
|
import { ExecutionError, FileError, RemoteExecutionError, } from "@sema-agent/core";
|
|
3
3
|
import { DEVICE_ERROR_CODES, isDeviceAbsolutePath, } from "../device-store.js";
|
|
4
|
+
import { boundDeviceOf } from "../execution-placement.js";
|
|
5
|
+
import { createCutError, createExecFailure } from "../exec-cut.js";
|
|
4
6
|
export const DEVICE_PROVIDER = "device";
|
|
5
7
|
const ok = (value) => ({ ok: true, value });
|
|
6
8
|
const STREAM_PENDING_MAX_BYTES = 2 * 1024 * 1024;
|
|
@@ -126,9 +128,7 @@ export class RemoteDeviceExecutionEnv {
|
|
|
126
128
|
homeDir;
|
|
127
129
|
cdPath = "unknown";
|
|
128
130
|
withheldEnv = [];
|
|
129
|
-
capabilities = { isolation: false, suspendable: false, canonicalPathAuthoritative: true };
|
|
130
|
-
externalContentTarget = true;
|
|
131
|
-
hostLocalPaths = false;
|
|
131
|
+
capabilities = { isolation: false, suspendable: false, canonicalPathAuthoritative: true, sharesProcessTable: false, hostLocalPaths: false, externalContentTarget: true, io: true, keepsReadFloor: false };
|
|
132
132
|
cwd;
|
|
133
133
|
readLink;
|
|
134
134
|
writeFileExclusive;
|
|
@@ -227,7 +227,8 @@ export class RemoteDeviceExecutionEnv {
|
|
|
227
227
|
}
|
|
228
228
|
let admitted;
|
|
229
229
|
try {
|
|
230
|
-
|
|
230
|
+
const placement = await this.cfg.enrollment.readPlacement(this.cfg.rootSessionId);
|
|
231
|
+
admitted = await this.cfg.enrollment.admitSession({ rootSessionId: this.cfg.rootSessionId, placement, requester: this.cfg.owner });
|
|
231
232
|
}
|
|
232
233
|
catch (err) {
|
|
233
234
|
return {
|
|
@@ -302,7 +303,7 @@ export class RemoteDeviceExecutionEnv {
|
|
|
302
303
|
return { ok: false, error: new ExecutionError("shell_unavailable", "execution env already destroyed") };
|
|
303
304
|
const failure = await this.ensureConnected();
|
|
304
305
|
if (failure)
|
|
305
|
-
return { ok: false, error:
|
|
306
|
+
return { ok: false, error: createExecFailure(failure.exec, failure.message) };
|
|
306
307
|
return undefined;
|
|
307
308
|
}
|
|
308
309
|
async fsGate(p) {
|
|
@@ -381,15 +382,14 @@ export class RemoteDeviceExecutionEnv {
|
|
|
381
382
|
case "env_terminal":
|
|
382
383
|
return { ok: false, error: new ExecutionError("shell_unavailable", ENV_TERMINAL_MESSAGE) };
|
|
383
384
|
case "rejected":
|
|
384
|
-
return { ok: false, error:
|
|
385
|
+
return { ok: false, error: createExecFailure(DEVICE_REJECT_TO_EXEC_ERROR[res.reject.code], rejectText(res.reject)) };
|
|
385
386
|
case "never_started":
|
|
386
387
|
case "settled": {
|
|
387
388
|
const o = res.outcome;
|
|
388
|
-
if (!o.ok)
|
|
389
|
-
return { ok: false, error:
|
|
390
|
-
}
|
|
389
|
+
if (!o.ok)
|
|
390
|
+
return { ok: false, error: createExecFailure(DEVICE_OUTCOME_TO_EXEC_ERROR[o.errorCode], o.message, res.kind === "settled" ? { stdout, stderr } : undefined) };
|
|
391
391
|
if (o.kind === "cancelled") {
|
|
392
|
-
return { ok: false, error:
|
|
392
|
+
return { ok: false, error: createCutError({ code: "aborted", message: `the device reported the command as ${o.state} after a cancel` }, { stdout, stderr }) };
|
|
393
393
|
}
|
|
394
394
|
if (o.kind !== "exec")
|
|
395
395
|
return { ok: false, error: new ExecutionError("unknown", `device returned a "${o.kind}" outcome for an exec — protocol violation`) };
|
|
@@ -799,17 +799,18 @@ export function deviceExecutionEnvFactory(deps) {
|
|
|
799
799
|
throw deviceFactoryRefusal(DEVICE_FACTORY_CONFIG_CODES.PLACEMENT_ROOT_INVALID, `device lane: ExecutionEnvFactoryContext.placementRootSessionId is empty [${DEVICE_FACTORY_CONFIG_CODES.PLACEMENT_ROOT_INVALID}] — ` +
|
|
800
800
|
"the device placement is keyed on the run tree's placement root; defaulting to the child sessionId would read as a NEW placement (v2 §4.3.2 / core design/380 O1①)");
|
|
801
801
|
}
|
|
802
|
-
const
|
|
802
|
+
const placement = await deps.seat.read(rootSessionId);
|
|
803
|
+
const binding = placement === undefined ? undefined : boundDeviceOf(placement.cell);
|
|
803
804
|
if (!binding) {
|
|
804
805
|
throw deviceFactoryRefusal(DEVICE_ERROR_CODES.NOT_BOUND, `device lane: no device binding for placement root ${rootSessionId} [${DEVICE_ERROR_CODES.NOT_BOUND}] — ` +
|
|
805
806
|
"the submit-time admission chain must bind the root session to a device before a run reaches the factory (v2 §4.3.2)");
|
|
806
807
|
}
|
|
807
|
-
const
|
|
808
|
-
if (
|
|
809
|
-
throw deviceFactoryRefusal(DEVICE_ERROR_CODES.IDENTITY_MISMATCH, `device lane: this resume rebuilds the workspace on device ${
|
|
808
|
+
const honor = ctx.resumeWorkspace?.deviceId;
|
|
809
|
+
if (honor !== undefined && honor !== binding.id) {
|
|
810
|
+
throw deviceFactoryRefusal(DEVICE_ERROR_CODES.IDENTITY_MISMATCH, `device lane: this resume rebuilds the workspace on device ${honor}, but placement root ${rootSessionId} is now bound to ${binding.id} [${DEVICE_ERROR_CODES.IDENTITY_MISMATCH}] — ` +
|
|
810
811
|
"a parked call does not follow a rebind (the new device has no copy of that workspace); finish or cancel the parked task, then start it again on the device now bound");
|
|
811
812
|
}
|
|
812
|
-
const view = await deps.store.readDeviceForAdmission(binding.
|
|
813
|
+
const view = await deps.store.readDeviceForAdmission(binding.id, binding.owner);
|
|
813
814
|
if (!view) {
|
|
814
815
|
throw deviceFactoryRefusal(DEVICE_ERROR_CODES.NOT_FOUND, `device lane: the bound device is gone or not owned by the binding's principal [${DEVICE_ERROR_CODES.NOT_FOUND}]`);
|
|
815
816
|
}
|
|
@@ -821,13 +822,13 @@ export function deviceExecutionEnvFactory(deps) {
|
|
|
821
822
|
hub: deps.hub,
|
|
822
823
|
enrollment: deps.enrollment,
|
|
823
824
|
rootSessionId,
|
|
824
|
-
deviceId: binding.
|
|
825
|
+
deviceId: binding.id,
|
|
825
826
|
owner: binding.owner,
|
|
826
827
|
cwd: effectiveDeviceCwd(deps.perSessionCwd?.get(ctx.sessionId), ctx.parentCwd, view.row),
|
|
827
828
|
...(deps.homeDir === undefined ? {} : { homeDir: deps.homeDir }),
|
|
828
829
|
...(deps.perSessionShellEnv?.get(ctx.sessionId) === undefined ? {} : { shellEnv: deps.perSessionShellEnv.get(ctx.sessionId) }),
|
|
829
830
|
execTimeoutMs,
|
|
830
|
-
supports: deps.hub.declaredSupports(binding.
|
|
831
|
+
supports: deps.hub.declaredSupports(binding.id),
|
|
831
832
|
...(deps.logger === undefined ? {} : { logger: deps.logger }),
|
|
832
833
|
});
|
|
833
834
|
};
|
|
@@ -135,10 +135,17 @@ export declare class RemoteContainerExecutionEnv implements RemoteExecutionEnv,
|
|
|
135
135
|
constructor(config: RemoteContainerEnvConfig, deps?: RemoteContainerEnvDeps);
|
|
136
136
|
/** [ref] §9: E2B is a FULL RemoteExecutionEnv — an isolated, snapshot-suspendable microVM. (SSH/ADB
|
|
137
137
|
* adapters declare {isolation:false, suspendable:false} — real machines/devices, not snapshotable.) */
|
|
138
|
+
/** `sharesProcessTable`(core 7.57.0,必填):命令跑在云沙箱里,不在本进程所在的进程表。 */
|
|
139
|
+
/** 能力块(引擎 [ref],八键必填、各键只经引擎读器读):`hostLocalPaths` = 这条车道答出的路径是不是本进程所在机器上的文件;`externalContentTarget` = 产出是否来自部署信任边界之外;`io` = 文件与 shell 两面真到达目标;`keepsReadFloor` = 是否守住读拒表的字节地板(本车道不守 ⇒ 读拒表非空的 run 被告知,部署要求地板时准备期拒)。 */
|
|
138
140
|
readonly capabilities: {
|
|
139
|
-
isolation:
|
|
140
|
-
suspendable:
|
|
141
|
-
canonicalPathAuthoritative:
|
|
141
|
+
isolation: true;
|
|
142
|
+
suspendable: true;
|
|
143
|
+
canonicalPathAuthoritative: true;
|
|
144
|
+
sharesProcessTable: false;
|
|
145
|
+
hostLocalPaths: false;
|
|
146
|
+
externalContentTarget: false;
|
|
147
|
+
io: true;
|
|
148
|
+
keepsReadFloor: false;
|
|
142
149
|
};
|
|
143
150
|
workspaceHandle(): WorkspaceHandle;
|
|
144
151
|
/**
|
|
@@ -252,7 +259,14 @@ export declare class RemoteContainerExecutionEnv implements RemoteExecutionEnv,
|
|
|
252
259
|
abortSignal?: AbortSignal;
|
|
253
260
|
}): Promise<Result<string[], FileError>>;
|
|
254
261
|
readBinaryFile(p: string, abortSignal?: AbortSignal): Promise<Result<Uint8Array, FileError>>;
|
|
262
|
+
/** 暂存写的两只面(规则与提交脚本的唯一属主 = `remote-shell.ts` `writeFileStaged` / `appendFileStaged`;本腿是原生 SDK 腿,与四条
|
|
263
|
+
* shell 腿共用的是这只编排,不是 `posix-shell-fs` 基座;父目录与私有暂存目录由编排的准备那一步建):上传 = `files.write` 整份写到私有目录里的
|
|
264
|
+
* 暂存件;提交 = 一次 `exec`。两者同一身份(`files.write` 与 `commands.run` 都不带 `user`,沙箱侧取同一个缺省用户)。 */
|
|
265
|
+
private stagedLane;
|
|
266
|
+
/** 整旧或整新(暂存 → 一次 `mv -f`)。修前 `files.write` 直落目标:上传中途断 / 取消时目标剩什么取决于沙箱侧的落地实现,本适配器不依赖它。 */
|
|
255
267
|
writeFile(p: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;
|
|
268
|
+
/** 追加(暂存要加的字节 → 一次 `cat >>`;inode 不换、已有字节不重写)。修前 = 读旧 + 拼 + 整份 `files.write`(O(文件);中途断 ⇒
|
|
269
|
+
* 半份)。目录不收新名字 / 私有目录收不成私有时的退路 = 旧形(`rewriteAppend`:读旧 + 拼 + 整份 `files.write` 到目标;只有写那一步经 `ctx.inPlace`)。 */
|
|
256
270
|
appendFile(p: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;
|
|
257
271
|
fileInfo(p: string, abortSignal?: AbortSignal): Promise<Result<FileInfo, FileError>>;
|
|
258
272
|
listDir(p: string, abortSignal?: AbortSignal): Promise<Result<FileInfo[], FileError>>;
|
|
@@ -338,6 +352,8 @@ export declare class RemoteContainerExecutionEnv implements RemoteExecutionEnv,
|
|
|
338
352
|
private tempResult;
|
|
339
353
|
private toFileInfo;
|
|
340
354
|
private fileError;
|
|
355
|
+
/** A thrown provider error → this exec's error. The two cuts (aborted, the provider's own command deadline) are a command that RAN: they carry what it had
|
|
356
|
+
* written (`captured`, `createCutError`); an unclassified fault does not (its outcome is unknown, the engine reads no partial output from it). */
|
|
341
357
|
private execError;
|
|
342
358
|
private remoteError;
|
|
343
359
|
}
|
|
@@ -2,8 +2,9 @@ import path from "node:path";
|
|
|
2
2
|
import { Sandbox, CommandExitError, FileType } from "e2b";
|
|
3
3
|
import { FileError, ExecutionError, RemoteExecutionError, RollingTailBuffer, markTruncated, } from "@sema-agent/core";
|
|
4
4
|
import { fileErrorFromExec } from "./remote-env-file-error.js";
|
|
5
|
-
import { numEnvOr, runSymlinkChain, shellQuote } from "./remote-shell.js";
|
|
5
|
+
import { appendFileStaged, rewriteAppend, numEnvOr, runSymlinkChain, shellQuote, writeFileStaged } from "./remote-shell.js";
|
|
6
6
|
import { buildLivenessCut, commandBoundsOf, firstBoundOf, laneLivenessOf, LIVENESS_MS_DEFAULT } from "../exec-bounds.js";
|
|
7
|
+
import { createCutError } from "../exec-cut.js";
|
|
7
8
|
import { BackgroundShellManager } from "./background-shell-support.js";
|
|
8
9
|
const PROVIDER = "e2b";
|
|
9
10
|
const DEFAULT_MOUNT_PATH = "/home/user";
|
|
@@ -46,7 +47,7 @@ export class RemoteContainerExecutionEnv {
|
|
|
46
47
|
this.homeDir = config.homeDir;
|
|
47
48
|
this.sandboxApi = deps.sandboxApi ?? Sandbox;
|
|
48
49
|
}
|
|
49
|
-
capabilities = { isolation: true, suspendable: true, canonicalPathAuthoritative: true };
|
|
50
|
+
capabilities = { isolation: true, suspendable: true, canonicalPathAuthoritative: true, sharesProcessTable: false, hostLocalPaths: false, externalContentTarget: false, io: true, keepsReadFloor: false };
|
|
50
51
|
workspaceHandle() {
|
|
51
52
|
if (!this.handle) {
|
|
52
53
|
throw new RemoteExecutionError("connect_failed", "workspaceHandle() called before connect()");
|
|
@@ -251,6 +252,8 @@ export class RemoteContainerExecutionEnv {
|
|
|
251
252
|
stderr: { tail: new RollingTailBuffer(), total: 0, cursor: 0 },
|
|
252
253
|
}
|
|
253
254
|
: undefined;
|
|
255
|
+
let started;
|
|
256
|
+
const captured = () => ({ stdout: started?.stdout ?? "", stderr: started?.stderr ?? "" });
|
|
254
257
|
let detached = false;
|
|
255
258
|
const teePush = (s, d) => {
|
|
256
259
|
const b = Buffer.from(d, "utf8");
|
|
@@ -299,10 +302,10 @@ export class RemoteContainerExecutionEnv {
|
|
|
299
302
|
this.settleAbandonedStart(starting);
|
|
300
303
|
const cut = e instanceof RemoteExecutionError && e.code === "timeout" && !options?.abortSignal?.aborted;
|
|
301
304
|
if (!cut)
|
|
302
|
-
return { ok: false, error: this.execError(e, options?.abortSignal) };
|
|
303
|
-
|
|
304
|
-
return { ok: false, error: new ExecutionError(bc.code, bc.message, undefined, undefined, bc.noOutputSec) };
|
|
305
|
+
return { ok: false, error: this.execError(e, options?.abortSignal, captured()) };
|
|
306
|
+
return { ok: false, error: createCutError(boundCut(first), captured()) };
|
|
305
307
|
}
|
|
308
|
+
started = handle;
|
|
306
309
|
const onAbort = () => { if (!detached)
|
|
307
310
|
void handle.kill().catch(() => { }); };
|
|
308
311
|
if (options?.abortSignal) {
|
|
@@ -352,6 +355,7 @@ export class RemoteContainerExecutionEnv {
|
|
|
352
355
|
disarmed: () => detached,
|
|
353
356
|
...(tee ? { wall: { totalMs: this.commandTimeoutMs(options?.timeout), leftMs: Math.max(0, this.commandTimeoutMs(options?.timeout) - (Date.now() - startedAt)) } } : {}),
|
|
354
357
|
liveness: bounds.liveness,
|
|
358
|
+
captured,
|
|
355
359
|
});
|
|
356
360
|
const winner = await (detachedP ? Promise.race([fg, detachedP]) : fg);
|
|
357
361
|
if (winner === "detached") {
|
|
@@ -365,18 +369,18 @@ export class RemoteContainerExecutionEnv {
|
|
|
365
369
|
});
|
|
366
370
|
}
|
|
367
371
|
if (options?.abortSignal?.aborted && !detached) {
|
|
368
|
-
return { ok: false, error:
|
|
372
|
+
return { ok: false, error: createCutError({ code: "aborted", message: "exec aborted (remote process killed)" }, captured()) };
|
|
369
373
|
}
|
|
370
374
|
return ok({ stdout: winner.stdout, stderr: winner.stderr, exitCode: winner.exitCode });
|
|
371
375
|
}
|
|
372
376
|
catch (e) {
|
|
373
377
|
if (options?.abortSignal?.aborted && !detached) {
|
|
374
|
-
return { ok: false, error: this.execError(e, options.abortSignal) };
|
|
378
|
+
return { ok: false, error: this.execError(e, options.abortSignal, captured()) };
|
|
375
379
|
}
|
|
376
380
|
if (e instanceof CommandExitError) {
|
|
377
381
|
return ok({ stdout: e.stdout, stderr: e.stderr, exitCode: e.exitCode });
|
|
378
382
|
}
|
|
379
|
-
return { ok: false, error: this.execError(e, options?.abortSignal) };
|
|
383
|
+
return { ok: false, error: this.execError(e, options?.abortSignal, captured()) };
|
|
380
384
|
}
|
|
381
385
|
finally {
|
|
382
386
|
fgSettled = true;
|
|
@@ -659,37 +663,33 @@ export class RemoteContainerExecutionEnv {
|
|
|
659
663
|
return { ok: false, error: this.fileError(e, this.resolve(p), abortSignal) };
|
|
660
664
|
}
|
|
661
665
|
}
|
|
666
|
+
stagedLane(sbx, content) {
|
|
667
|
+
const data = typeof content === "string" ? content : toArrayBuffer(content);
|
|
668
|
+
return {
|
|
669
|
+
upload: async (dest, signal) => {
|
|
670
|
+
try {
|
|
671
|
+
await this.rpc("files.write", (s) => sbx.files.write(dest, data, { signal: s }), signal, this.cfg.dataTimeoutMs);
|
|
672
|
+
return ok(undefined);
|
|
673
|
+
}
|
|
674
|
+
catch (e) {
|
|
675
|
+
return { ok: false, error: this.fileError(e, dest, signal) };
|
|
676
|
+
}
|
|
677
|
+
},
|
|
678
|
+
run: (cmd, signal) => this.exec(cmd, { cwd: "/", ...(signal !== undefined ? { abortSignal: signal } : {}) }),
|
|
679
|
+
};
|
|
680
|
+
}
|
|
662
681
|
async writeFile(p, content, abortSignal) {
|
|
663
682
|
const sbx = await this.fsReady(p);
|
|
664
683
|
if (!sbx.ok)
|
|
665
684
|
return sbx;
|
|
666
|
-
|
|
667
|
-
const data = typeof content === "string" ? content : toArrayBuffer(content);
|
|
668
|
-
await this.rpc("files.write", (signal) => sbx.value.files.write(this.resolve(p), data, { signal }), abortSignal, this.cfg.dataTimeoutMs);
|
|
669
|
-
return ok(undefined);
|
|
670
|
-
}
|
|
671
|
-
catch (e) {
|
|
672
|
-
return { ok: false, error: this.fileError(e, this.resolve(p), abortSignal) };
|
|
673
|
-
}
|
|
685
|
+
return writeFileStaged(this.stagedLane(sbx.value, content), this.resolve(p), abortSignal);
|
|
674
686
|
}
|
|
675
687
|
async appendFile(p, content, abortSignal) {
|
|
688
|
+
const sbx = await this.fsReady(p);
|
|
689
|
+
if (!sbx.ok)
|
|
690
|
+
return sbx;
|
|
676
691
|
const abs = this.resolve(p);
|
|
677
|
-
|
|
678
|
-
let base;
|
|
679
|
-
if (existing.ok) {
|
|
680
|
-
base = existing.value;
|
|
681
|
-
}
|
|
682
|
-
else if (existing.error.code === "not_found") {
|
|
683
|
-
base = new Uint8Array(0);
|
|
684
|
-
}
|
|
685
|
-
else {
|
|
686
|
-
return existing;
|
|
687
|
-
}
|
|
688
|
-
const add = typeof content === "string" ? new TextEncoder().encode(content) : content;
|
|
689
|
-
const merged = new Uint8Array(base.length + add.length);
|
|
690
|
-
merged.set(base, 0);
|
|
691
|
-
merged.set(add, base.length);
|
|
692
|
-
return this.writeFile(abs, merged, abortSignal);
|
|
692
|
+
return appendFileStaged(this.stagedLane(sbx.value, content), abs, abortSignal, (ctx) => rewriteAppend((signal) => this.readBinaryFile(abs, signal), (bytes, signal) => this.stagedLane(sbx.value, bytes).upload(abs, signal), content, ctx));
|
|
693
693
|
}
|
|
694
694
|
async fileInfo(p, abortSignal) {
|
|
695
695
|
const sbx = await this.fsReady(p);
|
|
@@ -851,16 +851,15 @@ export class RemoteContainerExecutionEnv {
|
|
|
851
851
|
async waitWithLiveness(handle, lastProgress, opts) {
|
|
852
852
|
let timer;
|
|
853
853
|
let wallTimer;
|
|
854
|
-
const liveness = opts
|
|
854
|
+
const liveness = opts.liveness;
|
|
855
855
|
const idle = new Promise((_, reject) => {
|
|
856
856
|
const arm = (bound) => {
|
|
857
857
|
timer = setTimeout(() => {
|
|
858
|
-
if (opts
|
|
858
|
+
if (opts.disarmed?.())
|
|
859
859
|
return;
|
|
860
860
|
const idleFor = Date.now() - lastProgress();
|
|
861
861
|
if (idleFor >= bound.ms) {
|
|
862
|
-
|
|
863
|
-
reject(new ExecutionError(cut.code, cut.message, undefined, undefined, cut.noOutputSec));
|
|
862
|
+
reject(createCutError(buildLivenessCut(bound), opts.captured()));
|
|
864
863
|
void handle.kill().catch(() => { });
|
|
865
864
|
this.cfg.metrics?.inc("remote_env_exec_idle_timeout_total", { provider: PROVIDER });
|
|
866
865
|
}
|
|
@@ -871,18 +870,18 @@ export class RemoteContainerExecutionEnv {
|
|
|
871
870
|
};
|
|
872
871
|
if (liveness !== undefined)
|
|
873
872
|
arm(liveness);
|
|
874
|
-
if (opts
|
|
873
|
+
if (opts.wall !== undefined) {
|
|
875
874
|
const wall = opts.wall;
|
|
876
875
|
wallTimer = setTimeout(() => {
|
|
877
876
|
if (opts.disarmed?.())
|
|
878
877
|
return;
|
|
879
|
-
reject(
|
|
878
|
+
reject(createCutError({ code: "timeout", message: wallCutMessage(wall.totalMs) }, opts.captured()));
|
|
880
879
|
void handle.kill().catch(() => { });
|
|
881
880
|
}, wall.leftMs);
|
|
882
881
|
}
|
|
883
882
|
});
|
|
884
883
|
try {
|
|
885
|
-
return await Promise.race([opts
|
|
884
|
+
return await Promise.race([opts.waitP ?? handle.wait(), idle]);
|
|
886
885
|
}
|
|
887
886
|
finally {
|
|
888
887
|
if (timer)
|
|
@@ -953,15 +952,15 @@ export class RemoteContainerExecutionEnv {
|
|
|
953
952
|
return new FileError("is_directory", msg, p);
|
|
954
953
|
return new FileError("unknown", msg, p);
|
|
955
954
|
}
|
|
956
|
-
execError(e, abortSignal) {
|
|
955
|
+
execError(e, abortSignal, captured) {
|
|
957
956
|
if (e instanceof ExecutionError)
|
|
958
957
|
return e;
|
|
959
958
|
const msg = errMsg(e);
|
|
960
959
|
const name = e?.name;
|
|
961
960
|
if (abortSignal?.aborted || name === "AbortError")
|
|
962
|
-
return
|
|
961
|
+
return createCutError({ code: "aborted", message: msg }, captured);
|
|
963
962
|
if (providerTimedOut(e))
|
|
964
|
-
return
|
|
963
|
+
return createCutError({ code: "timeout", message: msg }, captured);
|
|
965
964
|
return new ExecutionError("unknown", msg, e instanceof Error ? e : undefined);
|
|
966
965
|
}
|
|
967
966
|
remoteError(e, code = "unknown") {
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
import { FileError, type ExecutionError, type RemoteExecutionError } from "@sema-agent/core";
|
|
19
19
|
/** exec 层的错误码 → 文件层的码。只映射**有对应语义**的那些,其余落 `unknown`(诚实降级)。 */
|
|
20
20
|
export declare function fileErrorFromExec(e: ExecutionError | RemoteExecutionError, path?: string): FileError;
|
|
21
|
+
/** POSIX 的一个名字段上限(NAME_MAX,字节;Linux / macOS / 常见文件系统同值)。也是远端写的私有暂存目录名的上限(`remote-shell.ts` `buildStagingPaths`)。 */
|
|
22
|
+
export declare const NAME_MAX_BYTES = 255;
|
|
21
23
|
/**
|
|
22
24
|
* core 7.56.0 [ref]:`ENAMETOOLONG` 的**一只读法**(本文件的 stderr 分类器与 host 车道的 errno 映射同调)—— 路径有一段超过 NAME_MAX(按 UTF-8 字节量)⇒
|
|
23
25
|
* `name_too_long`:没有目录装得下这个名字,引擎读站把它读作缺席(与 `not_found` 同读);段都短、只是整条超 PATH_MAX ⇒ `unknown`(那种文件可能存在,从下层目录够得着)。
|
|
@@ -3,7 +3,7 @@ export function fileErrorFromExec(e, path) {
|
|
|
3
3
|
const code = e.code === "aborted" ? "aborted" : "unknown";
|
|
4
4
|
return new FileError(code, e.message, path, e);
|
|
5
5
|
}
|
|
6
|
-
const NAME_MAX_BYTES = 255;
|
|
6
|
+
export const NAME_MAX_BYTES = 255;
|
|
7
7
|
export function nameTooLongCode(p) {
|
|
8
8
|
return p !== undefined && p.split(/[\\/]/).some((part) => Buffer.byteLength(part, "utf8") > NAME_MAX_BYTES) ? "name_too_long" : "unknown";
|
|
9
9
|
}
|
|
@@ -71,14 +71,21 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
71
71
|
* (RPC 断、传输掉),不是「一个它看不见的命名空间」⇒ 读边界的执行期复核**拒**,而不是让词法判决站着。
|
|
72
72
|
* 缺席是今天每个适配器的读法(整链沉默 ⇒ 词法判决成立、命令照跑),所以这一声明是**收紧**方向。
|
|
73
73
|
* 严格读法 `=== true`(core `canonicalPathIsAuthoritative`);`isSuspendable` 一族同样严格,对本仓的
|
|
74
|
-
* 布尔字面量零影响(
|
|
74
|
+
* 布尔字面量零影响(四键都写死字面 `true`/`false`,不存在「非布尔值被读成缺席」那一形 —— 那一形由
|
|
75
75
|
* core 的 `config.execution_env_capability_invalid` 普查响亮播报,本仓的受众表已登记)。
|
|
76
76
|
* 谁该声明由闭集门看着(`test/execution-lane-caps.test.ts` 的逐 lane 对账:新 lane 不表态 = 编译红)。
|
|
77
77
|
*/
|
|
78
|
+
/** `sharesProcessTable`(core 7.57.0,必填):命令是本进程 spawn 的子进程(同一张进程表),引擎的模式杀守卫对它照 NodeExecutionEnv 一样问 `pgrep`。 */
|
|
79
|
+
/** 能力块(引擎 [ref],八键必填、各键只经引擎读器读):`hostLocalPaths` = 这条车道答出的路径是不是本进程所在机器上的文件;`externalContentTarget` = 产出是否来自部署信任边界之外;`io` = 文件与 shell 两面真到达目标;`keepsReadFloor` = 是否守住读拒表的字节地板(本车道不守 ⇒ 读拒表非空的 run 被告知,部署要求地板时准备期拒)。 */
|
|
78
80
|
readonly capabilities: {
|
|
79
|
-
isolation:
|
|
80
|
-
suspendable:
|
|
81
|
-
canonicalPathAuthoritative:
|
|
81
|
+
isolation: false;
|
|
82
|
+
suspendable: false;
|
|
83
|
+
canonicalPathAuthoritative: true;
|
|
84
|
+
sharesProcessTable: true;
|
|
85
|
+
hostLocalPaths: true;
|
|
86
|
+
externalContentTarget: false;
|
|
87
|
+
io: true;
|
|
88
|
+
keepsReadFloor: false;
|
|
82
89
|
};
|
|
83
90
|
/** R7 自唤醒: structural delegation to the injected scheduler backend. `hasScheduler(env)` checks BOTH the method
|
|
84
91
|
* presence (always, since we implement the interface) AND `schedulerCapabilities.supported===true` — which is only
|
|
@@ -297,8 +304,9 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
297
304
|
maxLines?: number;
|
|
298
305
|
abortSignal?: AbortSignal;
|
|
299
306
|
}): Promise<Result<string[], FileError>>;
|
|
300
|
-
/** core `NodeExecutionEnv`
|
|
301
|
-
|
|
307
|
+
/** core `NodeExecutionEnv` 的一只实例 —— 本车道把**落盘那一步**委托给它的两只面({@link openBound} 的持有读、{@link writeFile} 的整文件替换);
|
|
308
|
+
* 首次用时建(构造无副作用,只记 cwd)。门(`fsReady`)、在飞登记(`withInflightFs`)与工作区相对解析留在本适配器。 */
|
|
309
|
+
private coreFs;
|
|
302
310
|
/**
|
|
303
311
|
* core 7.50.0 `FileSystem.openBound`:开着并**持有**一只对象(叶链接不跟,开后身份对拍),调用方判的与读的是同一只对象 ——
|
|
304
312
|
* LSP 会话的读口与路径附件只经它读,不实现 ⇒ 两者在本车道一律拒。
|
|
@@ -308,8 +316,15 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
|
|
|
308
316
|
*/
|
|
309
317
|
openBound(p: string, abortSignal?: AbortSignal): Promise<Result<BoundFileObject, FileError>>;
|
|
310
318
|
readBinaryFile(p: string, abortSignal?: AbortSignal): Promise<Result<Uint8Array, FileError>>;
|
|
311
|
-
/**
|
|
312
|
-
*
|
|
319
|
+
/**
|
|
320
|
+
* 整文件替换:**整旧或整新;`aborted` ⇒ 目标未碰**。
|
|
321
|
+
* 🔴 **不重写**:落盘那一步委托 core `NodeExecutionEnv.writeFile`(同目录暂存 + fsync + rename;父目录自建;单跳链接改写它指的
|
|
322
|
+
* 常规文件、链接链 / 非常规文件 / rename 不可用的文件系统回落就地写;已有文件的权限位带过去)—— 该原语的唯一属主在 core。
|
|
323
|
+
* 本适配器只给它与其余写面同一的入口纪律:[ref] 在飞登记(整只方法体**含** `fsReady` 那道门走 `withInflightFs` —— 登记必须与
|
|
324
|
+
* 调用方同一同步段,否则 destroy 的快照可能刚好在 `await this.fsReady(...)` 的让出点上取完;见那处头注)、销毁后拒、工作区相对解析。
|
|
325
|
+
* 修前这里是裸 `fs.writeFile(abs, data, { signal })`:open 即 O_TRUNC,中途取消 / 出错 ⇒ 目标 = 新内容的前缀,原件不可恢复。
|
|
326
|
+
* 已知语义变化(与 core / CC 同形):rename 断开硬链接 —— 另一条链接留旧内容。
|
|
327
|
+
*/
|
|
313
328
|
writeFile(p: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;
|
|
314
329
|
/** [ref](写面一律 `withInflightFs`,见那处头注)。本面是唯一能在 `destroyed` **之后**过门的写面
|
|
315
330
|
* (`lingeringBgWrite`):那一族登记在 destroy 的快照**之后**,所以 destroy 不会去等它们 —— 正确,
|