@junghanacs/entwurf 0.25.1 → 0.30.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 +1 -1
- package/BASELINE.md +1 -1
- package/CHANGELOG.md +190 -0
- package/DELIVERY.md +1 -1
- package/README.md +183 -674
- package/VERIFY.md +21 -17
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +115 -18
- package/docs/external-mcp-host.md +1 -1
- package/docs/setup-clean-host.md +346 -10
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +27 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +55 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-mailbox.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +3 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +3 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +13 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +3 -3
- package/mcp/entwurf-bridge/src/index.ts +30 -3
- package/package.json +13 -13
- package/pi-extensions/entwurf-control.ts +50 -637
- package/pi-extensions/lib/acp/backend-adapter.ts +81 -1
- package/pi-extensions/lib/acp/backend.ts +41 -5
- package/pi-extensions/lib/entwurf-fact-provider.ts +2 -2
- package/pi-extensions/lib/entwurf-mcp-server.ts +74 -0
- package/pi-extensions/lib/entwurf-peers-render.ts +1 -1
- package/pi-extensions/lib/entwurf-v2-decider.ts +2 -2
- package/pi-extensions/lib/entwurf-v2-mailbox.ts +2 -2
- package/pi-extensions/lib/entwurf-v2-production.ts +3 -3
- package/pi-extensions/lib/entwurf-v2-surface.ts +3 -5
- package/pi-extensions/lib/entwurf-v2-visible-resume.ts +3 -3
- package/pi-extensions/lib/fresh-call-composition.ts +13 -6
- package/pi-extensions/lib/meta-mailbox-body.ts +1 -1
- package/pi-extensions/lib/mux-fresh-call.ts +3 -3
- package/run.sh +177 -53
- package/scripts/check-entwurf-peers-surface.ts +8 -15
- package/scripts/check-entwurf-self-address.ts +7 -11
- package/scripts/check-entwurf-v2-surface.ts +20 -112
- package/scripts/check-fresh-call-dispatch.ts +12 -13
- package/scripts/check-gate-qualification.ts +250 -5
- package/scripts/check-herdr-plugin-build.ts +159 -57
- package/scripts/check-install-surface.ts +3 -2
- package/scripts/check-mux-resume-call.ts +24 -35
- package/scripts/check-pi-bridge-sender.ts +130 -0
- package/scripts/check-pi-mcp-bridge.ts +251 -0
- package/scripts/fixtures/herdr-supply.json +8 -8
- package/scripts/lib/codex-fresh-live-protocol.ts +17 -5
- package/scripts/lib/codex-fresh-source-receipts.ts +21 -2
- package/scripts/lib/mutation-qualify.ts +269 -44
- package/scripts/lib/pi-mcp-bridge-probe.mjs +170 -0
- package/scripts/meta-bridge-doctor.sh +16 -5
- package/scripts/mutants/acp-foreground-only.json +17 -0
- package/scripts/mutants/acp-prompt-lifecycle.json +2 -2
- package/scripts/mutants/acp-vendor-rejection.json +62 -0
- package/scripts/mutants/codex-native.json +11 -11
- package/scripts/mutants/control-send-receipt.json +6 -4
- package/scripts/mutants/copilot-birth.json +2 -1
- package/scripts/mutants/fresh-call-dispatch.json +3 -14
- package/scripts/mutants/gate-qualification.json +59 -0
- package/scripts/mutants/herdr-plugin-build.json +11 -0
- package/scripts/mutants/herdr-supply.json +3 -3
- package/scripts/mutants/mux-fresh-call.json +43 -45
- package/scripts/mutants/mux-resume-call.json +11 -35
- package/scripts/mutants/pack-install.json +15 -4
- package/scripts/mutants/pi-bridge-sender.json +54 -0
- package/scripts/mutants/pi-floor.json +6 -2
- package/scripts/mutants/pi-mcp-bridge.json +36 -0
- package/scripts/mutants/pi-mcp-register.json +51 -0
- package/scripts/mutants/v2-surface.json +6 -6
- package/scripts/resident-rpc-drive.ts +5 -1
- package/scripts/smoke-acp-bundled-mcp-live.ts +3 -1
- package/scripts/smoke-acp-cortex-live.ts +3 -1
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-acp-session-reuse-live.ts +29 -12
- package/scripts/smoke-acp-socket-citizen-live.ts +3 -1
- package/scripts/smoke-acp-v2-send-live.ts +3 -1
- package/scripts/smoke-codex-fresh-live.ts +20 -1
- package/scripts/smoke-entwurf-chain-live.ts +6 -2
- package/scripts/smoke-entwurf-v2-matrix-live.ts +3 -1
- package/scripts/smoke-herdr-fresh-call-live.ts +21 -7
- package/scripts/smoke-herdr-raw-install-live.sh +1 -1
- package/scripts/smoke-mux-lifecycle-live.ts +1 -1
- package/scripts/smoke-resident-garden-guard.sh +3 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"lane": "pi-mcp-bridge",
|
|
4
|
+
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "PI-MCP-BRIDGE-DECLARED-SURFACE",
|
|
7
|
+
"title": "entwurf_self joins the hidden list, so a pi model is no longer declared the verb that tells it who it is",
|
|
8
|
+
"subject": "pi-extensions/lib/entwurf-mcp-server.ts",
|
|
9
|
+
"find": [
|
|
10
|
+
"export const ENTWURF_MCP_HIDDEN_TOOLS = [\"entwurf_inbox_read\", \"entwurf_register_native\"] as const;"
|
|
11
|
+
],
|
|
12
|
+
"replace": [
|
|
13
|
+
"export const ENTWURF_MCP_HIDDEN_TOOLS = [\"entwurf_inbox_read\", \"entwurf_register_native\", \"entwurf_self\"] as const;"
|
|
14
|
+
],
|
|
15
|
+
"gate": ["bash", "run.sh", "check-pi-mcp-bridge"],
|
|
16
|
+
"timeoutSeconds": 120,
|
|
17
|
+
"signature": "[QK:PI-MCP-BRIDGE-DECLARED-SURFACE]",
|
|
18
|
+
"signatureSource": "scripts/check-pi-mcp-bridge.ts",
|
|
19
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"claim": "PI-MCP-BRIDGE-SAME-CHILD-IDENTITY",
|
|
23
|
+
"title": "the explicit carrier is built from the model label when one is known, so the spawned child claims an identity no record holds",
|
|
24
|
+
"subject": "pi-extensions/lib/entwurf-mcp-server.ts",
|
|
25
|
+
"find": ["\t\tenv: { PI_SESSION_ID: input.gardenId, ...(input.agentId ? { PI_AGENT_ID: input.agentId } : {}) },"],
|
|
26
|
+
"replace": [
|
|
27
|
+
"\t\tenv: { PI_SESSION_ID: input.agentId ?? input.gardenId, ...(input.agentId ? { PI_AGENT_ID: input.agentId } : {}) },"
|
|
28
|
+
],
|
|
29
|
+
"gate": ["bash", "run.sh", "check-pi-mcp-bridge"],
|
|
30
|
+
"timeoutSeconds": 120,
|
|
31
|
+
"signature": "[QK:PI-MCP-BRIDGE-SAME-CHILD-IDENTITY]",
|
|
32
|
+
"signatureSource": "scripts/check-pi-mcp-bridge.ts",
|
|
33
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"lane": "pi-mcp-register",
|
|
4
|
+
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "PI-MCP-REGISTER-AFTER-BIRTH",
|
|
7
|
+
"title": "the extension factory registers the bridge at load, before any record exists — the server is on Pi's registry with no born identity to carry",
|
|
8
|
+
"subject": "pi-extensions/entwurf-control.ts",
|
|
9
|
+
"find": ["\tpi.registerMessageRenderer(SESSION_MESSAGE_TYPE, renderSessionMessage);"],
|
|
10
|
+
"replace": [
|
|
11
|
+
"\tpi.registerMessageRenderer(SESSION_MESSAGE_TYPE, renderSessionMessage);",
|
|
12
|
+
"\tpi.registerMcpServer(ENTWURF_MCP_SERVER_NAME, { command: process.execPath, args: [] });"
|
|
13
|
+
],
|
|
14
|
+
"gate": ["bash", "run.sh", "check-pi-mcp-register"],
|
|
15
|
+
"timeoutSeconds": 60,
|
|
16
|
+
"signature": "[QK:PI-MCP-REGISTER-AFTER-BIRTH]",
|
|
17
|
+
"signatureSource": "pi-extensions/entwurf-control.test.ts",
|
|
18
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"claim": "PI-MCP-IDENTITY-BEFORE-UI",
|
|
22
|
+
"title": "the status line is rendered before the registration, so a UI failure leaves a born citizen without its verbs",
|
|
23
|
+
"subject": "pi-extensions/entwurf-control.ts",
|
|
24
|
+
"find": [
|
|
25
|
+
"\t\tregisterEntwurfMcpServer(pi, ctx, birth.gardenId);",
|
|
26
|
+
"\t\tupdateStatus(ctx, true, birth.gardenId);"
|
|
27
|
+
],
|
|
28
|
+
"replace": [
|
|
29
|
+
"\t\tupdateStatus(ctx, true, birth.gardenId);",
|
|
30
|
+
"\t\tregisterEntwurfMcpServer(pi, ctx, birth.gardenId);"
|
|
31
|
+
],
|
|
32
|
+
"gate": ["bash", "run.sh", "check-pi-mcp-register"],
|
|
33
|
+
"timeoutSeconds": 60,
|
|
34
|
+
"signature": "[QK:PI-MCP-IDENTITY-BEFORE-UI]",
|
|
35
|
+
"signatureSource": "pi-extensions/entwurf-control.test.ts",
|
|
36
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"claim": "PI-MCP-EXPOSURE",
|
|
40
|
+
"title": "the verbs are registered behind codemode instead of declared directly, so the model is handed a different surface than every other host",
|
|
41
|
+
"subject": "pi-extensions/lib/entwurf-mcp-server.ts",
|
|
42
|
+
"find": ["\t\texposure: \"direct\","],
|
|
43
|
+
"replace": ["\t\texposure: \"codemode\","],
|
|
44
|
+
"gate": ["bash", "run.sh", "check-pi-mcp-register"],
|
|
45
|
+
"timeoutSeconds": 60,
|
|
46
|
+
"signature": "[QK:PI-MCP-EXPOSURE]",
|
|
47
|
+
"signatureSource": "pi-extensions/entwurf-control.test.ts",
|
|
48
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
@@ -39,20 +39,20 @@
|
|
|
39
39
|
"signatureSource": "scripts/check-entwurf-v2-production.ts"
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
|
-
"claim": "V2SURF-
|
|
43
|
-
"title": "
|
|
44
|
-
"subject": "
|
|
42
|
+
"claim": "V2SURF-MODE-SCOPE",
|
|
43
|
+
"title": "the bridge mode param (the one every caller reads, pi included since #125) reverts to \"Delivery mode for a live send\" (false for native-push) (#120 P2: the description was rewritten to stop claiming an interrupt, so the mutation now strips the rail scope from the corrected sentence)",
|
|
44
|
+
"subject": "mcp/entwurf-bridge/src/index.ts",
|
|
45
45
|
"find": [
|
|
46
|
-
"\t\t\t\t\
|
|
46
|
+
"\t\t\t\t\"Injection style for a CONTROL-SOCKET send only: steer (ask the receiver's steering queue) or follow_up (ask its follow-up queue). NEITHER is an interrupt: a busy receiver drains steering after each turn and follow-ups only when its inner loop ends, so a later steer overtakes every earlier follow_up and there is no order between the two. Both queues are volatile process memory — an abort drops what is in them. The receipt names which queue accepted the message (`queued-steer` / `queued-follow-up`); an idle receiver answers `sent`, meaning the turn was TRIGGERED, not that the model has seen the text. \" +",
|
|
47
47
|
"\t\t\t\t\t\"The mailbox and native-push plans carry no mode, so it has no effect on those rails.\","
|
|
48
48
|
],
|
|
49
49
|
"replace": [
|
|
50
|
-
"\t\t\t\t\
|
|
50
|
+
"\t\t\t\t\"Delivery mode for a live send: steer (ask the steering queue) or follow_up (ask the follow-up queue). \" +",
|
|
51
51
|
"\t\t\t\t\t\"Both queues are volatile process memory.\","
|
|
52
52
|
],
|
|
53
53
|
"gate": ["bash", "run.sh", "check-entwurf-v2-surface"],
|
|
54
54
|
"timeoutSeconds": 60,
|
|
55
|
-
"signature": "[QK:V2SURF-
|
|
55
|
+
"signature": "[QK:V2SURF-MODE-SCOPE]",
|
|
56
56
|
"signatureSource": "scripts/check-entwurf-v2-surface.ts"
|
|
57
57
|
},
|
|
58
58
|
{
|
|
@@ -169,7 +169,11 @@ const result: DriveResult = {
|
|
|
169
169
|
timedOut: false,
|
|
170
170
|
};
|
|
171
171
|
|
|
172
|
-
|
|
172
|
+
// `--no-extensions` keeps ambient discovery out, and since Pi 0.99 it also turns the BUILT-IN
|
|
173
|
+
// extensions off: a citizen born here had no MCP, so its entwurf-bridge registration threw
|
|
174
|
+
// `register_mcp_server` (`[측정 2026-10-02]` pi 0.99.2 and 1.0.0 alike). `-e builtin:mcp` restores
|
|
175
|
+
// that one built-in — the MCP a resident has — and nothing else.
|
|
176
|
+
const piArgs = ["--no-extensions", "-e", "builtin:mcp", "-e", REPO_ROOT, "--entwurf-control"];
|
|
173
177
|
// NO --session-id. That injection is what the #50 C2 cut removed: pi mints its own
|
|
174
178
|
// id and the record mints the address. A resume names the FILE, never an id.
|
|
175
179
|
if (sessionFile) piArgs.push("--session", sessionFile);
|
|
@@ -62,7 +62,9 @@ const REAL_CONTROL_DIR = path.join(os.homedir(), ".pi", "entwurf-control");
|
|
|
62
62
|
const SOCKET_SUFFIX = ".sock";
|
|
63
63
|
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
64
64
|
// Load ONLY this checkout's extensions so the resident registers THIS acp-provider.ts.
|
|
65
|
-
|
|
65
|
+
// `builtin:mcp`: `--no-extensions` also turns off Pi's built-in MCP, which a citizen's entwurf-bridge
|
|
66
|
+
// registration needs (scripts/resident-rpc-drive.ts has the measurement).
|
|
67
|
+
const REPO_EXTENSION_ARGS = ["--no-extensions", "-e", "builtin:mcp", "-e", REPO_ROOT] as const;
|
|
66
68
|
|
|
67
69
|
const BOOT_TIMEOUT_MS = PI_BOOT_TIMEOUT_MS; // shared: pi lock-stale window + boot (see pi-record-discovery)
|
|
68
70
|
const TURN_TIMEOUT_MS = Number(process.env.ENTWURF_ACP_PROVIDER_TIMEOUT_MS) || 240_000;
|
|
@@ -68,7 +68,9 @@ const CORTEX_OVERLAYS_ROOT = path.join(os.homedir(), ".pi", "agent", "cortex-ove
|
|
|
68
68
|
const SOCKET_SUFFIX = ".sock";
|
|
69
69
|
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
70
70
|
// Load ONLY this checkout's extensions so the resident registers THIS acp-provider.ts.
|
|
71
|
-
|
|
71
|
+
// `builtin:mcp`: `--no-extensions` also turns off Pi's built-in MCP, which a citizen's entwurf-bridge
|
|
72
|
+
// registration needs (scripts/resident-rpc-drive.ts has the measurement).
|
|
73
|
+
const REPO_EXTENSION_ARGS = ["--no-extensions", "-e", "builtin:mcp", "-e", REPO_ROOT] as const;
|
|
72
74
|
|
|
73
75
|
const BOOT_TIMEOUT_MS = PI_BOOT_TIMEOUT_MS; // shared: pi lock-stale window + boot (see pi-record-discovery)
|
|
74
76
|
// The cortex CLI self-extracts on first launch and its newSession alone is
|
|
@@ -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
|
|
6
|
+
// (@agentclientprotocol/claude-agent-acp, exact pin in package.json) 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+).
|
|
@@ -3,13 +3,20 @@
|
|
|
3
3
|
// LIVE=1 ./run.sh smoke-acp-session-reuse-live
|
|
4
4
|
//
|
|
5
5
|
// The deterministic gate (check-acp-session-reuse) proves the prompt SCOPE
|
|
6
|
-
// (delta-only) and the wiring with a fake seam. This
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// (
|
|
12
|
-
//
|
|
6
|
+
// (delta-only) and the wiring with a fake seam. This drives the REAL backend
|
|
7
|
+
// (real overlay + spawn + vendor) through TWO ACP turns in one process-scoped
|
|
8
|
+
// process and asserts that turn 2 took the REUSE path.
|
|
9
|
+
//
|
|
10
|
+
// The reuse verdict is the production lifecycle notice, NOT the recall. A
|
|
11
|
+
// respawned (`new`) turn re-sends the FULL transcript (context.ts
|
|
12
|
+
// buildAcpPrompt "new"), and turn 2's context below carries turn 1's codeword, so
|
|
13
|
+
// a respawn-per-turn backend would recall it just as well — recall alone cannot
|
|
14
|
+
// tell reuse from rebuild. Turn 2 must therefore announce `[acp: reusing live
|
|
15
|
+
// session]` and must not announce `[acp: preparing …]`; check-acp-session-reuse
|
|
16
|
+
// pins that the reuse branch emits exactly that and the new branch the opposite.
|
|
17
|
+
// The recall is kept as a supporting check that the reused session still answers
|
|
18
|
+
// from its own history. Delta-only on the wire stays the deterministic gate's
|
|
19
|
+
// claim: the vendor stdio is not observed here.
|
|
13
20
|
//
|
|
14
21
|
// It forces process-scoped by pushing the real `--entwurf-control` resident flag
|
|
15
22
|
// into argv (resolveLifecyclePolicy reads process.argv), then calls the REAL
|
|
@@ -175,18 +182,28 @@ async function main(): Promise<void> {
|
|
|
175
182
|
if (!r2.done) fail("turn 2 did not complete");
|
|
176
183
|
console.error(`[smoke-acp-session-reuse-live] turn 2 reply: ${JSON.stringify(r2.text.slice(-120))}`);
|
|
177
184
|
|
|
178
|
-
// The
|
|
179
|
-
//
|
|
185
|
+
// The reuse verdict: the backend's own lifecycle notices ride the text stream, so
|
|
186
|
+
// turn 2 names the path it took. A rebuild would announce `preparing` instead.
|
|
187
|
+
assert.ok(
|
|
188
|
+
r2.text.includes("[acp: reusing live session]"),
|
|
189
|
+
`turn 2 did not take the reuse path — no "[acp: reusing live session]" notice (reply: ${JSON.stringify(r2.text.slice(-200))})`,
|
|
190
|
+
);
|
|
191
|
+
assert.ok(
|
|
192
|
+
!r2.text.includes("[acp: preparing"),
|
|
193
|
+
`turn 2 re-bootstrapped a session ("[acp: preparing …]") instead of reusing turn 1's (reply: ${JSON.stringify(r2.text.slice(-200))})`,
|
|
194
|
+
);
|
|
195
|
+
// Supporting only: the reused session still answers from its own history. A rebuild
|
|
196
|
+
// would pass this too (it re-sends the codeword), which is why it is not the verdict.
|
|
180
197
|
assert.ok(
|
|
181
198
|
r2.text.includes(codeword),
|
|
182
|
-
`turn 2 reply did not contain the turn-1 codeword ${codeword} —
|
|
199
|
+
`turn 2 reply did not contain the turn-1 codeword ${codeword} — the reused session lost its history (reply: ${JSON.stringify(r2.text.slice(-200))})`,
|
|
183
200
|
);
|
|
184
201
|
|
|
185
202
|
console.log(
|
|
186
|
-
"[smoke-acp-session-reuse-live] PASS —
|
|
203
|
+
"[smoke-acp-session-reuse-live] PASS — turn 2 took the reuse path ([acp: reusing live session], no re-bootstrap) and recalled the turn-1 codeword",
|
|
187
204
|
);
|
|
188
205
|
console.log(` model: entwurf/${MODEL}`);
|
|
189
|
-
console.log(` codeword: ${codeword} recalled in turn 2 (delta-only)`);
|
|
206
|
+
console.log(` codeword: ${codeword} recalled in turn 2 (supporting; delta-only is check-acp-session-reuse's claim)`);
|
|
190
207
|
}
|
|
191
208
|
|
|
192
209
|
main()
|
|
@@ -41,7 +41,9 @@ const REAL_CONTROL_DIR = path.join(os.homedir(), ".pi", "entwurf-control");
|
|
|
41
41
|
const SOCKET_SUFFIX = ".sock";
|
|
42
42
|
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
43
43
|
// Load ONLY this checkout's extensions so the resident registers THIS acp-provider.ts.
|
|
44
|
-
|
|
44
|
+
// `builtin:mcp`: `--no-extensions` also turns off Pi's built-in MCP, which a citizen's entwurf-bridge
|
|
45
|
+
// registration needs (scripts/resident-rpc-drive.ts has the measurement).
|
|
46
|
+
const REPO_EXTENSION_ARGS = ["--no-extensions", "-e", "builtin:mcp", "-e", REPO_ROOT] as const;
|
|
45
47
|
|
|
46
48
|
const BOOT_TIMEOUT_MS = PI_BOOT_TIMEOUT_MS; // shared: pi lock-stale window + boot (see pi-record-discovery)
|
|
47
49
|
const POLL_MS = 100;
|
|
@@ -95,7 +95,9 @@ const REAL_CONTROL_DIR = path.join(os.homedir(), ".pi", "entwurf-control");
|
|
|
95
95
|
const SOCKET_SUFFIX = ".sock";
|
|
96
96
|
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
97
97
|
// Load ONLY this checkout's extensions so the resident registers THIS acp-provider.ts.
|
|
98
|
-
|
|
98
|
+
// `builtin:mcp`: `--no-extensions` also turns off Pi's built-in MCP, which a citizen's entwurf-bridge
|
|
99
|
+
// registration needs (scripts/resident-rpc-drive.ts has the measurement).
|
|
100
|
+
const REPO_EXTENSION_ARGS = ["--no-extensions", "-e", "builtin:mcp", "-e", REPO_ROOT] as const;
|
|
99
101
|
|
|
100
102
|
const BOOT_TIMEOUT_MS = PI_BOOT_TIMEOUT_MS; // shared: pi lock-stale window + boot (see pi-record-discovery)
|
|
101
103
|
const TURN_TIMEOUT_MS = Number(process.env.ENTWURF_ACP_PROVIDER_TIMEOUT_MS) || 240_000;
|
|
@@ -75,6 +75,7 @@ import {
|
|
|
75
75
|
import {
|
|
76
76
|
buildCodexInstruction,
|
|
77
77
|
buildInitialPiPhaseOne,
|
|
78
|
+
buildInitialPiRelay,
|
|
78
79
|
runCleanupStages,
|
|
79
80
|
selectReport,
|
|
80
81
|
} from "./lib/codex-fresh-live-protocol.ts";
|
|
@@ -954,6 +955,8 @@ async function run(): Promise<void> {
|
|
|
954
955
|
model: piModel,
|
|
955
956
|
cwd: scratch,
|
|
956
957
|
task:
|
|
958
|
+
"Session-info already supplies device and time. Do not recheck time, cwd, or identity with shell or other tools. " +
|
|
959
|
+
"Your FIRST tool call must be mcp__entwurf_bridge__entwurf_callback with no arguments, and nothing before it. " +
|
|
957
960
|
`After your required callback receipt, end the turn and wait passively for one addressed message containing ${initialPiWaitToken}. ` +
|
|
958
961
|
"That message is the only authority to open the Codex sibling. Do not call shell, sleep, terminal, or any tool to wait.",
|
|
959
962
|
});
|
|
@@ -1070,7 +1073,6 @@ async function run(): Promise<void> {
|
|
|
1070
1073
|
codexWaitToken,
|
|
1071
1074
|
codexModel,
|
|
1072
1075
|
scratch,
|
|
1073
|
-
codexInstruction,
|
|
1074
1076
|
});
|
|
1075
1077
|
|
|
1076
1078
|
// The model is asked to operate public tools, but every launch and delivery assertion below
|
|
@@ -1190,6 +1192,23 @@ async function run(): Promise<void> {
|
|
|
1190
1192
|
`requested=${scratch}\npane=${codexPaneCwd}\nrollout-session_meta=${codexRolloutCwd}\n` +
|
|
1191
1193
|
`record=${codexIdentity.cwd}\napp-server=${appServerCwd}`;
|
|
1192
1194
|
|
|
1195
|
+
// Only after the exact callback and record/cwd joins: instruct the initial Pi to relay,
|
|
1196
|
+
// never deliver the Codex payload on its behalf.
|
|
1197
|
+
const relayInitialPi = await bridge.call("entwurf_v2", {
|
|
1198
|
+
target: initialPiGid,
|
|
1199
|
+
intent: "fire-and-forget",
|
|
1200
|
+
message: buildInitialPiRelay({ codexGardenId, codexInstruction }),
|
|
1201
|
+
});
|
|
1202
|
+
receipts["8c-fixture-to-initial-pi-relay"] = relayInitialPi.text;
|
|
1203
|
+
ok(
|
|
1204
|
+
"the fixture addressed the initial Pi relay phase through the control socket",
|
|
1205
|
+
!relayInitialPi.isError &&
|
|
1206
|
+
/entwurf_v2 control-socket → (?:sent|queued-steer|queued-follow-up|accepted-unknown-boundary)/.test(
|
|
1207
|
+
relayInitialPi.text,
|
|
1208
|
+
),
|
|
1209
|
+
relayInitialPi.text,
|
|
1210
|
+
);
|
|
1211
|
+
|
|
1193
1212
|
const initialPiToCodex = await awaitOrRecover("the initial Pi joined native-push receipt", SOURCE_WAIT_MS, () => {
|
|
1194
1213
|
const piSources = piSourceToolReceipts(readInitialPiEntries());
|
|
1195
1214
|
assertSourceCallScopes(piSources, "entwurf_v2", "target", [callerGid, codexGardenId], "initial Pi v2 roles");
|
|
@@ -67,7 +67,9 @@ import { terminateChild } from "./lib/acp-child-cleanup.ts";
|
|
|
67
67
|
import { skipLive } from "./lib/live-skip.ts";
|
|
68
68
|
|
|
69
69
|
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
70
|
-
|
|
70
|
+
// `builtin:mcp`: `--no-extensions` also turns off Pi's built-in MCP, which a citizen's entwurf-bridge
|
|
71
|
+
// registration needs (scripts/resident-rpc-drive.ts has the measurement).
|
|
72
|
+
const REPO_EXTENSION_ARGS = ["--no-extensions", "-e", "builtin:mcp", "-e", REPO_ROOT] as const;
|
|
71
73
|
const REAL_CONTROL_DIR = path.join(os.homedir(), ".pi", "entwurf-control");
|
|
72
74
|
const SOCKET_SUFFIX = ".sock";
|
|
73
75
|
|
|
@@ -329,9 +331,11 @@ async function main(): Promise<void> {
|
|
|
329
331
|
`Call the tool mcp__entwurf-bridge__entwurf_v2 exactly once with target=${gidD}, intent=fire-and-forget,`,
|
|
330
332
|
`and message=${nonce} terminus. Then reply with only the tool's outcome line.`,
|
|
331
333
|
].join(" ");
|
|
334
|
+
// hop2 is read by B, a NATIVE pi citizen: Pi folds the server key's hyphen, so B is declared
|
|
335
|
+
// `mcp__entwurf_bridge__entwurf_v2`. hop1 (Claude Code) and hop3 (ACP Claude) keep the hyphen.
|
|
332
336
|
const hop2 = [
|
|
333
337
|
`${nonce} hop2. You are the second hop of a delivery chain.`,
|
|
334
|
-
`Call the tool
|
|
338
|
+
`Call the tool mcp__entwurf_bridge__entwurf_v2 exactly once with target=${gidC}, intent=fire-and-forget,`,
|
|
335
339
|
"and message set to exactly the text between INNER-BEGIN and INNER-END.",
|
|
336
340
|
"Then reply with only the tool's outcome line.",
|
|
337
341
|
"INNER-BEGIN",
|
|
@@ -76,7 +76,9 @@ const SOCKET_SUFFIX = ".sock";
|
|
|
76
76
|
// Release-gate topology: repo-under-test, not deployment smoke. Load only this
|
|
77
77
|
// checkout's extension so resident behavior is independent of global pi packages.
|
|
78
78
|
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
79
|
-
|
|
79
|
+
// `builtin:mcp`: `--no-extensions` also turns off Pi's built-in MCP, which a citizen's entwurf-bridge
|
|
80
|
+
// registration needs (scripts/resident-rpc-drive.ts has the measurement).
|
|
81
|
+
const REPO_EXTENSION_ARGS = ["--no-extensions", "-e", "builtin:mcp", "-e", REPO_ROOT] as const;
|
|
80
82
|
|
|
81
83
|
// Staged timeouts (automation): short and per-stage so a stall is attributable.
|
|
82
84
|
//
|
|
@@ -196,10 +196,9 @@ function transcriptRecords(file: string): string[] {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
|
-
* The
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* writes `"type":"toolCall","name":"entwurf_callback"`; neither records
|
|
199
|
+
* The verb names as the CLAUDE MCP activity log spells them — NOT the model-facing dialect.
|
|
200
|
+
* `[측정 2026-09-20, this smoke's own red fixture]` that log carries `Calling MCP tool:
|
|
201
|
+
* entwurf_callback` / `Tool 'entwurf_callback' completed`, never
|
|
203
202
|
* `mcp__entwurf-bridge__entwurf_callback`. The per-host dialect is owned by
|
|
204
203
|
* `FRESH_CALL_CALLBACK_TOOL` / `FRESH_CALL_DELIVERY_TOOL` in `fresh-call-composition.ts` and is
|
|
205
204
|
* what the FRAMING says; these two are what an OBSERVER sees, and conflating them is what this
|
|
@@ -208,6 +207,17 @@ function transcriptRecords(file: string): string[] {
|
|
|
208
207
|
const CALLBACK_VERB = "entwurf_callback";
|
|
209
208
|
const DELIVERY_VERB = "entwurf_v2";
|
|
210
209
|
|
|
210
|
+
/**
|
|
211
|
+
* The callback as a Pi 1.0 TRANSCRIPT spells it. Pi's built-in MCP is the tool ingress since
|
|
212
|
+
* 0.30.0, and Pi records the qualified name the model called. The bare `entwurf_callback` this
|
|
213
|
+
* smoke read on 2026-09-20 belonged to the retired native-tool registration; a pi→pi child that
|
|
214
|
+
* called back correctly would then never match, which is a red about the oracle, not the rail.
|
|
215
|
+
* Declared here as the OBSERVED literal, not imported from the compositor, so a change to the
|
|
216
|
+
* production framing cannot quietly move this expectation with it — the same rule as
|
|
217
|
+
* `PI_SOURCE_VERBS` in `lib/codex-fresh-source-receipts.ts`. No prefix strip, no legacy OR.
|
|
218
|
+
*/
|
|
219
|
+
const PI_CALLBACK_TOOL = "mcp__entwurf_bridge__entwurf_callback";
|
|
220
|
+
|
|
211
221
|
/**
|
|
212
222
|
* The id of the CALLBACK toolCall in this record, or null. Pi writes a call and its result as two
|
|
213
223
|
* records; this is one half of the join that replaces a same-record read.
|
|
@@ -238,7 +248,9 @@ function entwurfCallIdFor(record: string, nonce: string, callerGid: string): str
|
|
|
238
248
|
if (!Array.isArray(content)) return null;
|
|
239
249
|
for (const part of content) {
|
|
240
250
|
const call = part as { type?: unknown; name?: unknown; id?: unknown; arguments?: unknown };
|
|
241
|
-
if (call.type !== "toolCall"
|
|
251
|
+
if (call.type !== "toolCall") continue;
|
|
252
|
+
if (call.name === CALLBACK_VERB) throw new Error(`${LABEL}: retired raw Pi tool name ${CALLBACK_VERB}`);
|
|
253
|
+
if (call.name !== PI_CALLBACK_TOOL) continue;
|
|
242
254
|
// Zero-argument verb: absent, null and `{}` are all the shape it is called with. A supplied
|
|
243
255
|
// target or message is a model naming an address the env already owns — not this act.
|
|
244
256
|
const args = call.arguments as { message?: unknown; target?: unknown } | undefined | null;
|
|
@@ -444,8 +456,10 @@ async function main(): Promise<void> {
|
|
|
444
456
|
childBackend: "pi",
|
|
445
457
|
callerKind: "pi",
|
|
446
458
|
childKind: "pi",
|
|
447
|
-
|
|
448
|
-
|
|
459
|
+
// The approved roster's lighter Pi GPT tier (agent-config MODELS.md, 2026-09-30); gpt-5.6-sol
|
|
460
|
+
// is still in the catalog but no longer on the roster. A LIVE cost choice, not a product set.
|
|
461
|
+
callerModel: "openai-codex/gpt-6-luna",
|
|
462
|
+
childModel: "openai-codex/gpt-6-luna",
|
|
449
463
|
},
|
|
450
464
|
{
|
|
451
465
|
label: "claude→claude",
|
|
@@ -491,7 +491,7 @@ for (const name of ["entwurf", "entwurf-bridge", "entwurf-statusline"]) {
|
|
|
491
491
|
|
|
492
492
|
// The REAL subcommand, from the installed bin, under node_modules — which is the branch of
|
|
493
493
|
// start.sh that runs the prebuilt dist. Its own oracle is an EXACT set (`EXPECT_TOOLS` in run.sh check-bridge), so a
|
|
494
|
-
// zero exit here is the artifact listing all
|
|
494
|
+
// zero exit here is the artifact listing all eight garden verbs and no ninth.
|
|
495
495
|
const run = spawnSync(path.join(binDir, "entwurf"), ["check-bridge"], { encoding: "utf8" });
|
|
496
496
|
const said = `${run.stdout ?? ""}${run.stderr ?? ""}`;
|
|
497
497
|
// [a-z0-9_] and not [a-z_]: the first draft of this line printed `entwurf_v` for entwurf_v2
|
|
@@ -108,7 +108,7 @@ const BRIDGE_LAUNCHER = path.join(REPO, "mcp", "entwurf-bridge", "start.sh");
|
|
|
108
108
|
|
|
109
109
|
/** The two pi shapes. They differ in exactly one thing that matters here — whether the record
|
|
110
110
|
* carries `provider=entwurf`, which is what makes the resume argv re-inject the bridge (#29). */
|
|
111
|
-
const PI_NATIVE_MODEL = "openai-codex/gpt-
|
|
111
|
+
const PI_NATIVE_MODEL = "openai-codex/gpt-6-luna";
|
|
112
112
|
const PI_ACP_MODEL = "entwurf/claude-sonnet-5";
|
|
113
113
|
const CLAUDE_MODEL = "claude-sonnet-5";
|
|
114
114
|
|
|
@@ -91,7 +91,9 @@ TIMEOUT="${SMOKE_RGG_TIMEOUT:-90}"
|
|
|
91
91
|
# Release-gate topology: this is a repo-under-test smoke, not a deployment smoke.
|
|
92
92
|
# Load ONLY this checkout's extension so results do not depend on device-local global
|
|
93
93
|
# packages / current branch wiring.
|
|
94
|
-
|
|
94
|
+
# `builtin:mcp`: `--no-extensions` also turns off Pi's built-in MCP, which a citizen's entwurf-bridge
|
|
95
|
+
# registration needs (scripts/resident-rpc-drive.ts has the measurement).
|
|
96
|
+
REPO_EXTENSION_ARGS=(--no-extensions -e builtin:mcp -e "$REPO")
|
|
95
97
|
|
|
96
98
|
pass=0
|
|
97
99
|
fail=0
|