@junghanacs/entwurf 0.13.0 → 0.14.0
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 +52 -17
- package/BASELINE.md +60 -206
- package/CHANGELOG.md +64 -0
- package/CONTRIBUTING.md +14 -10
- package/DELIVERY.md +118 -261
- package/README.md +91 -431
- package/VERIFY.md +74 -95
- package/demo/README.md +1 -1
- package/demo/demo-baseline.sh +1 -3
- package/demo/demo.sh +2 -5
- package/docs/acp-backend-rail.md +236 -0
- package/docs/external-mcp-host.md +131 -0
- package/docs/fresh-cut-policy.md +99 -0
- package/docs/setup-clean-host.md +124 -328
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +160 -67
- 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 +11 -9
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +7 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-core.js +13 -14
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-resume-args.js +45 -40
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +117 -95
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +23 -57
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +16 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +5 -53
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-release.js +21 -36
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-runner.js +3 -15
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send-fallback.js +12 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send.js +2 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +30 -67
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +256 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +91 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +258 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-launch.js +202 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-placement.js +289 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +170 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +136 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/session-id.js +8 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/socket-discovery.js +3 -3
- package/mcp/entwurf-bridge/dist/scripts/meta-facts.js +51 -0
- package/mcp/entwurf-bridge/dist/scripts/new-session-id.js +9 -4
- package/mcp/entwurf-bridge/src/index.ts +176 -67
- package/mcp/entwurf-bridge/start.sh +2 -2
- package/mcp/entwurf-bridge/test.sh +23 -9
- package/mcp/entwurf-bridge/tsconfig.build.json +11 -2
- package/package.json +25 -11
- package/pi-extensions/entwurf-control.ts +220 -71
- 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 +366 -38
- 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 +11 -9
- package/pi-extensions/lib/entwurf-control-rpc.ts +7 -5
- package/pi-extensions/lib/entwurf-core.ts +15 -15
- package/pi-extensions/lib/entwurf-resume-args.ts +41 -52
- package/pi-extensions/lib/entwurf-v2-contract-schema.ts +1 -1
- package/pi-extensions/lib/entwurf-v2-contract.ts +120 -99
- package/pi-extensions/lib/entwurf-v2-decider.ts +30 -91
- package/pi-extensions/lib/entwurf-v2-lock.ts +16 -7
- package/pi-extensions/lib/entwurf-v2-production.ts +4 -78
- package/pi-extensions/lib/entwurf-v2-release.ts +25 -49
- package/pi-extensions/lib/entwurf-v2-runner.ts +6 -21
- package/pi-extensions/lib/entwurf-v2-send-fallback.ts +12 -11
- package/pi-extensions/lib/entwurf-v2-send.ts +2 -7
- package/pi-extensions/lib/entwurf-v2-surface.ts +36 -76
- package/pi-extensions/lib/entwurf-v2-visible-resume.ts +370 -0
- package/pi-extensions/lib/meta-session.ts +93 -5
- package/pi-extensions/lib/mux-fresh-call.ts +328 -0
- package/pi-extensions/lib/mux-launch.ts +267 -0
- package/pi-extensions/lib/mux-placement.ts +387 -0
- package/pi-extensions/lib/mux-resume-call.ts +221 -0
- package/pi-extensions/lib/resume-launch-identity.ts +162 -0
- package/pi-extensions/lib/session-id.js +8 -5
- package/pi-extensions/lib/socket-discovery.ts +3 -3
- package/prompts/engraving.md +13 -5
- package/run.sh +688 -282
- package/scripts/agy-bridge-config.py +5 -1
- package/scripts/check-acp-backend-preflight.ts +1 -1
- package/scripts/check-acp-carrier-augment.ts +246 -10
- package/scripts/check-acp-cortex.ts +5 -5
- package/scripts/check-acp-overlay.ts +13 -3
- package/scripts/check-acp-prompt-lifecycle.ts +565 -0
- package/scripts/check-acp-stop-reason.ts +342 -0
- package/scripts/check-acp-stream-hooks.ts +504 -0
- package/scripts/check-elapsed.sh +25 -0
- package/scripts/check-entwurf-bridge-boot.ts +51 -4
- package/scripts/check-entwurf-bridge-pi-free.ts +6 -5
- package/scripts/check-entwurf-control-rpc.ts +4 -3
- package/scripts/check-entwurf-resume-args.ts +72 -70
- package/scripts/check-entwurf-session-identity.ts +14 -10
- package/scripts/check-entwurf-v2-contract.ts +34 -59
- package/scripts/check-entwurf-v2-decider.ts +17 -177
- package/scripts/check-entwurf-v2-lock.ts +5 -2
- package/scripts/check-entwurf-v2-matrix.ts +3 -53
- package/scripts/check-entwurf-v2-production.ts +2 -91
- package/scripts/check-entwurf-v2-release.ts +10 -105
- package/scripts/check-entwurf-v2-runner.ts +4 -85
- package/scripts/check-entwurf-v2-send-fallback.ts +5 -6
- package/scripts/check-entwurf-v2-send.ts +0 -28
- package/scripts/check-entwurf-v2-surface.ts +198 -128
- package/scripts/check-entwurf-v2-visible-resume.ts +445 -0
- package/scripts/check-fresh-cut-gate.sh +1 -1
- package/scripts/check-gate-qualification.ts +101 -7
- package/scripts/check-install-container.sh +10 -2
- package/scripts/check-install-surface.ts +1 -1
- package/scripts/check-keyset-overlap.py +1 -1
- package/scripts/check-meta-facts.ts +249 -0
- package/scripts/check-meta-identity-consumers.ts +1 -1
- package/scripts/check-meta-session.ts +169 -0
- package/scripts/check-mux-launch-tmux.ts +316 -0
- package/scripts/check-mux-launch.ts +288 -0
- package/scripts/check-mux-launcher-fence.ts +264 -0
- package/scripts/check-mux-parent-artifact.ts +195 -0
- package/scripts/check-mux-placement-tmux.ts +322 -0
- package/scripts/check-mux-placement.ts +323 -0
- package/scripts/check-mux-resume-call.ts +282 -0
- package/scripts/check-probe-cli-shim.ts +25 -22
- package/scripts/check-probe-ordering.ts +121 -81
- package/scripts/check-release-gate-outcomes.ts +452 -0
- package/scripts/check-resume-launch-identity.ts +244 -0
- package/scripts/check-socket-discovery.ts +1 -1
- package/scripts/fixtures/mux-parent-transcript.scrubbed.jsonl +3 -0
- package/scripts/inventory-verification-surface.ts +349 -0
- package/scripts/lib/claude-launcher-fence.ts +322 -0
- package/scripts/lib/live-skip.ts +33 -0
- package/scripts/lib/mutation-qualify.ts +109 -3
- package/scripts/lib/probe-acp-turn.ts +33 -12
- package/scripts/lib/step-outcome.sh +88 -0
- package/scripts/meta-bridge-doctor.sh +6 -8
- package/scripts/meta-facts.ts +60 -0
- package/scripts/mutants/acp-augment.json +106 -0
- package/scripts/mutants/acp-cortex.json +2 -2
- package/scripts/mutants/acp-overlay.json +17 -0
- package/scripts/mutants/acp-prompt-lifecycle.json +100 -0
- package/scripts/mutants/acp-stop-reason.json +80 -0
- package/scripts/mutants/acp-stream-hooks.json +158 -0
- package/scripts/mutants/bridge-boot-resume.json +45 -0
- package/scripts/mutants/meta-facts.json +50 -0
- package/scripts/mutants/meta-identity.json +36 -0
- package/scripts/mutants/mux-boundary.json +196 -0
- package/scripts/mutants/mux-fresh-call.json +185 -0
- package/scripts/mutants/mux-launcher-fence.json +123 -0
- package/scripts/mutants/mux-parent-artifact.json +39 -0
- package/scripts/mutants/mux-resume-call.json +148 -0
- package/scripts/mutants/probe-ordering.json +6 -1021
- package/scripts/mutants/release-gate.json +140 -0
- package/scripts/mutants/resume-args.json +76 -0
- package/scripts/mutants/resume-launch-identity.json +96 -0
- package/scripts/mutants/v2-surface.json +74 -23
- package/scripts/mutants/v2-visible-resume.json +215 -0
- package/scripts/new-session-id.ts +9 -4
- 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 +10 -20
- 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 +3 -3
- package/scripts/smoke-meta-honesty.sh +1 -1
- package/scripts/smoke-meta-install-state.sh +4 -0
- package/scripts/smoke-mux-fresh-call-live.ts +365 -0
- package/scripts/smoke-mux-lifecycle-live.ts +1136 -0
- package/scripts/smoke-pi-attach.ts +1 -1
- package/scripts/smoke-user-scope-citizen.sh +1 -1
- package/scripts/tsconfig.json +1 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-preflight.js +0 -160
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn-production.js +0 -273
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn.js +0 -216
- package/pi-extensions/lib/entwurf-v2-spawn-production.ts +0 -373
- package/pi-extensions/lib/entwurf-v2-spawn.ts +0 -323
- package/scripts/check-acp-sdk-surface.ts +0 -236
- package/scripts/check-entwurf-v2-spawn-production.ts +0 -551
- package/scripts/check-entwurf-v2-spawn.ts +0 -399
- package/scripts/smoke-entwurf-v2-spawn-live.ts +0 -188
- package/scripts/smoke-entwurf-v2-spawn-resume-live.ts +0 -469
|
@@ -39,7 +39,6 @@ export interface AcpStreamIdentity {
|
|
|
39
39
|
type ObservedToolState = {
|
|
40
40
|
title: string;
|
|
41
41
|
status?: string;
|
|
42
|
-
notifiedRunning?: boolean;
|
|
43
42
|
};
|
|
44
43
|
|
|
45
44
|
export type AcpPiStreamState = {
|
|
@@ -81,7 +80,12 @@ export function createAcpStreamState(
|
|
|
81
80
|
provider: identity.provider,
|
|
82
81
|
model: identity.model,
|
|
83
82
|
usage: zeroUsage(),
|
|
84
|
-
|
|
83
|
+
// Seed, not a verdict. pi 0.83 added "pending" for exactly this: a partial
|
|
84
|
+
// streaming message has not observed a terminal reason yet, and every pi
|
|
85
|
+
// provider seeds it here and treats a stream that ENDS still-pending as an
|
|
86
|
+
// error rather than a successful stop. Seeding "stop" instead would
|
|
87
|
+
// pre-claim success for the whole time the turn is in flight.
|
|
88
|
+
stopReason: "pending",
|
|
85
89
|
timestamp: opts?.timestamp ?? Date.now(),
|
|
86
90
|
};
|
|
87
91
|
return {
|
|
@@ -221,22 +225,21 @@ function renderToolUpdate(state: AcpPiStreamState, update: Record<string, unknow
|
|
|
221
225
|
const title = titleForTool(update, previous?.title);
|
|
222
226
|
const status = typeof update?.status === "string" ? (update.status as string) : previous?.status;
|
|
223
227
|
const updateContent = Array.isArray(update?.content) ? (update.content as unknown[]) : undefined;
|
|
224
|
-
const meta = update?._meta as { terminal_output?: unknown } | undefined;
|
|
225
|
-
|
|
226
|
-
let notifiedRunning = previous?.notifiedRunning;
|
|
227
228
|
|
|
228
229
|
if (update.sessionUpdate === "tool_call") {
|
|
229
|
-
observedTools.set(toolCallId, { title, status
|
|
230
|
+
observedTools.set(toolCallId, { title, status });
|
|
230
231
|
pushNotice(state, `\n[tool:start] ${sanitizeNoticeFragment(title, NOTICE_TITLE_MAX)}\n`);
|
|
231
232
|
return;
|
|
232
233
|
}
|
|
233
234
|
|
|
234
|
-
// tool_call_update
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
235
|
+
// tool_call_update. There is deliberately no mid-flight `[tool:running]`
|
|
236
|
+
// notice: it would have to be driven by the adapter's `_meta.terminal_output`,
|
|
237
|
+
// and that meta is gated upstream on `clientCapabilities._meta.terminal_output
|
|
238
|
+
// === true` while backend.ts initializes with `clientCapabilities: {}`. So the
|
|
239
|
+
// branch could never fire for any backend we ship — it claimed a transcript
|
|
240
|
+
// line the operator was never going to see. Declaring the terminal capability
|
|
241
|
+
// is a separate axis, not a one-line re-enable: the adapter would then send
|
|
242
|
+
// terminal widgets/metas this mapper cannot render honestly into a transcript.
|
|
240
243
|
if (status && status !== previous?.status) {
|
|
241
244
|
const summary = firstTextContent(update?.rawOutput) ?? firstTextContent(updateContent);
|
|
242
245
|
const suffix = summary ? ` — ${sanitizeNoticeFragment(summary, NOTICE_SUMMARY_MAX)}` : "";
|
|
@@ -250,7 +253,7 @@ function renderToolUpdate(state: AcpPiStreamState, update: Record<string, unknow
|
|
|
250
253
|
}
|
|
251
254
|
}
|
|
252
255
|
|
|
253
|
-
observedTools.set(toolCallId, { title, status
|
|
256
|
+
observedTools.set(toolCallId, { title, status });
|
|
254
257
|
}
|
|
255
258
|
|
|
256
259
|
/** Push a permission-decision notice (informational text, not a tool call). */
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
// Two curated sets live here, one per adapter on the rail
|
|
10
10
|
// (backend-adapter.ts): the unprefixed Claude ids (the reference backend) and
|
|
11
11
|
// the `cortex-` prefixed Snowflake Cortex Code ids (the second backend, landed
|
|
12
|
-
// 0.13.0 — docs/acp-backend-rail.md
|
|
13
|
-
//
|
|
12
|
+
// 0.13.0 — docs/acp-backend-rail.md “Cortex Code audit”). Codex has native
|
|
13
|
+
// delivery-probe evidence but no managed citizen lane; neither it nor Gemini is an ACP backend here.
|
|
14
14
|
// A third governed backend EXTENDS this file the same way — it does not change
|
|
15
15
|
// the pattern.
|
|
16
16
|
|
|
@@ -87,9 +87,9 @@ function claudeContextWindow(model: { id: string; contextWindow: number }): numb
|
|
|
87
87
|
// ── Cortex (Snowflake Cortex Code) curated surface ──────────────────────────
|
|
88
88
|
//
|
|
89
89
|
// entwurf's ACP rail is backend-extensible: Cortex is the first NON-claude
|
|
90
|
-
// backend to land through it (docs/acp-backend-rail.md
|
|
90
|
+
// backend to land through it (docs/acp-backend-rail.md, “Shipped adapters”). pi-ai carries no
|
|
91
91
|
// snowflake/cortex model source, so this surface is HAND-CURATED. Every id
|
|
92
|
-
// carries the reserved `cortex-` prefix
|
|
92
|
+
// carries the reserved `cortex-` prefix: the prefix is the SINGLE routing
|
|
93
93
|
// authority (backend-adapter.ts `routeModel`) and keeps the ids off the Claude
|
|
94
94
|
// native ids Cortex routes to (`cortex-claude-sonnet-5` vs the unprefixed
|
|
95
95
|
// `claude-sonnet-5` the claude adapter owns). `routeModel` strips the prefix to
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
//
|
|
3
3
|
// claude-agent-acp's SettingsManager loads the operator's `~/.claude/settings.json`
|
|
4
4
|
// DIRECTLY (CLAUDE_CONFIG_DIR is the only knob that redirects that read). So the
|
|
5
|
-
// operator's native `permissions.defaultMode` (
|
|
6
|
-
// per-cwd memory/projects state would otherwise leak into entwurf ACP
|
|
5
|
+
// operator's native `permissions.defaultMode` (whatever its current value), hooks,
|
|
6
|
+
// plugins, and per-cwd memory/projects state would otherwise leak into entwurf ACP
|
|
7
7
|
// sessions. The overlay redirects SettingsManager at a pi-owned directory whose
|
|
8
8
|
// `settings.json` WE author (minimal, `hooks:{}`), while keeping exactly the
|
|
9
9
|
// operator entries a backend needs (credentials, caches, built-in skills)
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// materializer, and cortex did: `ensureCortexDualHomeOverlay` (+ projectCortexMcpJson /
|
|
16
16
|
// sweepDeadCortexOverlays / cortexOverlayScopeId) lives further down this file with its
|
|
17
17
|
// own D-number rationale — an isolated HOME rather than a redirect knob, because cortex
|
|
18
|
-
// has no CLAUDE_CONFIG_DIR equivalent (
|
|
18
|
+
// has no CLAUDE_CONFIG_DIR equivalent (ACP rail Cortex audit D2). The two share the module, never the
|
|
19
19
|
// strategy. Codex/Gemini overlays (CODEX_HOME / admin.toml) remain behavior-oracle
|
|
20
20
|
// territory and are out of scope here.
|
|
21
21
|
//
|
|
@@ -105,9 +105,11 @@ export const OVERLAY_BINARY_OWNED: ReadonlySet<string> = new Set([".claude.json"
|
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
107
|
* Minimal overlay settings.json. Only fields with a reason to pin:
|
|
108
|
-
* - `permissions.defaultMode: "
|
|
109
|
-
*
|
|
110
|
-
*
|
|
108
|
+
* - `permissions.defaultMode: "bypassPermissions"` is deliberate unattended
|
|
109
|
+
* ACP operation: a tool call must never suspend a model turn on an interactive
|
|
110
|
+
* permission prompt. This does not widen the callable surface — explicit
|
|
111
|
+
* `tools`/`disallowedTools` still shape it, and `permissionAllow` still rides
|
|
112
|
+
* the inline Claude settings — or bypass backend authentication.
|
|
111
113
|
* - `autoMemoryEnabled: false` — SDK opt-out for auto-memory (defense in
|
|
112
114
|
* depth; the tiny non-empty engraving/preset replacement is the primary
|
|
113
115
|
* write-containment lever for Claude ACP).
|
|
@@ -117,7 +119,7 @@ export const OVERLAY_BINARY_OWNED: ReadonlySet<string> = new Set([".claude.json"
|
|
|
117
119
|
export function overlaySettingsJson(): string {
|
|
118
120
|
return `${JSON.stringify(
|
|
119
121
|
{
|
|
120
|
-
permissions: { defaultMode: "
|
|
122
|
+
permissions: { defaultMode: "bypassPermissions" },
|
|
121
123
|
autoMemoryEnabled: false,
|
|
122
124
|
hooks: {},
|
|
123
125
|
},
|
|
@@ -230,11 +232,11 @@ export function ensureClaudeConfigOverlay(
|
|
|
230
232
|
|
|
231
233
|
// ============================================================================
|
|
232
234
|
// Cortex dual-HOME overlay — the as-measured containment for `cortex acp serve`
|
|
233
|
-
// (CP0 audit 2026-07-29, Cortex Code v1.1.52; docs/acp-backend-rail.md
|
|
235
|
+
// (CP0 audit 2026-07-29, Cortex Code v1.1.52; docs/acp-backend-rail.md “Cortex Code audit”).
|
|
234
236
|
// ============================================================================
|
|
235
237
|
//
|
|
236
238
|
// Why this is NOT the claude-shaped `SNOWFLAKE_HOME`-only overlay PR #40 shipped
|
|
237
|
-
// (each point is a measured defect, not a preference —
|
|
239
|
+
// (each point is a measured defect, not a preference — Cortex audit D-numbers):
|
|
238
240
|
//
|
|
239
241
|
// - D2: cortex reads `CONFIG_DIRS = [".claude", ".cortex"]` at `homedir()` and
|
|
240
242
|
// `~/.claude/skills` — a SNOWFLAKE_HOME redirect cannot move that axis. An
|
|
@@ -37,12 +37,14 @@ export interface SenderEnvelope {
|
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* THE `<sender_info>` synthesis — the one place the sender envelope becomes
|
|
40
|
-
* message text.
|
|
40
|
+
* message text. ONE consumer since the visible-first cut (#50 C3):
|
|
41
41
|
* - the live socket rail's RECEIVER (entwurf-control handleCommand("send")
|
|
42
|
-
* appends it to the delivered customMessage)
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
42
|
+
* appends it to the delivered customMessage).
|
|
43
|
+
*
|
|
44
|
+
* The second consumer was the dormant spawn-resume rail's SENDER, which appended
|
|
45
|
+
* the same shape to a resume prompt so a resumed citizen woke knowing who called.
|
|
46
|
+
* That rail is gone. The shape stays single-sourced here so a future VISIBLE
|
|
47
|
+
* resume renders an identical envelope rather than inventing a second one.
|
|
46
48
|
* `wants_reply` is emitted only when explicitly true — an unset/false marker
|
|
47
49
|
* renders nothing (etiquette marker, not transport contract).
|
|
48
50
|
*/
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* entwurf-core — sync entwurf execution, host-agnostic.
|
|
3
3
|
*
|
|
4
|
-
* DIRECT importers, MEASURED from the import graph (2026-
|
|
4
|
+
* DIRECT importers, RE-MEASURED from the import graph (2026-08-06) — not from memory:
|
|
5
5
|
* - pi-extensions/entwurf-control.ts (the pi adapter)
|
|
6
|
-
* - pi-extensions/lib/
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* - pi-extensions/lib/resume-launch-identity.ts (record-authoritative launch identity,
|
|
7
|
+
* itself consumer-zero since the visible-first cut — see its header)
|
|
8
|
+
* - plus gates/smokes (check-shell-quote, check-package-source-routing,
|
|
9
|
+
* check-entwurf-session-identity, new-session-id, resolve-acp-bridge)
|
|
10
10
|
*
|
|
11
|
-
* The MCP bridge
|
|
12
|
-
*
|
|
13
|
-
* entwurf-v2-
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* runtime, only for spawn-bg production.
|
|
11
|
+
* The MCP bridge does NOT reach this module on any path, and that changed with the cut:
|
|
12
|
+
* its one edge ran `index.ts → entwurf-v2-surface.ts → entwurf-v2-production.ts →
|
|
13
|
+
* entwurf-v2-spawn-production.ts → entwurf-core.ts`, and the spawn-production link was
|
|
14
|
+
* deleted with the `spawn-bg` transport. Do not restate the old "transitive consumer,
|
|
15
|
+
* for spawn-bg production" line; re-measure before claiming either way.
|
|
17
16
|
*
|
|
18
17
|
* NOT consumers on any path: `pi-extensions/entwurf.ts` (the v1 pi native tool surface) and
|
|
19
18
|
* `pi-extensions/lib/entwurf-async.ts` were REMOVED in the 0.12 cutover. Do not reintroduce
|
|
@@ -131,9 +130,9 @@ export function normalizeCodexEntwurfModelForAcp(model?: string): string | undef
|
|
|
131
130
|
// The Entwurf Target Registry is GONE (#50 C3). `pi/entwurf-targets.json` and its
|
|
132
131
|
// reader chain (loadEntwurfTargets / resolveEntwurfTarget / EntwurfRegistryError +
|
|
133
132
|
// the ~/.pi/agent symlink machinery) were the v1 "narrow door" for spawn-model
|
|
134
|
-
// policy — but v2 never spawns from a model tuple: entwurf_v2
|
|
135
|
-
// already-identified record-backed citizen, and the model axis is the
|
|
136
|
-
// own (Identity Preservation Rule). The last readers were the RT-dead
|
|
133
|
+
// policy — but v2 never spawns at all, from a model tuple or otherwise: entwurf_v2
|
|
134
|
+
// addresses an already-identified record-backed citizen, and the model axis is the
|
|
135
|
+
// citizen's own (Identity Preservation Rule). The last readers were the RT-dead
|
|
137
136
|
// buildSessionName mirror and the v1 spawn guard, both swept with this cut.
|
|
138
137
|
// Bridge-extension routing for provider=entwurf survives below (getRegistryRouting
|
|
139
138
|
// ← scripts/resolve-acp-bridge.ts) and takes a caller-supplied target — no file.
|
|
@@ -211,7 +210,8 @@ const SESSION_READ_CHUNK_BYTES = 64 * 1024;
|
|
|
211
210
|
* old name-mirror integrity check and the `requireEntwurf` name-tag
|
|
212
211
|
* authorization are gone (#50 C3): resume authorization is record existence,
|
|
213
212
|
* and transcript integrity is the caller's header-id ↔ record.nativeSessionId
|
|
214
|
-
* check (
|
|
213
|
+
* check (resume-launch-identity.resolveResumeLaunchIdentity — the leaf that
|
|
214
|
+
* outlived the spawn-bg factory it used to sit in).
|
|
215
215
|
*/
|
|
216
216
|
export interface RecordedSessionIdentity {
|
|
217
217
|
/** JSONL header `id` (pi's own session id — the record's `nativeSessionId`). */
|
|
@@ -1,41 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* entwurf-resume-args — the SINGLE source of truth for the `pi` argv
|
|
3
|
-
*
|
|
4
|
-
* `--no-extensions` / `--entwurf-control` decision so the launch shape cannot drift.
|
|
2
|
+
* entwurf-resume-args — the SINGLE source of truth for the `pi` argv that reopens a dormant
|
|
3
|
+
* citizen, MEASURED against the runtime rather than inherited from the removed transport.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
* `-p <prompt>` (the prompt-as-turn authority), and the child is launched WITH
|
|
8
|
-
* `--entwurf-control` and WITHOUT `--no-extensions` — the keep-alive is the GOAL here: the
|
|
9
|
-
* resumed session stands its control socket up and stays addressable. The 5c-3a watcher's
|
|
10
|
-
* `socket-alive` is exactly that "resumed citizen is up" observation (→ release the per-gid
|
|
11
|
-
* lock, child lives on); `child-exited` is the early-exit/failure observation.
|
|
12
|
-
* `plan.launchArgs` (`--approve` or empty, from the decider's preflight) rides along as pi
|
|
13
|
-
* flags before the prompt.
|
|
5
|
+
* ── What changed, and why the old shape was not reusable ──
|
|
14
6
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
7
|
+
* Until the visible-first cut this builder emitted `--mode json -p … <prompt>`: a headless JSON
|
|
8
|
+
* child running one prompt-as-turn, which is what `spawn-bg`'s detached `defaultSpawnChild`
|
|
9
|
+
* needed. The design notes carried that argv forward as "visibility-neutral, reuse verbatim".
|
|
10
|
+
* It is not. Measured 2026-08-06 (private tmux + fixture, `pi --help`: `--print, -p` =
|
|
11
|
+
* *non-interactive mode*): dropped into a tmux window, the headless prefix produces a JSON
|
|
12
|
+
* stream and a turn, not a window an operator can type into. The visible dialect is the one
|
|
13
|
+
* `mux-fresh-call` already measured for a fresh sibling — no `--mode`, no `-p`.
|
|
17
14
|
*
|
|
18
|
-
*
|
|
19
|
-
* getEntwurfExplicitExtensions) — this builder only LAYS OUT argv, it never resolves
|
|
20
|
-
* identity. `explicitExtensionArgs` is preserved verbatim: a recorded `provider=entwurf`
|
|
21
|
-
* resume needs the bridge re-injected to resolve the provider, and dropping it would
|
|
22
|
-
* re-introduce the "Unknown provider" footgun (#29). (A future slice may dedup against
|
|
23
|
-
* settings-loaded extensions; not here.)
|
|
15
|
+
* So the shipped posture is now one shape, and it is the only one:
|
|
24
16
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
17
|
+
* pi --entwurf-control [-e <bridge> …] --session <file> [--provider <p>] --model <m>
|
|
18
|
+
*
|
|
19
|
+
* There is deliberately NO prompt and no `launchArgs`. A resume opens the window; talking to the
|
|
20
|
+
* citizen afterwards is `entwurf_v2`'s job on the socket this launch stands up. That split is
|
|
21
|
+
* what makes the resumed turn free: measured, a promptless resume left the transcript
|
|
22
|
+
* byte-identical (1666 → 1666) and started no model turn at all.
|
|
23
|
+
*
|
|
24
|
+
* The one-shot `legacy` variant (removed 2026-07-27) and the headless prefix (removed here) are
|
|
25
|
+
* not kept behind a variant flag for compatibility. An exported branch no product path takes is
|
|
26
|
+
* exactly what let this module's prose claim a live consumer for months.
|
|
27
|
+
*
|
|
28
|
+
* Provider/model identity is the caller's authority (`resolveResumeLaunchIdentity`) — this
|
|
29
|
+
* builder only LAYS OUT argv, it never resolves identity. `explicitExtensionArgs` is preserved
|
|
30
|
+
* verbatim: a recorded `provider=entwurf` resume needs the bridge re-injected or pi cannot
|
|
31
|
+
* resolve the provider (#29). Measured on that axis: the resolver emits
|
|
32
|
+
* `["-e", "<bridge>"]`, and the argv carries it exactly once, between `--entwurf-control` and
|
|
33
|
+
* `--session`.
|
|
34
|
+
*
|
|
35
|
+
* The runtime path itself is NOT here. Resolving `pi` on PATH and proving it launchable belongs
|
|
36
|
+
* to the mux lane (`mux-launch`), so this module emits the flags after the runtime and nothing
|
|
37
|
+
* more — which is also why it stays import-free and can be read by both tsconfigs.
|
|
27
38
|
*/
|
|
28
39
|
|
|
29
|
-
/** The one shipped launch posture: a resident citizen (`--entwurf-control`, extensions
|
|
30
|
-
* loaded). The `legacy` one-shot variant was removed with its launcher (2026-07-27) — an
|
|
31
|
-
* exported branch no product path took was what let this module's prose claim a second
|
|
32
|
-
* live consumer for months. Reviving a one-shot posture means adding it back deliberately,
|
|
33
|
-
* with a consumer, not un-deleting a dead enum member. */
|
|
34
|
-
export type ResumeArgsVariant = "v2-control";
|
|
35
|
-
|
|
36
40
|
export interface ResumePiArgsInput {
|
|
37
|
-
/** v2-control = resident citizen (`--entwurf-control`, extensions loaded). */
|
|
38
|
-
variant: ResumeArgsVariant;
|
|
39
41
|
/** ABSOLUTE path of the session JSONL to resume — pi's `--session <path>` (#50 C2).
|
|
40
42
|
* It replaced `--session-id <gardenId>`, which did two jobs that are no longer the
|
|
41
43
|
* same string: it named the session to reopen AND fixed the control-socket key. The
|
|
@@ -52,37 +54,24 @@ export interface ResumePiArgsInput {
|
|
|
52
54
|
provider: string | null | undefined;
|
|
53
55
|
/** The resolved launch model (caller applies `modelOverride ?? resumeModel`). */
|
|
54
56
|
model: string;
|
|
55
|
-
/** The resume prompt — the final positional, run as the model turn under `-p`. */
|
|
56
|
-
prompt: string;
|
|
57
|
-
/** The decider's `plan.launchArgs` (`["--approve"]` or `[]`). */
|
|
58
|
-
launchArgs?: readonly string[];
|
|
59
57
|
}
|
|
60
58
|
|
|
61
59
|
/**
|
|
62
|
-
* Build the `pi`
|
|
63
|
-
* child, prompt-as-turn); then the resident posture; then
|
|
64
|
-
* `[…ext args] --session <file> [--provider <p>] --model <m> <prompt>`.
|
|
60
|
+
* Build the `pi` flags for a VISIBLE resume, in the measured order.
|
|
65
61
|
*
|
|
66
62
|
* Invariants the gate pins:
|
|
67
|
-
* -
|
|
68
|
-
*
|
|
69
|
-
* -
|
|
70
|
-
* -
|
|
63
|
+
* - `--entwurf-control` is FIRST: the resumed session must stand its control socket up, or
|
|
64
|
+
* the transcript comes back with no address and the citizen is still unreachable.
|
|
65
|
+
* - no `--mode`, no `-p`, no positional prompt — the window is interactive and no turn runs.
|
|
66
|
+
* - `explicitExtensionArgs` appears exactly once, after the control flag and before
|
|
67
|
+
* `--session`.
|
|
68
|
+
* - provider is emitted only when recorded; `--model <m>` is the tail.
|
|
71
69
|
*/
|
|
72
70
|
export function buildResumePiArgs(input: ResumePiArgsInput): string[] {
|
|
73
|
-
const args: string[] = ["--
|
|
74
|
-
|
|
75
|
-
// Resident citizen: stand the control socket up (A1) and keep extensions loaded. The
|
|
76
|
-
// keep-alive the removed one-shot launcher had to avoid is precisely the goal here —
|
|
77
|
-
// the resumed session must stay addressable. `--approve`/launchArgs ride along.
|
|
78
|
-
args.push("--entwurf-control");
|
|
79
|
-
args.push(...(input.launchArgs ?? []));
|
|
80
|
-
|
|
81
|
-
// Suffix — the fixed identity layout.
|
|
71
|
+
const args: string[] = ["--entwurf-control"];
|
|
82
72
|
args.push(...input.explicitExtensionArgs);
|
|
83
73
|
args.push("--session", input.sessionFile);
|
|
84
74
|
if (input.provider) args.push("--provider", input.provider);
|
|
85
|
-
args.push("--model", input.model
|
|
86
|
-
|
|
75
|
+
args.push("--model", input.model);
|
|
87
76
|
return args;
|
|
88
77
|
}
|
|
@@ -46,7 +46,7 @@ export const EntwurfV2InputSchema = Type.Object(
|
|
|
46
46
|
}),
|
|
47
47
|
intent: StringEnum(ENTWURF_INTENTS, {
|
|
48
48
|
description:
|
|
49
|
-
"caller's declared outcome contract (F1): fire-and-forget = ack only, owned-outcome
|
|
49
|
+
"caller's declared outcome contract (F1). ONE member since the visible-first cut: fire-and-forget = ack only. The second, owned-outcome (caller owns completion), resumed a dormant citizen by launching a hidden background child and was withdrawn — a dormant citizen is currently unreachable by this verb.",
|
|
50
50
|
}),
|
|
51
51
|
mode: Type.Optional(
|
|
52
52
|
StringEnum(ENTWURF_V2_MODES, {
|