@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
|
@@ -4,6 +4,55 @@ import { mkdir, readdir, realpath, stat } from "node:fs/promises";
|
|
|
4
4
|
import { basename, join, resolve } from "node:path";
|
|
5
5
|
import lockfile from "proper-lockfile";
|
|
6
6
|
import { getAgentDir } from "entwurf-pi-dist:config.js";
|
|
7
|
+
import { isSessionName } from "./session-name.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* An owner's end as the opener observed it (#157 B3): the storage outcome of the first actual close, and the
|
|
11
|
+
* lease outcome of its release.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
/** One admitted owner of a session directory, from before its native lease through its end. */
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/** What an owned open hands the fence once its directory is chosen, before its native lease. */
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The owner fence every owned open takes before its native lease (#157 B3). Supplied by the host, never imported
|
|
51
|
+
* here; it admits this opener or refuses, and a refusal opens nothing.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
7
56
|
|
|
8
57
|
/** One session directory holding `session.sqlite`, locked by this process. */
|
|
9
58
|
|
|
@@ -13,6 +62,8 @@ import { getAgentDir } from "entwurf-pi-dist:config.js";
|
|
|
13
62
|
|
|
14
63
|
|
|
15
64
|
|
|
65
|
+
|
|
66
|
+
|
|
16
67
|
|
|
17
68
|
|
|
18
69
|
/** One session of a directory as its files show it (#143): nothing in it is opened. */
|
|
@@ -27,8 +78,6 @@ import { getAgentDir } from "entwurf-pi-dist:config.js";
|
|
|
27
78
|
|
|
28
79
|
|
|
29
80
|
|
|
30
|
-
const SESSION_NAME = /^\d{13}-[0-9a-f-]{36}$/u;
|
|
31
|
-
|
|
32
81
|
/** A path's stat, or undefined when nothing is there; any other failure is thrown, never read as absence. */
|
|
33
82
|
async function statIfPresent(path ) {
|
|
34
83
|
try {
|
|
@@ -39,15 +88,17 @@ async function statIfPresent(path ) {
|
|
|
39
88
|
}
|
|
40
89
|
}
|
|
41
90
|
|
|
42
|
-
async function sessionRoot(cwdInput )
|
|
91
|
+
async function sessionRoot(cwdInput ) {
|
|
43
92
|
const cwd = await realpath(resolve(cwdInput));
|
|
93
|
+
// Read once: the root is joined from this value, and the fence is handed this same value as it came (#157 B3).
|
|
94
|
+
const agentDir = getAgentDir();
|
|
44
95
|
const root = join(
|
|
45
|
-
|
|
96
|
+
agentDir,
|
|
46
97
|
"experimental",
|
|
47
98
|
"durable-sessions",
|
|
48
99
|
createHash("sha256").update(cwd).digest("hex").slice(0, 24),
|
|
49
100
|
);
|
|
50
|
-
return { cwd, root };
|
|
101
|
+
return { cwd, root, profile: agentDir };
|
|
51
102
|
}
|
|
52
103
|
|
|
53
104
|
/** The sessions of `cwd`, most recently written first. Reads names, file times and locks; creates nothing. */
|
|
@@ -62,7 +113,7 @@ export async function listSessions(cwdInput ) {
|
|
|
62
113
|
}
|
|
63
114
|
const sessions = [];
|
|
64
115
|
for (const entry of entries) {
|
|
65
|
-
if (!entry.isDirectory() || !
|
|
116
|
+
if (!entry.isDirectory() || !isSessionName(entry.name)) continue;
|
|
66
117
|
const directory = join(root, entry.name);
|
|
67
118
|
let lastWriteAt ;
|
|
68
119
|
for (const file of ["session.sqlite", "session.sqlite-wal"]) {
|
|
@@ -80,19 +131,23 @@ export async function listSessions(cwdInput ) {
|
|
|
80
131
|
return sessions.sort((a, b) => (b.lastWriteAt ?? b.createdAt) - (a.lastWriteAt ?? a.createdAt));
|
|
81
132
|
}
|
|
82
133
|
|
|
83
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* A new session for `cwd`, its newest one with `continueSession`, or the existing one named by `sessionId`, admitted
|
|
136
|
+
* by `fence` before its native lease is taken.
|
|
137
|
+
*/
|
|
84
138
|
export async function selectSession(
|
|
139
|
+
fence ,
|
|
85
140
|
cwdInput ,
|
|
86
141
|
continueSession ,
|
|
87
142
|
sessionId ,
|
|
88
143
|
) {
|
|
89
|
-
const { cwd, root } = await sessionRoot(cwdInput);
|
|
144
|
+
const { cwd, root, profile } = await sessionRoot(cwdInput);
|
|
90
145
|
await mkdir(root, { recursive: true });
|
|
91
146
|
|
|
92
147
|
let directory ;
|
|
93
148
|
let created = false;
|
|
94
149
|
if (sessionId !== undefined) {
|
|
95
|
-
const chosen =
|
|
150
|
+
const chosen = isSessionName(sessionId) ? join(root, sessionId) : undefined;
|
|
96
151
|
if (chosen === undefined || !(await statIfPresent(chosen))?.isDirectory()) {
|
|
97
152
|
throw new Error(`No durable session ${sessionId} exists for ${cwd}`);
|
|
98
153
|
}
|
|
@@ -100,7 +155,7 @@ export async function selectSession(
|
|
|
100
155
|
} else if (continueSession) {
|
|
101
156
|
const entries = await readdir(root, { withFileTypes: true });
|
|
102
157
|
const newest = entries
|
|
103
|
-
.filter((entry) => entry.isDirectory() &&
|
|
158
|
+
.filter((entry) => entry.isDirectory() && isSessionName(entry.name))
|
|
104
159
|
.map((entry) => entry.name)
|
|
105
160
|
.sort()
|
|
106
161
|
.at(-1);
|
|
@@ -112,15 +167,36 @@ export async function selectSession(
|
|
|
112
167
|
created = true;
|
|
113
168
|
}
|
|
114
169
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
170
|
+
// The owner fence admits this opener BEFORE the native lease, or refuses with nothing taken (#157 B3).
|
|
171
|
+
const ticket = await fence.admit({ directory, selectedN: basename(directory), created, profile });
|
|
172
|
+
// A lock left by a crashed process goes stale after 10 s; wait that long before giving up.
|
|
173
|
+
const lease = lockfile.lock(directory, {
|
|
174
|
+
realpath: false,
|
|
175
|
+
retries: { retries: 12, minTimeout: 1000, maxTimeout: 1000 },
|
|
176
|
+
});
|
|
177
|
+
const release = await lease.catch(async (error) => {
|
|
178
|
+
const refused = new Error(`Session is already open in another process: ${directory}`, { cause: error });
|
|
179
|
+
// No acquisition was observed: this owner ends with nothing opened and no lease held.
|
|
180
|
+
await ticket.endUnleased().catch((fenceError) => {
|
|
181
|
+
throw new AggregateError([refused, fenceError], `${refused.message}; its owner end was not recorded`);
|
|
121
182
|
});
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
183
|
+
throw refused;
|
|
184
|
+
});
|
|
185
|
+
// Apart from the lease's own failure: the lease IS held here, so a failed checkpoint is never recorded as an
|
|
186
|
+
// acquire failure. The open fails and releases the lease; the claim stays fenced under this live owner.
|
|
187
|
+
await ticket.leased().catch(async (error) => {
|
|
188
|
+
await release().catch((releaseError) => {
|
|
189
|
+
throw new AggregateError([error, releaseError], `lease checkpoint and release both failed: ${directory}`);
|
|
190
|
+
});
|
|
191
|
+
throw error;
|
|
192
|
+
});
|
|
193
|
+
return {
|
|
194
|
+
id: basename(directory),
|
|
195
|
+
directory,
|
|
196
|
+
database: join(directory, "session.sqlite"),
|
|
197
|
+
cwd,
|
|
198
|
+
created,
|
|
199
|
+
release,
|
|
200
|
+
ticket,
|
|
201
|
+
};
|
|
126
202
|
}
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
57
|
"carrier": {
|
|
58
|
-
"comment": "files: the six app files bootstrap.mjs needs (main.ts is replaced by the bootstrap), each emitted from its owned `source`. distTargets: every pi-coding-agent internal the stripped files import, as paths under the published dist/; the emitter refuses a set that differs and the resolver answers no other.",
|
|
58
|
+
"comment": "files: the six app files bootstrap.mjs needs (main.ts is replaced by the bootstrap) and the import-free session-name grammar Entwurf owns beside them (#157 G3 B-L1, no upstream origin), each emitted from its owned `source`. distTargets: every pi-coding-agent internal the stripped files import, as paths under the published dist/; the emitter refuses a set that differs and the resolver answers no other.",
|
|
59
59
|
"emitter": {
|
|
60
60
|
"tool": "node:module.stripTypeScriptTypes",
|
|
61
61
|
"mode": "strip",
|
|
@@ -70,8 +70,8 @@
|
|
|
70
70
|
{
|
|
71
71
|
"name": "runtime.js",
|
|
72
72
|
"source": "pi/pi-durable/src/experimental/durable/runtime.ts",
|
|
73
|
-
"blob": "
|
|
74
|
-
"sha256": "
|
|
73
|
+
"blob": "bb5715ab143945e9f2c067ff71d890fe29875d09",
|
|
74
|
+
"sha256": "437834997b9870ab0383aea7d86ff7880ef7e2ebbbfe4c6360f67d8d36f777ec"
|
|
75
75
|
},
|
|
76
76
|
{
|
|
77
77
|
"name": "tui.js",
|
|
@@ -94,14 +94,20 @@
|
|
|
94
94
|
{
|
|
95
95
|
"name": "sessions.js",
|
|
96
96
|
"source": "pi/pi-durable/src/experimental/durable/sessions.ts",
|
|
97
|
-
"blob": "
|
|
98
|
-
"sha256": "
|
|
97
|
+
"blob": "f107238fefb63c95b29add5aa76fcbd9153c94db",
|
|
98
|
+
"sha256": "0ac7c919f2bf8a3b0e5dbd307045108a803b161f87298cd9fe440f239784c7ac"
|
|
99
99
|
},
|
|
100
100
|
{
|
|
101
101
|
"name": "subagent.js",
|
|
102
102
|
"source": "pi/pi-durable/src/experimental/durable/subagent.ts",
|
|
103
103
|
"blob": "3fa9aebbe93e82cfff436e7b9d4c9877e0ed0e27",
|
|
104
104
|
"sha256": "1cb2390b673a0d914117bc07708c39b004fb10e48b31436bf9d9d2b953a64220"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"name": "session-name.js",
|
|
108
|
+
"source": "pi/pi-durable/src/experimental/durable/session-name.ts",
|
|
109
|
+
"blob": "c57c760ca3c14363f195ac9d9c2add984e4118d5",
|
|
110
|
+
"sha256": "9eadecfe9a48bba0d221370f4a236286e0d6f10d663b15ff4cb5975adee67018"
|
|
105
111
|
}
|
|
106
112
|
],
|
|
107
113
|
"distTargets": [
|