qwen-code/scripts/sandbox-prototype/api.mjs
jinye 3486cd8f86
feat(core): Add the bwrap execution foundation (#12067)
* feat(core): add bwrap execution foundation

Extract the internal structured launch, confined worker and trusted completion boundary from the tool sandbox work. Keep runtime policy and CLI activation for subsequent changes, and include worker packaging and standalone Linux acceptance.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(core): bound streamStdout post-exit settle and harden sandbox plumbing

Address PR #12067 review findings:

- settle streaming pipe executions on 'close' or a bounded 1s post-exit
  drain, whichever comes first: settling on 'close' alone wedged the
  result promise whenever an exited shell left a grandchild holding the
  pipes (background shells, ACP channel) — @chiga0 F1, ci-bot R1-2
- keep transport-level stdin EPIPE out of the spawn-error slot unless the
  process left no exit information, so a receipt-confirmed run finalizes
  confirmed and an interrupted run cleans up (ci-bot R1-26); re-key
  sandbox evidence retention on the finalized status (R1-24) and prefer
  the specific transport/spawn error over the generic status error (R1-22)
- allowlist the two new sandbox dist assets for standalone packaging and
  require them at archive build time, with a drift test covering every
  npm files entry (ci-bot R1-1)
- default the payload TERM under --clearenv (R1-4), gate the streaming
  decoder flush on text mode (R1-7), log post-promote settle chain
  failures (R1-25), and pin the caller-signal/pipe-transport contract
  (R1-15), the caller-owned stdin snapshot (R1-31) and both settle-timing
  directions (R1-30) in tests

* fix(core): scope sandbox evidence retention to post-exec uncertainty

Address @wenshao's blocking verification finding on PR #12067: keying
retention on status.state === 'unconfirmed' retained the control/scratch
dir pair for failures that never reached exec — a missing bubblewrap or
payload binary — leaking one pair per invocation and breaking the PR's
own verifier (33/34 on tip vs 34/34 on the reviewed candidate).

- parseBwrapStatus now reports payloadExitObserved when the status wire
  carries a well-formed bwrap exit-code record (bwrap only emits it once
  the payload is past exec), and the relay attests payloadExitObserved:
  false on spawn/transport failure
- the finalizer retains only when the payload may genuinely have run:
  the receipt attests an exit record, or the receipt file exists but is
  unreadable (relay died mid-payload); a missing receipt means the relay
  died before the spawn call, and an attested no-exec receipt means
  setup failed before exec — both clean up
- sandboxStatusError distinguishes the attested no-exec case with a
  "did not run (setup failed before execution)" message (N4)
- design docs record the retention scoping and the drain-window abort
  nuance (N2); PR body now states the bounded-drain trade-off (N1)

Verified: core 204/204, tsc/eslint clean, and the relay probed against
real processes — missing bwrap and exec failure now produce no-exec
receipts (cleanup), an uncorrelated exit record retains, and the
confirmed path is byte-identical.

* fix(core): keep unknown sandbox receipts from attesting non-execution

Address @wenshao's round-2 findings on PR #12067:

- the no-exec attestation was being manufactured from absence of
  evidence at two sites: the finalizer coerced a receipt without
  payloadExitObserved into an explicit false, and the relay attested
  false for every `failed` cause although only a bwrap spawn error
  proves the payload never ran (a status-stream transport error or a
  16KiB wire overflow can happen after the payload executed). Both now
  keep undefined as unknown: only a genuine spawn error or a wire with
  no bwrap exit-code record attests no-exec.
- retention follows: clean up only on a positive no-exec attestation or
  a missing receipt file (the relay creates it O_EXCL before spawning
  bwrap, so absence proves pre-spawn death); unknown retains. The
  fail-safe direction for a retry-deciding caller is restored — an
  oversized or empty wire with the payload having run now retains and
  reads "may have run" instead of "did not run".
- the no-exec message no longer points only at bubblewrap; it also
  covers a missing payload binary.
- verify.mjs: the mount-setup-failure receipt assertion now expects the
  payloadExitObserved attestation (the one shipped deepEqual on the
  receipt object).
- bwrap-status tests now discriminate all three attestation states
  strictly (absent vs false vs true) instead of applying the same
  coercion the code did.

Verified: core 204/204, tsc/eslint clean, relay probed against real
processes for six receipt shapes — spawn failure and child-pid-only
wire attest false, mismatched exit record attests true, empty and
oversized wires stay unattested, success control confirmed.

* fix(core): harden bwrap execution foundation

Address the remaining execution-foundation review findings with deterministic file-worker framing, separate worker diagnostics, scratch-root admission, CI unit coverage, and stronger Linux acceptance evidence.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(core): Make bwrap verifier portable

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-09-19 13:54:30 +00:00

14 lines
569 B
JavaScript

/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
export { ShellExecutionService } from '../../packages/core/src/services/shellExecutionService.ts';
export {
executeBwrap,
sandboxAsset,
} from '../../packages/core/src/sandbox/bwrap-execution.ts';
export { writeSandboxFile } from '../../packages/core/src/sandbox/file-worker-client.ts';
export { getSandboxFileVersion } from '../../packages/core/src/sandbox/file-version.ts';
export { encodeSandboxWriteRequest } from '../../packages/core/src/sandbox/file-worker-protocol.ts';