Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .agents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,5 @@ Builtin transport: [direct HTTP](notes/implemented/architecture/2026-10-18-nativ
- [Bounded public PyPI readback](notes/proposed/process/2026-10-25-pypi-public-readback.md) · [verification](evidence/verify/2026-10-25-pypi-public-readback.md)

- [Public runtime fixture images](notes/proposed/process/2026-10-25-public-runtime-fixture-images.md) · [verification](evidence/verify/2026-10-25-public-runtime-fixture-images.md)

- [Prepared-template readiness](notes/proposed/feature/2026-10-25-prepared-template-readiness.md) · [verification](evidence/verify/2026-10-25-prepared-template-readiness.md).
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"public_a2": {
"packages": {
"agent-sandbox-client": "0.4.0a2",
"agent-sandbox-server": "0.4.0a2"
},
"created": 1,
"cleanup_confirmed": 1,
"first_ready_exit": 1,
"first_ready_seconds": 0.524,
"first_ready_connection_refused": true,
"acknowledged_process_running_at_first_probe": true,
"same_owned_port_later_ready": true,
"later_health_seconds_after_first_probe": 5.364,
"diagnostic_probe_count": 7,
"same_pid_running_after_health": true,
"unchanged_failed_result_returned_to_launcher": true,
"launch_error_reason": "readiness-command-failed",
"launch_error_phase": "before-invoke",
"result": "CONFIRMED_BUG",
"script_sha256": "d9e7a27d76e953210f0f7fda27fded8592e1ff9e3fc40984f405fb8533708bf4"
},
"candidate": {
"packages": {
"agent-sandbox-client": "0.4.0a3",
"agent-sandbox-server": "0.4.0a3"
},
"runtime_source_sha256": "ddf984968e5f2dcdf280616196e4a4c62e2175b0e524e0cfb79b73b1b88efcb3",
"cases": [
{
"mode": "delayed-ready",
"ready_exits": [
1,
1,
1,
1,
1,
0
],
"same_owned_server_pid_alive": true,
"independent_typed_absence": true,
"result": "PASS"
},
{
"mode": "deadline",
"ready_exits": [
1
],
"typed_deadline_failure": true,
"probe_to_failure_seconds": 1.174,
"independent_typed_absence": true,
"result": "PASS"
},
{
"mode": "cancel",
"ready_exits": [
1
],
"cancelled": true,
"independent_typed_absence": true,
"result": "PASS"
}
],
"created": 3,
"cleanup_confirmed": 3,
"result": "PASS",
"script_sha256": "e59c64622435cc7cdd5e28dc8ce37b0fe809145188c16db6d8bcf4923bcf3ccc"
},
"limits": "Public a2 causal reproduction and candidate editable installation are separate. This is not whole-catalog or public a3 acceptance."
}
106 changes: 106 additions & 0 deletions .agents/evidence/verify/2026-10-25-prepared-template-readiness-t4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
{
"run_url": "https://github.com/Alibaba-Agent-Sandbox/awesome-providers/actions/runs/40208641347",
"run_id": 40208641347,
"provider_head": "b78ea200ba7ccb30cd868ae609cad9c7b5031e31",
"job_id": 121214919187,
"conclusion": "success",
"steps": [
{
"name": "Set up job",
"status": "completed",
"conclusion": "success"
},
{
"name": "Run actions/checkout@v4",
"status": "completed",
"conclusion": "success"
},
{
"name": "Run actions/setup-node@v4",
"status": "completed",
"conclusion": "success"
},
{
"name": "Run actions/setup-python@v5",
"status": "completed",
"conclusion": "success"
},
{
"name": "Fetch reviewed host sources",
"status": "completed",
"conclusion": "success"
},
{
"name": "Install locked CLI and matching SDK packages",
"status": "completed",
"conclusion": "success"
},
{
"name": "Project parser with actual host staging and pinned official SDK",
"status": "completed",
"conclusion": "success"
},
{
"name": "Verify SDK fixture and CLI share an isolated home",
"status": "completed",
"conclusion": "success"
},
{
"name": "Start real Docker-backed OpenSandbox",
"status": "completed",
"conclusion": "success"
},
{
"name": "Run direct plugin, installed contract kit and environment tests",
"status": "completed",
"conclusion": "success"
},
{
"name": "Run Python SDK scenarios with streamed diagnostics",
"status": "completed",
"conclusion": "success"
},
{
"name": "Run CLI lifecycle, commands and files",
"status": "completed",
"conclusion": "success"
},
{
"name": "Run native project build, registry push and automatic launch",
"status": "completed",
"conclusion": "success"
},
{
"name": "Cleanup and verify no sandbox containers remain",
"status": "completed",
"conclusion": "success"
},
{
"name": "Post Run actions/setup-python@v5",
"status": "completed",
"conclusion": "success"
},
{
"name": "Post Run actions/setup-node@v4",
"status": "completed",
"conclusion": "success"
},
{
"name": "Post Run actions/checkout@v4",
"status": "completed",
"conclusion": "success"
},
{
"name": "Complete job",
"status": "completed",
"conclusion": "success"
}
],
"candidate_sdk_source": "2ad2a482571bc030a7be298217cf7956bd164da4",
"template_source": "26d6e24e8b29b6c73b364d954dc0279d22a6f9d5",
"cli_source": "6dfdf9897ad76bf2fdb7eb1ccf455252b2dcb9fb",
"node_source": "63d7cd8ece221b9f968eef51eeb932ea334c90d3",
"host_install": "exact candidate source pair 0.4.0a3, not public PyPI",
"guest_server_version": "0.4.0a2",
"scope": "full existing OpenSandbox T4 default/portable/proxy, CLI/Python/Node application calls and final no-owned-container check"
}
68 changes: 68 additions & 0 deletions .agents/evidence/verify/2026-10-25-prepared-template-readiness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Prepared-template readiness verification

Date: 2026-10-25
Result: PARTIAL

Scope: prepared-reference Python `ready_cmd` polling, four regressions, bilingual
launch documentation and the lockstep 0.4.0a3 version/dependency metadata. Base is
main cdf807a4a40079e7d215c0d299b061f0c895a8cb. No provider implementation changes.

## Original failure and cause

OpenSandbox run [40206364834](https://github.com/Alibaba-Agent-Sandbox/awesome-providers/actions/runs/40206364834)
failed in the public a2 Python prepared launch with readiness-command-failed.
Default source CLI build/push/launch, named application command, direct/proxy
authentication probes and independent Node proxy call had passed. Final runner
cleanup passed. This was not a failed image build or push.

Installed public a2 was reproduced on an actual Express `base` reference. A stdlib
HTTP server delayed its listener five seconds. Its first ready probe returned exit 1
(connection refused) after 0.524s. The exact owned server PID was alive before and
after the same port became healthy 5.364s later, inside the 15s budget. A diagnostic
observer returned the original failed result unchanged; a2 still rejected launch.
Independent typed NotFound confirmed cleanup. An earlier diagnostic incorrectly
assumed process-list membership for a child PID and failed its own assertion; its
sandbox was also confirmed absent. Both original receipts remain preserved.

## Executed checks

Python 3.11.15, new isolated editable candidate pair 0.4.0a3 (not a public package),
no source-path injection into public a2. Candidate runtime bytes and sanitized
cloud results are in the adjacent JSON receipt.

- New regression baseline against a2: 3 FAIL, 1 PASS (cancellation already cleaned).
- Focused `pytest tests/test_api/test_template_launch.py -q`: PASS, 25 tests.
- `make lint typecheck`: PASS for both packages.
- `make hygiene`: PASS.
- `make format-check build dist-check`: PASS; all four distributions built,
both wheels installed together in a clean environment, imports and pip check
passed. The built client wheel's runtime bytes match the cloud-tested source.
- Full client suite: 2,275 PASS, 26 explicit skips.
- `make test` server suite: 877 PASS, 3 FAIL. Two raw-HTTP tests observed 501/502
instead of expected 400, matching the earlier local-environment failures. The
package-list test initially lacked pytest because the new uv environment had
no pip. Installing pip in that environment and rerunning that test passed.
The original failures remain recorded; no assertion or fixture was weakened.
- Final repository health after record insertion: 106 PASS, 1 WARN, 0 FAIL.
- Actual candidate cloud: delayed listener initially returned five nonzero probes,
then exit 0 and successful launch; exact server PID remained alive. Permanent
unready launch used a 1s budget and raised the existing phase error after 1.174s
including cleanup. Cancellation after the first probe preserved cleanup.
All 3 owned sandboxes were independently typed NotFound; no recovery was needed.
- Independent read-only review of runtime/tests: no blocking finding. Budget is
per ready-command phase, not a new whole-launch deadline.

The full existing OpenSandbox T4 passed in
[run 40208641347](https://github.com/Alibaba-Agent-Sandbox/awesome-providers/actions/runs/40208641347),
provider source b78ea200ba7ccb30cd868ae609cad9c7b5031e31 and exact SDK candidate
2ad2a482571bc030a7be298217cf7956bd164da4. All job steps succeeded, including
default/portable/proxy launch, CLI and Python application calls, independent Node
proxy call and final owned-container absence. Adjacent JSON records exact inputs.
This is a candidate host pair; the actual guest server remained public a2.
The final follow-up changes only this evidence, not the tested runtime bytes.

Latest-head remote CI, final review,
normal merge and public a3 release/readback remain pending. There is no Docker on
this local host. No public a3 or whole-catalog acceptance is claimed.

Issue: https://github.com/Alibaba-Agent-Sandbox/python-sdk/issues/121
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Decision: Wait for prepared-template readiness within its existing budget

Status: proposed

## Problem

Public a2 acknowledges the started process, then runs `ready_cmd` once. A valid
application can still be initializing, so an early nonzero health probe fails
launch before `readiness_timeout` expires. OpenSandbox T4 observed this after
successful default CLI build, push, launch and application calls.

## Decision

Poll nonzero readiness results inside one monotonic `readiness_timeout` budget.
Constrain each command await by its remaining budget and preserve the provider's
positive integer command timeout. Keep the existing failure reason/phase and owned
cleanup, including cancellation. Do not replay creation or startup. Prepare the
immutable lockstep client/server 0.4.0a3 pair; a2 artifacts remain unchanged.

## API Design

No parameters, launch modes, provider declarations or states are added. This budget
covers `ready_cmd` probes; existing startup-acknowledgement and per-service health
budgets are unchanged. Invalid results and transport errors still fail immediately.

## Alternatives considered

Adding a fixed sleep to the provider test would hide the product race. Retrying the
entire create/launch could duplicate resources. A new readiness framework or global
launch-budget redesign is unnecessary for this defect.

## Dependencies

- https://github.com/Alibaba-Agent-Sandbox/awesome-providers/pull/40
- Existing public a2 and CLI readiness semantics; no provider implementation change.

## Test Strategy

Regress delayed success after early failure, bounded permanent failure and hanging
probes, cancellation and owned cleanup. Compare installed public a2 with the exact
candidate against a real prepared sandbox. Run owner checks and complete real
OpenSandbox T4 on the candidate; public a3 installation/readback follows publication.

## Acceptance criteria

Focused/full tests, owner CI, real readiness scenarios and provider T4 pass with
independent owned cleanup. Normal review/merge and lockstep publication/readback
complete before claiming public delivery. Preserve failed and historical receipts.

[Verification](../../../evidence/verify/2026-10-25-prepared-template-readiness.md).

Issue: https://github.com/Alibaba-Agent-Sandbox/python-sdk/issues/121
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).

### Fixed

- Wait for prepared-template readiness inside one bounded `readiness_timeout` instead of failing after the first early nonzero probe; preserve timeout/cancellation cleanup. Prepare lockstep client/server 0.4.0a3 without changing published a2 artifacts. / 修复模板进程启动后首次就绪探测过早失败的问题,在单一预算内等待就绪,并保留超时和取消时的资源清理;准备同版本双包 0.4.0a3,不覆盖公开 a2。

- Build both SDK offline fixtures from the exact public a2 server on official PyPI, remove unreachable local-wheel fallback, and verify actual guest HTTP behavior and cleanup in CI. / SDK 两个离线夹具统一从官方 PyPI 安装精确 a2 服务端,删除不可达的本地 wheel 回退,并通过 CI 验证真实镜像 HTTP 行为及清理。

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion client-sdk/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ dependencies = [
"tomli>=2.0; python_version < '3.11'",
# The client uses the matching local server runtime for its server/declarative helpers.
# Keep the required distribution version equal to ``_version.py``.
"agent-sandbox-server==0.4.0a2",
"agent-sandbox-server==0.4.0a3",
]

[project.optional-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion client-sdk/src/agent_sandbox/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.4.0a2"
__version__ = "0.4.0a3"
26 changes: 21 additions & 5 deletions client-sdk/src/agent_sandbox/api/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import asyncio
import copy
import json
import math
import os
import re
from pathlib import Path
Expand Down Expand Up @@ -297,11 +298,26 @@ async def launch_template(
await _dispatch_start(sandbox, snapshot["start_cmd"], readiness_timeout)
journal.append("started", sandbox_id=sandbox.id)
if snapshot.get("ready_cmd"):
result = await sandbox.commands.run(
snapshot["ready_cmd"], timeout=int(readiness_timeout)
)
if not isinstance(result, ProcessResult) or result.exit_code != 0:
raise TemplateLaunchError("readiness-command-failed", "before-invoke")
loop = asyncio.get_running_loop()
deadline = loop.time() + readiness_timeout
while True:
remaining = deadline - loop.time()
if remaining <= 0:
raise TemplateLaunchError("readiness-command-failed", "before-invoke")
try:
result = await asyncio.wait_for(
sandbox.commands.run(
snapshot["ready_cmd"], timeout=max(1, math.ceil(remaining))
),
timeout=remaining,
)
except asyncio.TimeoutError:
raise TemplateLaunchError("readiness-command-failed", "before-invoke") from None
if not isinstance(result, ProcessResult):
raise TemplateLaunchError("readiness-command-failed", "before-invoke")
if result.exit_code == 0:
break
await asyncio.sleep(min(0.25, max(0, deadline - loop.time())))
for name, service in plan["services"].items():
client = await sandbox.application_client(service["port"], token=token)
clients[name] = client
Expand Down
Loading
Loading