fix(python-sdk): make runtime readiness explicit

This commit is contained in:
fz
2026-08-18 17:36:59 +08:00
parent ee5280bd6e
commit 7bb766fc82
15 changed files with 113 additions and 38 deletions
+5 -15
View File
@@ -149,6 +149,7 @@ MCP_SERVER_SCRIPT = """\
import json
import os
import sys
import time
log_path = os.environ.get("MCP_SMOKE_LOG")
@@ -179,6 +180,10 @@ for line in sys.stdin:
},
})
elif method == "tools/list":
# Keep discovery pending longer than the old smoke's 100 ms grace
# period. An SDK runtime that answers initialize too early will make
# its first model request without this tool and fail deterministically.
time.sleep(0.25)
send({
"jsonrpc": "2.0",
"id": request_id,
@@ -255,20 +260,6 @@ def mcp_cordis(server_script: Path) -> str:
], indent=2)
def wait_for_mcp_discovery(log_path: Path) -> None:
"""Wait until the external server has answered initial tool discovery."""
deadline = time.monotonic() + 10
while time.monotonic() < deadline:
if log_path.exists() and "tools/list" in log_path.read_text().splitlines():
# The server records the request before flushing its response; give the
# client one scheduler interval to register the returned generation.
time.sleep(0.1)
return
time.sleep(0.025)
observed = log_path.read_text() if log_path.exists() else "<no MCP requests>"
raise AssertionError(f"packaged MCP client did not complete tool discovery: {observed}")
class MockModelHandler(BaseHTTPRequestHandler):
"""Return deterministic text, worker, and orchestration completions."""
@@ -861,7 +852,6 @@ def smoke_sdk_mcp(base_url: str, executable: Path | None) -> None:
base_url=base_url,
request_timeout_seconds=60,
) as harness:
wait_for_mcp_discovery(discovery_log)
result = harness.run(MCP_PROMPT, session_id="mcp-smoke")
assert result.final_response == MCP_TEXT, result.final_response
+17
View File
@@ -99,6 +99,23 @@ describe('verifyRuntimeClosure', () => {
expect(result.failures).toEqual([])
})
it('requires preset plugins to be linked from the workspace', async () => {
const root = fixture({
'python/sdk-runtime/package.json': { name: 'runtime', dependencies: { '@scope/plugin': '1.2.3' } },
'python/sdk-runtime/platforms.json': platforms,
'apps/cli/config/agent-presets/standard/agent.cordis.yml': `
- id: plugin
name: '@scope/plugin'
`,
})
const result = await verifyRuntimeClosure(root)
expect(result.failures).toEqual([
'standard preset -> @scope/plugin [runtime dependency is "1.2.3"; expected workspace:] (linux-arm64, linux-x64, macos-arm64)',
])
})
it('fails when no shipped preset is discovered', async () => {
const root = fixture({
'python/sdk-runtime/package.json': { name: 'runtime', dependencies: {} },
+6 -2
View File
@@ -133,9 +133,13 @@ async function missingPresetPlugins(
for (const target of targets) {
const processPlatform = processPlatformForTarget(target)
for (const plugin of activeBarePluginPackages(document, processPlatform)) {
if (runtimeDependencies[plugin] !== undefined) continue
const version = runtimeDependencies[plugin]
if (version?.startsWith('workspace:') === true) continue
const preset = basename(dirname(presetPath))
const key = `${preset} preset -> ${plugin}`
const declaration = version === undefined
? ''
: ` [runtime dependency is ${JSON.stringify(version)}; expected workspace:]`
const key = `${preset} preset -> ${plugin}${declaration}`
const targets = missing.get(key) ?? new Set<string>()
targets.add(target)
missing.set(key, targets)