@junghanacs/entwurf 0.13.0 → 0.13.1
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/AGENTS.md +5 -3
- package/BASELINE.md +57 -203
- package/CHANGELOG.md +19 -0
- package/CONTRIBUTING.md +1 -1
- package/DELIVERY.md +117 -261
- package/README.md +68 -408
- package/VERIFY.md +58 -87
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +231 -0
- package/docs/external-mcp-host.md +132 -0
- package/docs/fresh-cut-policy.md +99 -0
- package/docs/setup-clean-host.md +123 -328
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +21 -48
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/acp-client.js +12 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +11 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/config.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/engraving.js +42 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +4 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +3 -3
- package/mcp/entwurf-bridge/src/index.ts +21 -48
- package/package.json +12 -9
- package/pi-extensions/entwurf-control.ts +20 -49
- package/pi-extensions/lib/acp/acp-client.ts +22 -0
- package/pi-extensions/lib/acp/augment.ts +42 -3
- package/pi-extensions/lib/acp/backend-adapter.ts +12 -11
- package/pi-extensions/lib/acp/backend.ts +305 -36
- package/pi-extensions/lib/acp/config.ts +1 -1
- package/pi-extensions/lib/acp/engraving.ts +43 -1
- package/pi-extensions/lib/acp/event-mapper.ts +16 -13
- package/pi-extensions/lib/acp/models.ts +4 -4
- package/pi-extensions/lib/acp/overlay.ts +3 -3
- package/prompts/engraving.md +13 -5
- package/run.sh +244 -108
- package/scripts/check-acp-carrier-augment.ts +246 -10
- package/scripts/check-acp-cortex.ts +5 -5
- package/scripts/check-acp-prompt-lifecycle.ts +565 -0
- package/scripts/check-acp-sdk-surface.ts +60 -21
- package/scripts/check-acp-stop-reason.ts +342 -0
- package/scripts/check-entwurf-v2-surface.ts +41 -0
- package/scripts/check-gate-qualification.ts +6 -3
- package/scripts/check-probe-ordering.ts +39 -7
- package/scripts/check-release-gate-outcomes.ts +332 -0
- package/scripts/lib/live-skip.ts +33 -0
- package/scripts/lib/probe-acp-turn.ts +33 -12
- package/scripts/lib/step-outcome.sh +88 -0
- package/scripts/mutants/acp-augment.json +106 -0
- package/scripts/mutants/acp-cortex.json +2 -2
- package/scripts/mutants/acp-prompt-lifecycle.json +100 -0
- package/scripts/mutants/acp-stop-reason.json +80 -0
- package/scripts/mutants/probe-ordering.json +27 -5
- package/scripts/mutants/release-gate.json +105 -0
- package/scripts/mutants/v2-surface.json +26 -15
- package/scripts/smoke-acp-bundled-mcp-live.ts +4 -3
- package/scripts/smoke-acp-carrier-augment-live.ts +2 -2
- package/scripts/smoke-acp-cortex-live.ts +8 -14
- package/scripts/smoke-acp-long-turn-live.ts +185 -0
- package/scripts/smoke-acp-mcp-live.ts +2 -2
- package/scripts/smoke-acp-memory-containment-live.ts +2 -2
- package/scripts/smoke-acp-ordering-probe-live.ts +2 -2
- package/scripts/smoke-acp-overlay-live.ts +2 -2
- package/scripts/smoke-acp-provider-live.ts +2 -2
- package/scripts/smoke-acp-raw-turn-live.ts +3 -3
- package/scripts/smoke-acp-session-reuse-live.ts +2 -2
- package/scripts/smoke-acp-skill-live.ts +2 -2
- package/scripts/smoke-acp-socket-citizen-live.ts +4 -3
- package/scripts/smoke-acp-v2-send-live.ts +4 -3
- package/scripts/smoke-agy-native-push-live.ts +4 -3
- package/scripts/smoke-claude-native-resume-live.sh +13 -3
- package/scripts/smoke-entwurf-chain-live.ts +352 -0
- package/scripts/smoke-entwurf-v2-matrix-live.ts +2 -2
- package/scripts/smoke-entwurf-v2-spawn-resume-live.ts +2 -4
- package/scripts/smoke-meta-install-state.sh +4 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// 0.13.1 — LIVE acceptance for the prompt-lifecycle contract. OUT of `pnpm check`.
|
|
2
|
+
//
|
|
3
|
+
// LIVE=1 ./run.sh smoke-acp-long-turn-live
|
|
4
|
+
//
|
|
5
|
+
// THE CLAIM THIS PAYS FOR. `check-acp-prompt-lifecycle` proves against fakes
|
|
6
|
+
// that a running prompt has no wall clock, and `check-probe-ordering` proves the
|
|
7
|
+
// source carries none. Neither can prove the whole stack — pi's runner, its
|
|
8
|
+
// retry policy, the real adapter, and a real model — lets a turn run past the
|
|
9
|
+
// number we deleted. This smoke does exactly that and nothing else: it drives
|
|
10
|
+
// ONE real turn whose tool work is deliberately longer than the retired 600s
|
|
11
|
+
// cutoff, and requires it to finish as ONE turn.
|
|
12
|
+
//
|
|
13
|
+
// Two facts are asserted together, because the old defect was their PRODUCT:
|
|
14
|
+
//
|
|
15
|
+
// survives the reply carries the nonce and the turn took LONGER than the
|
|
16
|
+
// retired cutoff — a fixed wall clock would have killed it.
|
|
17
|
+
// not replayed
|
|
18
|
+
// the persisted transcript contains exactly ONE bootstrap notice
|
|
19
|
+
// ("[acp: preparing claude session]"). The old failure mode
|
|
20
|
+
// replayed the same full prompt from a COLD ACP session up to
|
|
21
|
+
// `retry.maxRetries` times, so every replay re-announced that
|
|
22
|
+
// bootstrap. One occurrence is the direct evidence that the answer
|
|
23
|
+
// came from the original prompt.
|
|
24
|
+
//
|
|
25
|
+
// The work is split into several tool calls under the backend's own per-command
|
|
26
|
+
// tool cap rather than one huge wait, which is also the realistic shape of the
|
|
27
|
+
// turns that were being killed (continuous tool activity, no output for minutes).
|
|
28
|
+
//
|
|
29
|
+
// WHY THE WORK IS NOT `sleep` (measured 2026-07-30, first LIVE attempt). Asking
|
|
30
|
+
// for `sleep 240` produced a 22.9s turn with no nonce: the Claude backend's own
|
|
31
|
+
// Bash tool refuses a FOREGROUND `sleep` ("Foreground `sleep` is blocked; use
|
|
32
|
+
// Monitor with an until-loop"), so the model detached it, reported "Command
|
|
33
|
+
// running in background", said it would wait, and ended the turn. The tool work
|
|
34
|
+
// never happened, so nothing about the wall clock was measured. The blocking
|
|
35
|
+
// command below waits the same wall time without invoking `sleep`, which is the
|
|
36
|
+
// only reason the shape changed — the claim is unchanged.
|
|
37
|
+
|
|
38
|
+
import { strict as assert } from "node:assert";
|
|
39
|
+
import { spawnSync } from "node:child_process";
|
|
40
|
+
import { mkdtempSync, readdirSync, readFileSync, rmSync } from "node:fs";
|
|
41
|
+
import { tmpdir } from "node:os";
|
|
42
|
+
import { dirname, join, resolve } from "node:path";
|
|
43
|
+
import { fileURLToPath } from "node:url";
|
|
44
|
+
import { skipLive } from "./lib/live-skip.ts";
|
|
45
|
+
|
|
46
|
+
const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
47
|
+
const MODEL = process.env.ENTWURF_ACP_LONG_TURN_MODEL?.trim() || "claude-sonnet-5";
|
|
48
|
+
const PROVIDER = "entwurf";
|
|
49
|
+
|
|
50
|
+
/** The cutoff this cut removed. The turn must outlive it. */
|
|
51
|
+
const RETIRED_CUTOFF_MS = 600_000;
|
|
52
|
+
/** Seconds per tool call — each stays under the backend's own per-command cap. */
|
|
53
|
+
const SLEEP_SECONDS = Number(process.env.ENTWURF_ACP_LONG_TURN_SLEEP_SECONDS) || 240;
|
|
54
|
+
/** How many sequential waits to ask for (3 × 240s = 720s > the retired cutoff). */
|
|
55
|
+
const SLEEP_ROUNDS = Number(process.env.ENTWURF_ACP_LONG_TURN_ROUNDS) || 3;
|
|
56
|
+
/** Harness bound for the WHOLE smoke — an observation horizon, not a turn contract. */
|
|
57
|
+
const SMOKE_HORIZON_MS = Number(process.env.ENTWURF_ACP_LONG_TURN_HORIZON_MS) || 2_400_000;
|
|
58
|
+
|
|
59
|
+
function fail(msg: string): never {
|
|
60
|
+
console.error(`[smoke-acp-long-turn-live] FAIL: ${msg}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (process.env.LIVE !== "1") {
|
|
65
|
+
skipLive("smoke-acp-long-turn-live", "set LIVE=1 to run the real long ACP turn.");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const nonce = `${process.pid.toString(36)}${Date.now().toString(36)}`;
|
|
69
|
+
const expected = `LONGTURN_OK_${nonce}`;
|
|
70
|
+
const sessionId = `long-turn-${nonce}`;
|
|
71
|
+
|
|
72
|
+
const scratch = mkdtempSync(join(tmpdir(), "entwurf-long-turn-"));
|
|
73
|
+
const sessionDir = mkdtempSync(join(tmpdir(), "entwurf-long-turn-sessions-"));
|
|
74
|
+
const startedAt = Date.now();
|
|
75
|
+
try {
|
|
76
|
+
// Blocks for SLEEP_SECONDS in the FOREGROUND without invoking `sleep` (see the
|
|
77
|
+
// header): select() with only a timeout is a plain wait, and the echo gives
|
|
78
|
+
// each round a visible completion marker in the transcript.
|
|
79
|
+
const waitCommand = `python3 -c "import select; select.select([], [], [], ${SLEEP_SECONDS})" && echo ROUND_COMPLETE`;
|
|
80
|
+
|
|
81
|
+
const prompt =
|
|
82
|
+
`Run exactly ${SLEEP_ROUNDS} shell commands with your shell tool, one at a time, waiting for each to print ` +
|
|
83
|
+
`ROUND_COMPLETE before starting the next. Each command is exactly: ${waitCommand}\n\n` +
|
|
84
|
+
`Each one blocks for about ${SLEEP_SECONDS} seconds, so give the shell tool a per-command limit of at least ` +
|
|
85
|
+
`${(SLEEP_SECONDS + 60) * 1000} ms. Run every command in the FOREGROUND and wait for its output — do not ` +
|
|
86
|
+
"detach it, do not run it in the background, do not poll for it, do not shorten it, and do not run them in " +
|
|
87
|
+
`parallel. When all ${SLEEP_ROUNDS} have printed ROUND_COMPLETE, reply with exactly ${expected} and nothing else.`;
|
|
88
|
+
|
|
89
|
+
const args = [
|
|
90
|
+
"--no-extensions",
|
|
91
|
+
"-e",
|
|
92
|
+
REPO_ROOT,
|
|
93
|
+
"--mode",
|
|
94
|
+
"text",
|
|
95
|
+
"--session-id",
|
|
96
|
+
sessionId,
|
|
97
|
+
"--session-dir",
|
|
98
|
+
sessionDir,
|
|
99
|
+
"-p",
|
|
100
|
+
"--approve",
|
|
101
|
+
"--provider",
|
|
102
|
+
PROVIDER,
|
|
103
|
+
"--model",
|
|
104
|
+
MODEL,
|
|
105
|
+
prompt,
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
console.error(`[smoke-acp-long-turn-live] repo: ${REPO_ROOT}`);
|
|
109
|
+
console.error(`[smoke-acp-long-turn-live] cwd: ${scratch}`);
|
|
110
|
+
console.error(`[smoke-acp-long-turn-live] model: ${PROVIDER}/${MODEL}`);
|
|
111
|
+
console.error(`[smoke-acp-long-turn-live] work: ${SLEEP_ROUNDS} × ${SLEEP_SECONDS}s foreground wait`);
|
|
112
|
+
console.error(`[smoke-acp-long-turn-live] expects: ${expected} after > ${RETIRED_CUTOFF_MS}ms`);
|
|
113
|
+
|
|
114
|
+
const res = spawnSync("pi", args, { cwd: scratch, encoding: "utf8", timeout: SMOKE_HORIZON_MS, env: process.env });
|
|
115
|
+
const elapsed = Date.now() - startedAt;
|
|
116
|
+
|
|
117
|
+
const stdout = res.stdout ?? "";
|
|
118
|
+
const stderr = res.stderr ?? "";
|
|
119
|
+
const combined = `${stdout}\n${stderr}`;
|
|
120
|
+
const tail = combined.split("\n").slice(-30).join("\n");
|
|
121
|
+
|
|
122
|
+
if (res.error) {
|
|
123
|
+
console.error(`[smoke-acp-long-turn-live] output tail:\n${tail}`);
|
|
124
|
+
fail(`pi spawn failed after ${elapsed}ms: ${res.error.message}`);
|
|
125
|
+
}
|
|
126
|
+
if (res.status !== 0) {
|
|
127
|
+
console.error(`[smoke-acp-long-turn-live] output tail:\n${tail}`);
|
|
128
|
+
fail(`pi exited ${res.status} (signal=${res.signal ?? "none"}) after ${elapsed}ms`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
assert.ok(
|
|
132
|
+
stdout.includes(expected),
|
|
133
|
+
`the long turn did not deliver its nonce ${expected} after ${elapsed}ms (stdout tail: ${JSON.stringify(stdout.slice(-400))})`,
|
|
134
|
+
);
|
|
135
|
+
assert.ok(
|
|
136
|
+
elapsed > RETIRED_CUTOFF_MS,
|
|
137
|
+
`the turn finished in ${elapsed}ms — under the retired ${RETIRED_CUTOFF_MS}ms cutoff, so it proves nothing. ` +
|
|
138
|
+
"Raise ENTWURF_ACP_LONG_TURN_ROUNDS / _SLEEP_SECONDS until the tool work outlasts the cutoff.",
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// No cold replay: exactly one bootstrap in the persisted transcript.
|
|
142
|
+
const jsonl = readdirSync(sessionDir, { recursive: true })
|
|
143
|
+
.filter((f): f is string => typeof f === "string" && f.endsWith(".jsonl"))
|
|
144
|
+
.map((f) => readFileSync(join(sessionDir, f), "utf8"))
|
|
145
|
+
.join("\n");
|
|
146
|
+
assert.ok(jsonl.length > 0, `no session JSONL under ${sessionDir} — cannot judge replay`);
|
|
147
|
+
const bootstraps = jsonl.split("[acp: preparing claude session]").length - 1;
|
|
148
|
+
assert.equal(
|
|
149
|
+
bootstraps,
|
|
150
|
+
1,
|
|
151
|
+
`the transcript shows ${bootstraps} cold ACP bootstraps — a long turn must be answered by its ORIGINAL prompt, ` +
|
|
152
|
+
"not replayed from a fresh session (that replay is what turned one long turn into four)",
|
|
153
|
+
);
|
|
154
|
+
// The DEFECT's own signatures, not the word "timeout" anywhere: the model
|
|
155
|
+
// narrates its shell tool's per-command limit in this transcript, so a bare
|
|
156
|
+
// /timeout/i would fail an otherwise perfect run on the assistant's own prose.
|
|
157
|
+
// These three strings are what the old pairing actually emitted — the retired
|
|
158
|
+
// cutoff's message, and pi's retry-exhaustion wording.
|
|
159
|
+
const defectMarkers = [
|
|
160
|
+
/prompt timed out after/i,
|
|
161
|
+
/Aborted after \d+ retry attempts?/i,
|
|
162
|
+
/\bretry attempt \d+/i,
|
|
163
|
+
].filter((re) => re.test(combined));
|
|
164
|
+
assert.equal(
|
|
165
|
+
defectMarkers.length,
|
|
166
|
+
0,
|
|
167
|
+
`the run reported the retired cutoff or a pi retry — neither may happen to a healthy long turn ` +
|
|
168
|
+
`(matched ${defectMarkers.join(", ")}; tail: ${JSON.stringify(tail)})`,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
console.log("[smoke-acp-long-turn-live] PASS — a real ACP turn outlived the retired wall clock and answered once");
|
|
172
|
+
console.log(` model: ${PROVIDER}/${MODEL}`);
|
|
173
|
+
console.log(` elapsed: ${elapsed}ms (> retired cutoff ${RETIRED_CUTOFF_MS}ms)`);
|
|
174
|
+
console.log(` nonce: ${expected} present in the assistant reply`);
|
|
175
|
+
console.log(" replay: exactly 1 cold ACP bootstrap in the persisted transcript");
|
|
176
|
+
console.log(" retry: no retry / timeout reported anywhere in the run");
|
|
177
|
+
} finally {
|
|
178
|
+
for (const d of [scratch, sessionDir]) {
|
|
179
|
+
try {
|
|
180
|
+
rmSync(d, { recursive: true, force: true });
|
|
181
|
+
} catch {
|
|
182
|
+
// best-effort cleanup
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -26,6 +26,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
|
26
26
|
import { tmpdir } from "node:os";
|
|
27
27
|
import { dirname, join, resolve } from "node:path";
|
|
28
28
|
import { fileURLToPath } from "node:url";
|
|
29
|
+
import { skipLive } from "./lib/live-skip.ts";
|
|
29
30
|
|
|
30
31
|
const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
31
32
|
const PROBE_SERVER = join(REPO_ROOT, "scripts", "fixtures", "probe-mcp-server.ts");
|
|
@@ -39,8 +40,7 @@ function fail(msg: string): never {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
if (process.env.LIVE !== "1") {
|
|
42
|
-
|
|
43
|
-
process.exit(0);
|
|
43
|
+
skipLive("smoke-acp-mcp-live", "set LIVE=1 to run the real pi provider turn.");
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
const STUB_PATTERN = /AcpBackendNotImplementedError|not implemented in S0/i;
|
|
@@ -68,6 +68,7 @@ import {
|
|
|
68
68
|
DEFAULT_CLAUDE_TOOLS,
|
|
69
69
|
} from "../pi-extensions/lib/acp/tool-surface.ts";
|
|
70
70
|
import { terminateChild } from "./lib/acp-child-cleanup.ts";
|
|
71
|
+
import { skipLive } from "./lib/live-skip.ts";
|
|
71
72
|
|
|
72
73
|
const REQUESTED_MODEL_ID = process.env.ENTWURF_ACP_MEMORY_MODEL ?? "claude-sonnet-5";
|
|
73
74
|
const ALLOW_PATH_FALLBACK = process.env.ENTWURF_ACP_MEMORY_ALLOW_PATH_FALLBACK === "1";
|
|
@@ -109,8 +110,7 @@ async function listMemoryFiles(dir: string): Promise<string[]> {
|
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
if (process.env.LIVE !== "1") {
|
|
112
|
-
|
|
113
|
-
process.exit(0);
|
|
113
|
+
skipLive("smoke-acp-memory-containment-live", "set LIVE=1 to run the real memory-containment turn.");
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
function resolveLaunch(): { command: string; args: string[]; source: string; acceptance: boolean } {
|
|
@@ -50,6 +50,7 @@ import { type AcpClientHandlers, connectAcpClient } from "../pi-extensions/lib/a
|
|
|
50
50
|
import type { AcpBackendAdapter } from "../pi-extensions/lib/acp/backend-adapter.ts";
|
|
51
51
|
import type { ResolvedAcpConfig } from "../pi-extensions/lib/acp/config.ts";
|
|
52
52
|
import { terminateChild } from "./lib/acp-child-cleanup.ts";
|
|
53
|
+
import { skipLive } from "./lib/live-skip.ts";
|
|
53
54
|
import { driveProbeTurn, type ProbeMcpEnricher, ProbePhaseError } from "./lib/probe-acp-turn.ts";
|
|
54
55
|
import {
|
|
55
56
|
AMBIENT_OVERRIDE_ENV,
|
|
@@ -103,8 +104,7 @@ function fail(msg: string): never {
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
if (process.env.LIVE !== "1") {
|
|
106
|
-
|
|
107
|
-
process.exit(0);
|
|
107
|
+
skipLive("smoke-acp-ordering-probe-live", "set LIVE=1 to run the real paired probe.");
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
for (const [label, d] of [
|
|
@@ -55,6 +55,7 @@ import {
|
|
|
55
55
|
DEFAULT_CLAUDE_TOOLS,
|
|
56
56
|
} from "../pi-extensions/lib/acp/tool-surface.ts";
|
|
57
57
|
import { terminateChild } from "./lib/acp-child-cleanup.ts";
|
|
58
|
+
import { skipLive } from "./lib/live-skip.ts";
|
|
58
59
|
|
|
59
60
|
const REQUESTED_MODEL_ID = process.env.ENTWURF_ACP_OVERLAY_MODEL ?? "claude-sonnet-5";
|
|
60
61
|
const ALLOW_PATH_FALLBACK = process.env.ENTWURF_ACP_OVERLAY_ALLOW_PATH_FALLBACK === "1";
|
|
@@ -76,8 +77,7 @@ function withTimeout<T>(label: string, p: Promise<T>, ms: number): Promise<T> {
|
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
if (process.env.LIVE !== "1") {
|
|
79
|
-
|
|
80
|
-
process.exit(0);
|
|
80
|
+
skipLive("smoke-acp-overlay-live", "set LIVE=1 to run the real overlay ACP turn.");
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
function resolveLaunch(): { command: string; args: string[]; source: string; acceptance: boolean } {
|
|
@@ -26,6 +26,7 @@ import { mkdtempSync, readdirSync, readFileSync, rmSync } from "node:fs";
|
|
|
26
26
|
import { tmpdir } from "node:os";
|
|
27
27
|
import { dirname, join, resolve } from "node:path";
|
|
28
28
|
import { fileURLToPath } from "node:url";
|
|
29
|
+
import { skipLive } from "./lib/live-skip.ts";
|
|
29
30
|
|
|
30
31
|
const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
31
32
|
const MODEL = process.env.ENTWURF_ACP_PROVIDER_MODEL?.trim() || "claude-sonnet-5";
|
|
@@ -38,8 +39,7 @@ function fail(msg: string): never {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
if (process.env.LIVE !== "1") {
|
|
41
|
-
|
|
42
|
-
process.exit(0);
|
|
42
|
+
skipLive("smoke-acp-provider-live", "set LIVE=1 to run the real pi provider turn.");
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// A nonce makes the expected reply unique so a stray "OK" elsewhere in the
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// LIVE=1 ./run.sh smoke-acp-raw-turn-live
|
|
4
4
|
//
|
|
5
5
|
// What this proves (and ONLY this): the pinned Claude ACP adapter
|
|
6
|
-
// (@agentclientprotocol/claude-agent-acp@0.
|
|
6
|
+
// (@agentclientprotocol/claude-agent-acp@0.64.0) spawns, speaks the ACP wire
|
|
7
7
|
// protocol over stdio NDJSON, and returns one real model turn. It is the
|
|
8
8
|
// bytes-flow proof that the S2a dep surface is not just installable but
|
|
9
9
|
// actually drivable — before any provider/overlay/streamSimple code (S2b+).
|
|
@@ -34,6 +34,7 @@ import { Readable, Writable } from "node:stream";
|
|
|
34
34
|
import { ndJsonStream, PROTOCOL_VERSION } from "@agentclientprotocol/sdk";
|
|
35
35
|
import { connectAcpClient } from "../pi-extensions/lib/acp/acp-client.ts";
|
|
36
36
|
import { terminateChild } from "./lib/acp-child-cleanup.ts";
|
|
37
|
+
import { skipLive } from "./lib/live-skip.ts";
|
|
37
38
|
|
|
38
39
|
const REQUESTED_MODEL_ID = process.env.ENTWURF_ACP_RAW_TURN_MODEL ?? "claude-sonnet-5";
|
|
39
40
|
const ALLOW_PATH_FALLBACK = process.env.ENTWURF_ACP_RAW_TURN_ALLOW_PATH_FALLBACK === "1";
|
|
@@ -59,8 +60,7 @@ function withTimeout<T>(label: string, p: Promise<T>, ms: number): Promise<T> {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
if (process.env.LIVE !== "1") {
|
|
62
|
-
|
|
63
|
-
process.exit(0);
|
|
63
|
+
skipLive("smoke-acp-raw-turn-live", "set LIVE=1 to run the real ACP turn.");
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// ---------------------------------------------------------------------------
|
|
@@ -25,6 +25,7 @@ import { tmpdir } from "node:os";
|
|
|
25
25
|
import { dirname, resolve } from "node:path";
|
|
26
26
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
27
27
|
import type { Api, AssistantMessageEvent, Context, Model } from "@earendil-works/pi-ai";
|
|
28
|
+
import { skipLive } from "./lib/live-skip.ts";
|
|
28
29
|
|
|
29
30
|
const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
30
31
|
const MODEL = process.env.ENTWURF_ACP_PROVIDER_MODEL?.trim() || "claude-sonnet-5";
|
|
@@ -46,8 +47,7 @@ function withTimeout<T>(label: string, p: Promise<T>, ms: number): Promise<T> {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
if (process.env.LIVE !== "1") {
|
|
49
|
-
|
|
50
|
-
process.exit(0);
|
|
50
|
+
skipLive("smoke-acp-session-reuse-live", "set LIVE=1 to run the real 2-turn reuse acceptance.");
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
type Stream = AsyncIterable<AssistantMessageEvent> & {
|
|
@@ -28,6 +28,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
|
28
28
|
import { tmpdir } from "node:os";
|
|
29
29
|
import { dirname, join, resolve } from "node:path";
|
|
30
30
|
import { fileURLToPath } from "node:url";
|
|
31
|
+
import { skipLive } from "./lib/live-skip.ts";
|
|
31
32
|
|
|
32
33
|
const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
33
34
|
const MODEL = process.env.ENTWURF_ACP_PROVIDER_MODEL?.trim() || "claude-sonnet-5";
|
|
@@ -40,8 +41,7 @@ function fail(msg: string): never {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
if (process.env.LIVE !== "1") {
|
|
43
|
-
|
|
44
|
-
process.exit(0);
|
|
44
|
+
skipLive("smoke-acp-skill-live", "set LIVE=1 to run the real pi provider turn.");
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const STUB_PATTERN = /AcpBackendNotImplementedError|not implemented in S0/i;
|
|
@@ -31,6 +31,7 @@ import { fileURLToPath } from "node:url";
|
|
|
31
31
|
import { fetchControlSocketRuntimeInfo, formatRuntimeModel } from "../pi-extensions/lib/entwurf-control-rpc.ts";
|
|
32
32
|
import { scanSocketProbes } from "../pi-extensions/lib/socket-discovery.ts";
|
|
33
33
|
import { terminateChild } from "./lib/acp-child-cleanup.ts";
|
|
34
|
+
import { skipLive } from "./lib/live-skip.ts";
|
|
34
35
|
import { waitForPiRecord } from "./lib/pi-record-discovery.ts";
|
|
35
36
|
|
|
36
37
|
const ACP_PROVIDER = "entwurf";
|
|
@@ -76,10 +77,10 @@ async function waitForGone(sockPath: string, timeoutMs: number): Promise<boolean
|
|
|
76
77
|
|
|
77
78
|
async function main(): Promise<void> {
|
|
78
79
|
if (process.env.LIVE !== "1") {
|
|
79
|
-
|
|
80
|
-
"
|
|
80
|
+
skipLive(
|
|
81
|
+
"smoke-acp-socket-citizen-live",
|
|
82
|
+
"set LIVE=1 to run (spawns a real pi --entwurf-control + opens a real socket).",
|
|
81
83
|
);
|
|
82
|
-
return;
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
const tmp = os.tmpdir();
|
|
@@ -81,6 +81,7 @@ import * as readline from "node:readline";
|
|
|
81
81
|
import { fileURLToPath } from "node:url";
|
|
82
82
|
import { upsertMetaSession, writeMetaReceiverMarker } from "../pi-extensions/lib/meta-session.ts";
|
|
83
83
|
import { terminateChild } from "./lib/acp-child-cleanup.ts";
|
|
84
|
+
import { skipLive } from "./lib/live-skip.ts";
|
|
84
85
|
import { waitForPiRecord } from "./lib/pi-record-discovery.ts";
|
|
85
86
|
|
|
86
87
|
const ACP_PROVIDER = "entwurf";
|
|
@@ -196,10 +197,10 @@ function driveTurn(child: ChildProcess, prompt: string): Promise<TurnCapture> {
|
|
|
196
197
|
|
|
197
198
|
async function main(): Promise<void> {
|
|
198
199
|
if (process.env.LIVE !== "1") {
|
|
199
|
-
|
|
200
|
-
"
|
|
200
|
+
skipLive(
|
|
201
|
+
"smoke-acp-v2-send-live",
|
|
202
|
+
"set LIVE=1 to run (spawns a real pi --entwurf-control resident + drives one model turn).",
|
|
201
203
|
);
|
|
202
|
-
return;
|
|
203
204
|
}
|
|
204
205
|
|
|
205
206
|
// One temp root holding the whole garden this smoke can see: store, mailbox and
|
|
@@ -54,6 +54,7 @@ import { runEntwurfV2 } from "../pi-extensions/lib/entwurf-v2-runner.ts";
|
|
|
54
54
|
import { upsertMetaSession } from "../pi-extensions/lib/meta-session.ts";
|
|
55
55
|
import { antigravityAdapter } from "../pi-extensions/lib/native-push/adapter.ts";
|
|
56
56
|
import { registerNativeConversation } from "../pi-extensions/lib/native-push/register.ts";
|
|
57
|
+
import { skipLive } from "./lib/live-skip.ts";
|
|
57
58
|
|
|
58
59
|
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
59
60
|
|
|
@@ -79,10 +80,10 @@ function smokeSender(gardenId: string, cwd: string): SenderEnvelope {
|
|
|
79
80
|
|
|
80
81
|
async function main(): Promise<void> {
|
|
81
82
|
if (process.env.LIVE !== "1") {
|
|
82
|
-
|
|
83
|
-
"
|
|
83
|
+
skipLive(
|
|
84
|
+
"smoke-agy-native-push-live",
|
|
85
|
+
"set LIVE=1 (+ AGY_CONVERSATION_ID) to run (sends into a real agy conversation).",
|
|
84
86
|
);
|
|
85
|
-
return;
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
// ── doctor-static preflight (runs FIRST; a dangling agy config FAILs here — the ③ gate) ──
|
|
@@ -18,11 +18,21 @@ set -euo pipefail
|
|
|
18
18
|
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
19
19
|
|
|
20
20
|
if [ "${LIVE:-0}" != "1" ]; then
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
. "$REPO/scripts/lib/step-outcome.sh"
|
|
22
|
+
echo "[entwurf:skip] smoke-claude-native-resume-live — set LIVE=1 to run (spawns two real Claude Code native turns)."
|
|
23
|
+
exit "$ENTWURF_STEP_SKIP_EXIT"
|
|
23
24
|
fi
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
# A missing `claude` is a PREREQUISITE, not a defect: this smoke drives the real
|
|
27
|
+
# native binary, so a host without it has nothing to prove wrong. Under the P1
|
|
28
|
+
# outcome protocol that is a SKIP the aggregate reports and `--cut` refuses —
|
|
29
|
+
# reporting it as FAIL would make a host's own toolchain look like a release
|
|
30
|
+
# defect, and the old exit 1 did exactly that once this smoke joined the gate.
|
|
31
|
+
command -v claude >/dev/null || {
|
|
32
|
+
. "$REPO/scripts/lib/step-outcome.sh"
|
|
33
|
+
echo "[entwurf:skip] smoke-claude-native-resume-live — claude not on PATH (install Claude Code and complete its own login flow)."
|
|
34
|
+
exit "$ENTWURF_STEP_SKIP_EXIT"
|
|
35
|
+
}
|
|
26
36
|
command -v node >/dev/null || { echo "FAIL: node not on PATH" >&2; exit 1; }
|
|
27
37
|
command -v python3 >/dev/null || { echo "FAIL: python3 not on PATH" >&2; exit 1; }
|
|
28
38
|
|