@junghanacs/entwurf 0.45.0 → 0.50.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 +3 -2
- package/BASELINE.md +110 -1
- package/CHANGELOG.md +169 -0
- package/CONTRIBUTING.md +1 -1
- package/ENTWURF-CLI.json +1229 -0
- package/ENTWURF.json +63 -15
- package/FAQ.md +4 -4
- package/README.md +124 -188
- package/VERIFY.md +2 -2
- package/docs/external-mcp-host.md +3 -3
- package/docs/setup-clean-host.md +37 -12
- package/mcp/entwurf-bridge/dist/ENTWURF.json +63 -15
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +19 -8
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +483 -18
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-dispatch.js +47 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +613 -33
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-resume-call.js +497 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +3 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +50 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-location.js +347 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-owner-fence.js +1132 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js +1 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +18 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +30 -15
- package/mcp/entwurf-bridge/dist/scripts/herdr-rails.mjs +40 -7
- package/mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js +1 -27
- package/mcp/entwurf-bridge/dist/scripts/meta-bridge-prune.js +1 -1
- package/mcp/entwurf-bridge/dist/scripts/meta-facts.js +1 -1
- package/mcp/entwurf-bridge/dist/scripts/peer-facts.js +1 -1
- package/mcp/entwurf-bridge/src/index.ts +28 -8
- package/mcp/entwurf-bridge/tsconfig.build.json +7 -1
- package/native/win32-x64/manifest.json +1 -1
- package/package.json +6 -3
- package/pi/pi-durable/bootstrap.mjs +179 -6
- package/pi/pi-durable/carrier/runtime.js +108 -9
- package/pi/pi-durable/carrier/session-name.js +11 -0
- package/pi/pi-durable/carrier/sessions.js +96 -20
- package/pi/pi-durable/overlay/upstream-pin.json +11 -5
- package/pi-extensions/lib/entwurf-v2-visible-resume.ts +710 -38
- package/pi-extensions/lib/fresh-call-dispatch.ts +64 -12
- package/pi-extensions/lib/herdr-fresh-call.ts +919 -40
- package/pi-extensions/lib/herdr-resume-call.ts +810 -0
- package/pi-extensions/lib/meta-session.ts +13 -8
- package/pi-extensions/lib/mux-resume-call.ts +77 -4
- package/pi-extensions/lib/pi-durable-fresh-preflight.ts +9 -2
- package/pi-extensions/lib/pi-durable-location.ts +446 -0
- package/pi-extensions/lib/pi-durable-owner-fence.ts +1461 -0
- package/pi-extensions/lib/pi-durable-runtime.ts +4 -0
- package/pi-extensions/lib/resume-launch-identity.ts +31 -12
- package/pi-extensions/meta-bridge-pi-durable.ts +29 -15
- package/run.sh +411 -657
- package/scripts/check-claude-launch.ts +341 -0
- package/scripts/check-entwurf-v2-surface.ts +2 -2
- package/scripts/check-fresh-call-dispatch.ts +70 -9
- package/scripts/check-fresh-cut-gate.sh +23 -9
- package/scripts/check-gate-qualification.ts +18 -13
- package/scripts/check-herdr-activation.ts +301 -23
- package/scripts/check-herdr-fresh-call.ts +161 -25
- package/scripts/check-herdr-plugin-build.ts +173 -1
- package/scripts/check-herdr-plugin.ts +28 -2
- package/scripts/check-herdr-runtime-bootstrap.ts +136 -0
- package/scripts/check-install-surface.ts +1236 -6
- package/scripts/check-meta-doctor-oracle.sh +1 -1
- package/scripts/check-meta-facts.ts +20 -1
- package/scripts/check-mux-resume-call.ts +28 -0
- package/scripts/check-peer-facts.ts +39 -2
- package/scripts/cli-help.mjs +222 -0
- package/scripts/dev-bin.sh +39 -2
- package/scripts/herdr-activation.mjs +28 -6
- package/scripts/herdr-operator-link.mjs +389 -0
- package/scripts/herdr-plugin-activate.mjs +5 -0
- package/scripts/herdr-plugin-deactivate.mjs +18 -5
- package/scripts/herdr-rails.mjs +43 -7
- package/scripts/herdr-runtime.mjs +27 -0
- package/scripts/meta-bridge-fresh-cut.ts +1 -27
- package/scripts/meta-bridge-prune.ts +1 -1
- package/scripts/meta-facts.ts +1 -1
- package/scripts/mutants/agent-guidance.json +4 -4
- package/scripts/mutants/claude-launch.json +142 -0
- package/scripts/mutants/cli-help-source.json +420 -0
- package/scripts/mutants/dev-gate-dispatch.json +109 -0
- package/scripts/mutants/fresh-call-dispatch.json +38 -2
- package/scripts/mutants/fresh-cut.json +15 -0
- package/scripts/mutants/herdr-activation.json +128 -0
- package/scripts/mutants/herdr-fresh-call.json +278 -5
- package/scripts/mutants/herdr-operator-link.json +318 -0
- package/scripts/mutants/herdr-plugin-build.json +55 -0
- package/scripts/mutants/herdr-plugin.json +17 -0
- package/scripts/mutants/herdr-resume-call.json +288 -0
- package/scripts/mutants/herdr-runtime-bootstrap.json +48 -0
- package/scripts/mutants/meta-facts.json +15 -0
- package/scripts/mutants/mux-resume-call.json +63 -2
- package/scripts/mutants/peer-facts.json +15 -0
- package/scripts/mutants/pi-durable.json +979 -1
- package/scripts/mutants/source-install.json +35 -0
- package/scripts/mutants/v2-surface.json +2 -2
- package/scripts/mutants/v2-visible-resume.json +255 -6
- package/scripts/peer-facts.ts +1 -1
- package/scripts/smoke-agy-install-state.sh +66 -0
- package/scripts/smoke-meta-prune.sh +23 -0
- package/scripts/smoke-omp-fresh-live.ts +12 -3
- package/scripts/smoke-omp-receive-live.ts +7 -1
- package/scripts/smoke-pi-durable-fresh-live.ts +12 -2
- package/team.json.example +39 -0
|
@@ -61,6 +61,9 @@ export interface PiDurablePackageLayout {
|
|
|
61
61
|
readonly bridgeEntry: string;
|
|
62
62
|
/** The compiled sender/receiver owner join the bridge decides deliverability with. */
|
|
63
63
|
readonly deliverability: string;
|
|
64
|
+
/** The compiled owner fence the packaged bootstrap imports. A fixed unit of this package that only the
|
|
65
|
+
* fresh preflight asks for; the carrier/SDK verifier below does not read it. */
|
|
66
|
+
readonly ownerFence: string;
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
/** The layout rooted at `root`, as plain paths. */
|
|
@@ -77,6 +80,7 @@ export function piDurableLayoutAt(root: string): PiDurablePackageLayout {
|
|
|
77
80
|
adapter: path.join(dist, "pi-extensions", "meta-bridge-pi-durable.js"),
|
|
78
81
|
bridgeEntry: path.join(dist, "mcp", "entwurf-bridge", "src", "index.js"),
|
|
79
82
|
deliverability: path.join(dist, "pi-extensions", "lib", "entwurf-deliverability.js"),
|
|
83
|
+
ownerFence: path.join(dist, "pi-extensions", "lib", "pi-durable-owner-fence.js"),
|
|
80
84
|
};
|
|
81
85
|
}
|
|
82
86
|
|
|
@@ -10,13 +10,15 @@
|
|
|
10
10
|
* must equal `record.nativeSessionId`, an addressable read (#52) rather than a plain targeted one,
|
|
11
11
|
* a recorded ACP provider must resolve its bridge.
|
|
12
12
|
*
|
|
13
|
-
* The domain is pi and claude-code (#147 L2)
|
|
14
|
-
* record when
|
|
15
|
-
* the transcript header (the launch needs its provider/model/cwd). For
|
|
16
|
-
* transcript at all — the vendor owns it, and `claude --resume <id>` needs
|
|
17
|
-
* checks only that the recorded path is an absolute file that still exists.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
13
|
+
* The domain is pi and claude-code (#147 L2), and pi-durable (#157 B3). Each owns its transcript
|
|
14
|
+
* and re-attaches to the same record when its native session reopens; this leaf only names that
|
|
15
|
+
* session. For pi it reads the transcript header (the launch needs its provider/model/cwd). For
|
|
16
|
+
* claude-code it reads no transcript at all — the vendor owns it, and `claude --resume <id>` needs
|
|
17
|
+
* nothing from it — and checks only that the recorded path is an absolute file that still exists.
|
|
18
|
+
* For pi-durable it reads nothing beyond the record: the parent's reservation re-reads the record
|
|
19
|
+
* and its location inside its own holds, and the launched child selects its session from the record
|
|
20
|
+
* itself, so this leaf only says WHICH backend the address names. Every other backend is a named
|
|
21
|
+
* refusal: native-push citizens have no resume authority (AGENTS Hard Rule 10).
|
|
20
22
|
*
|
|
21
23
|
* It takes a bare garden id, not a plan: the plan type it used to destructure belonged to
|
|
22
24
|
* the removed transport, and the identity question never needed the rest of it.
|
|
@@ -29,7 +31,7 @@ import { getEntwurfExplicitExtensions, readSessionIdentity } from "./entwurf-cor
|
|
|
29
31
|
import { readAddressableMetaIdentity } from "./meta-session.ts";
|
|
30
32
|
|
|
31
33
|
/** The backends with a visible same-id resume. A closed set: nothing outside it is reopened. */
|
|
32
|
-
export const RESUME_BACKENDS = ["pi", "claude-code"] as const;
|
|
34
|
+
export const RESUME_BACKENDS = ["pi", "claude-code", "pi-durable"] as const;
|
|
33
35
|
export type ResumeBackend = (typeof RESUME_BACKENDS)[number];
|
|
34
36
|
|
|
35
37
|
/**
|
|
@@ -42,8 +44,7 @@ export type ResumeBackend = (typeof RESUME_BACKENDS)[number];
|
|
|
42
44
|
* except by looking. A Claude Code or agy garden id is different in kind: nothing is broken,
|
|
43
45
|
* the operator simply asked a capability boundary to do something it does not cover, and the
|
|
44
46
|
* honest answer is a named refusal rather than an error report about a store that is fine. Since
|
|
45
|
-
* #147 L2 the boundary is `RESUME_BACKENDS`; a Codex, agy, Copilot
|
|
46
|
-
* this refusal.
|
|
47
|
+
* #147 L2 the boundary is `RESUME_BACKENDS`; a Codex, agy, Copilot or OMP garden id is this refusal.
|
|
47
48
|
*
|
|
48
49
|
* It carries a `reason` so the caller matches on a FIELD. Parsing the message string would make
|
|
49
50
|
* the wording load-bearing, and re-reading the record to ask "was it pi?" would read an address
|
|
@@ -55,7 +56,7 @@ export class ResumeBackendUnsupportedError extends Error {
|
|
|
55
56
|
constructor(gardenId: string, backend: string) {
|
|
56
57
|
super(
|
|
57
58
|
`resume-launch-identity: ${gardenId} is a ${backend} citizen — same-id resume is a host-adapter relaunch ` +
|
|
58
|
-
`capability, and its domain is ${RESUME_BACKENDS.join("
|
|
59
|
+
`capability, and its domain is ${RESUME_BACKENDS.join(", ")}. ` +
|
|
59
60
|
`This is a capability boundary, not citizen rank.`,
|
|
60
61
|
);
|
|
61
62
|
this.name = "ResumeBackendUnsupportedError";
|
|
@@ -91,7 +92,19 @@ export interface ClaudeCodeLaunchIdentity {
|
|
|
91
92
|
attachIncarnation: string | null;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
|
|
95
|
+
/**
|
|
96
|
+
* A pi-durable citizen's facts, from the record only (#157 B3). Not launch inputs: the parent's
|
|
97
|
+
* reservation re-reads the record and its C4 location inside its own record-arm then S_D holds, and
|
|
98
|
+
* its launch takes the cwd from THAT pair. They are here so the backend the address names is read
|
|
99
|
+
* once, by the same addressable reader as every other backend.
|
|
100
|
+
*/
|
|
101
|
+
export interface PiDurableLaunchIdentity {
|
|
102
|
+
backend: "pi-durable";
|
|
103
|
+
nativeSessionId: string;
|
|
104
|
+
cwd: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export type LaunchIdentity = PiLaunchIdentity | ClaudeCodeLaunchIdentity | PiDurableLaunchIdentity;
|
|
95
108
|
|
|
96
109
|
/**
|
|
97
110
|
* Claude Code's session id grammar: a lowercase UUID. The value becomes an argv token after
|
|
@@ -180,6 +193,12 @@ function resolveClaudeCodeLaunchIdentity(
|
|
|
180
193
|
export function resolveResumeLaunchIdentity(gardenId: string): LaunchIdentity {
|
|
181
194
|
const record = readAddressableMetaIdentity(gardenId);
|
|
182
195
|
if (record.backend === "claude-code") return resolveClaudeCodeLaunchIdentity(gardenId, record);
|
|
196
|
+
if (record.backend === "pi-durable") {
|
|
197
|
+
if (!path.isAbsolute(record.cwd)) {
|
|
198
|
+
throw new Error(`resume-launch-identity: ${gardenId} records a relative cwd ${JSON.stringify(record.cwd)}.`);
|
|
199
|
+
}
|
|
200
|
+
return { backend: "pi-durable", nativeSessionId: record.nativeSessionId, cwd: record.cwd };
|
|
201
|
+
}
|
|
183
202
|
if (record.backend !== "pi") {
|
|
184
203
|
throw new ResumeBackendUnsupportedError(gardenId, record.backend);
|
|
185
204
|
}
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* createPiDurableContact({ bridgeEntry })
|
|
11
11
|
* .extension tools-only durable Extension, handed to the overlay's
|
|
12
12
|
* `openDurable({ extensions })` BEFORE the Harness opens
|
|
13
|
-
* .attach(session) once `openDurable` returns:
|
|
13
|
+
* .attach(session) once `openDurable` returns: commit the record's binding and its
|
|
14
|
+
* location evidence (`lib/pi-durable-location.ts`), name this process as
|
|
14
15
|
* sender, spawn the compiled entwurf-bridge as a DIRECT child, then
|
|
15
16
|
* release the tools
|
|
16
17
|
*
|
|
@@ -93,17 +94,21 @@
|
|
|
93
94
|
* refusal.
|
|
94
95
|
*
|
|
95
96
|
* CALLER FRESH (the sixth tool). `entwurf_fresh_call` lets this durable citizen open a sibling, under the
|
|
96
|
-
* bridge's own schema (pinned below, including the `pi-durable` backend itself).
|
|
97
|
-
* beside the tmux pane this host was launched in, which a fresh-opened durable host inherits
|
|
98
|
-
* operator host started outside tmux gets the bridge's named `no-tmux-context` refusal.
|
|
99
|
-
*
|
|
97
|
+
* bridge's own schema (pinned below, including the `pi-durable` backend itself). Outside herdr, the bridge
|
|
98
|
+
* places it beside the tmux pane this host was launched in, which a fresh-opened durable host inherits,
|
|
99
|
+
* and an operator host started outside tmux gets the bridge's named `no-tmux-context` refusal. Inside
|
|
100
|
+
* herdr (`HERDR_ENV=1`, which the bridge child inherits from this host) it requests a new herdr tab
|
|
101
|
+
* instead, for pi, claude-code or pi-durable only; the other backends are refused by name, and nothing
|
|
102
|
+
* falls back to tmux. Exposing the tool is not a caller LIVE proof.
|
|
100
103
|
*
|
|
101
104
|
* CALLER RESUME (the seventh tool, #147 L1). `entwurf_resume_call` lets this durable citizen reopen a
|
|
102
|
-
* DORMANT pi or Claude Code citizen under its own garden id (#147 L2
|
|
103
|
-
* single-`target` schema (pinned below). The bridge decides everything
|
|
104
|
-
* exactly as for any other caller: other backends have no same-id
|
|
105
|
-
*
|
|
106
|
-
*
|
|
105
|
+
* DORMANT pi, pi-durable or Claude Code citizen under its own garden id (#147 L2; pi-durable since
|
|
106
|
+
* #157 B3), under the bridge's own single-`target` schema (pinned below). The bridge decides everything
|
|
107
|
+
* else from the target's record, exactly as for any other caller: other backends have no same-id
|
|
108
|
+
* resume, a session a live process holds is refused, and outside herdr the window opens in the tmux
|
|
109
|
+
* session this host was launched in. Inside herdr a pi, pi-durable or Claude Code target reopens, in a
|
|
110
|
+
* new herdr tab (Claude Code since #157 stage 4). Exposing the tool names no new authority and is not a caller LIVE proof; it never resumes
|
|
111
|
+
* this durable host itself.
|
|
107
112
|
*
|
|
108
113
|
* NOT HERE: crash-safe receive. `entwurf_inbox_read` archives a body before the durable tool
|
|
109
114
|
* result commits, so a crash between the two loses that body to the model; recovery settles the
|
|
@@ -126,10 +131,10 @@ import {
|
|
|
126
131
|
readMetaReceiverMarker,
|
|
127
132
|
readMetaSenderMarker,
|
|
128
133
|
requireGardenId,
|
|
129
|
-
upsertMetaSession,
|
|
130
134
|
writeMetaReceiverMarker,
|
|
131
135
|
writeMetaSenderMarker,
|
|
132
136
|
} from "./lib/meta-session.ts";
|
|
137
|
+
import { commitPiDurableBirth } from "./lib/pi-durable-location.ts";
|
|
133
138
|
|
|
134
139
|
/** The backend id this contact mints under. */
|
|
135
140
|
export const PI_DURABLE_BACKEND = "pi-durable" as const;
|
|
@@ -1159,9 +1164,18 @@ export function createPiDurableContact(options: PiDurableContactOptions): PiDura
|
|
|
1159
1164
|
}
|
|
1160
1165
|
const ownerPid = options.ownerPid ?? process.pid;
|
|
1161
1166
|
const sendersDir = path.resolve(options.sendersDir ?? defaultMetaSendersDir());
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1164
|
-
|
|
1167
|
+
const receiversDir = path.resolve(options.receiversDir ?? defaultMetaReceiversDir());
|
|
1168
|
+
// The record and its location evidence, bound by one minted attachIncarnation under the
|
|
1169
|
+
// record-arm lock of the receivers root this birth uses. A refusal here fails the birth;
|
|
1170
|
+
// there is no unbound fallback. The pair is not one atomic commit: see the leaf's
|
|
1171
|
+
// FAILURE STATES for what each failure leaves behind.
|
|
1172
|
+
const upsert = commitPiDurableBirth({
|
|
1173
|
+
nativeSessionId: id,
|
|
1174
|
+
cwd,
|
|
1175
|
+
sessionDirectory: session.directory,
|
|
1176
|
+
ownerPid,
|
|
1177
|
+
sessionsDir: options.sessionsDir ?? defaultMetaSessionsDir(),
|
|
1178
|
+
receiversDir,
|
|
1165
1179
|
});
|
|
1166
1180
|
const gardenId = upsert.record.gardenId;
|
|
1167
1181
|
const markerPath = writeMetaSenderMarker({
|
|
@@ -1176,7 +1190,7 @@ export function createPiDurableContact(options: PiDurableContactOptions): PiDura
|
|
|
1176
1190
|
sessionsDir: upsert.dir,
|
|
1177
1191
|
sendersDir,
|
|
1178
1192
|
mailboxDir: path.resolve(options.mailboxDir ?? defaultMetaMailboxDir()),
|
|
1179
|
-
receiversDir
|
|
1193
|
+
receiversDir,
|
|
1180
1194
|
};
|
|
1181
1195
|
const client = await connect({
|
|
1182
1196
|
command: process.execPath,
|