@junghanacs/entwurf 0.41.2 → 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 +4 -2
- package/BASELINE.md +110 -1
- package/CHANGELOG.md +328 -0
- package/CONTRIBUTING.md +1 -1
- package/ENTWURF-CLI.json +1229 -0
- package/ENTWURF.json +284 -0
- package/FAQ.md +4 -4
- package/README.md +125 -184
- 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 +284 -0
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +46 -196
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/agent-guidance.js +299 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +21 -25
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +498 -29
- 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-fresh-call.js +23 -20
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +68 -21
- 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/remote-pair-send.js +7 -4
- 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 +53 -60
- 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 +149 -327
- package/mcp/entwurf-bridge/tsconfig.build.json +7 -1
- package/native/win32-x64/manifest.json +1 -1
- package/package.json +8 -4
- 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/agent-guidance.ts +341 -0
- package/pi-extensions/lib/entwurf-v2-surface.ts +25 -31
- package/pi-extensions/lib/entwurf-v2-visible-resume.ts +725 -54
- 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-fresh-call.ts +23 -26
- package/pi-extensions/lib/mux-resume-call.ts +95 -23
- 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/remote-pair-send.ts +7 -4
- package/pi-extensions/lib/resume-launch-identity.ts +31 -12
- package/pi-extensions/meta-bridge-pi-durable.ts +52 -79
- package/run.sh +417 -654
- package/scripts/build-bridge.sh +10 -0
- package/scripts/check-capability-bundle-reach.ts +86 -0
- package/scripts/check-claude-launch.ts +341 -0
- package/scripts/check-entwurf-self-address.ts +3 -3
- package/scripts/check-entwurf-v2-surface.ts +94 -98
- package/scripts/check-fresh-call-dispatch.ts +70 -9
- package/scripts/check-fresh-cut-gate.sh +23 -9
- package/scripts/check-gate-qualification.ts +24 -17
- 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 +48 -3
- 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 +160 -0
- package/scripts/mutants/bridge-boot-resume.json +6 -8
- package/scripts/mutants/capability-bundle-reach.json +124 -0
- package/scripts/mutants/claude-launch.json +142 -0
- package/scripts/mutants/cli-help-source.json +420 -0
- package/scripts/mutants/control-send-receipt.json +21 -4
- 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-fresh-call.json +14 -3
- package/scripts/mutants/mux-resume-call.json +89 -3
- package/scripts/mutants/peer-facts.json +15 -0
- package/scripts/mutants/pi-durable.json +996 -6
- package/scripts/mutants/remote-pair-send.json +21 -2
- package/scripts/mutants/self-address.json +10 -10
- package/scripts/mutants/source-install.json +35 -0
- package/scripts/mutants/v2-surface.json +50 -64
- package/scripts/mutants/v2-visible-resume.json +266 -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
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product-owned natural-language logic, authored only in root ENTWURF.json (#154).
|
|
3
|
+
* One module-relative file, read once. The compiled layout carries an exact copy at
|
|
4
|
+
* dist/ENTWURF.json; no cwd/HOME search, override, old-text fallback or hot reload.
|
|
5
|
+
* Existing operator engraving and user-configuration contracts are unrelated.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
const TOOL_SPEC = {
|
|
9
|
+
entwurf_v2: { durable: true, parameters: ["target", "intent", "message", "mode", "wants_reply"] },
|
|
10
|
+
entwurf_self: { durable: true, parameters: [] },
|
|
11
|
+
entwurf_peers: { durable: true, parameters: [] },
|
|
12
|
+
entwurf_inbox_read: { durable: true, parameters: ["gardenId"] },
|
|
13
|
+
entwurf_register_native: { durable: false, parameters: ["backend", "nativeSessionId", "cwd"] },
|
|
14
|
+
entwurf_fresh_call: {
|
|
15
|
+
durable: true,
|
|
16
|
+
parameters: ["backend", "model", "task", "cwd", "placement.tmuxSession", "placement"],
|
|
17
|
+
},
|
|
18
|
+
entwurf_resume_call: { durable: true, parameters: ["target"] },
|
|
19
|
+
entwurf_retire_call: { durable: true, parameters: ["target"] },
|
|
20
|
+
entwurf_callback: { durable: true, parameters: [] },
|
|
21
|
+
};
|
|
22
|
+
/** Frozen entwurf_v2 reject wire ids whose static next-step hint text lives in the root file. */
|
|
23
|
+
const REJECT_HINT_REASONS = [
|
|
24
|
+
"dormant-fire-forget-unsupported",
|
|
25
|
+
"indeterminate-no-spawn",
|
|
26
|
+
"native-push-target-dead",
|
|
27
|
+
"native-push-probe-indeterminate",
|
|
28
|
+
"native-push-paused",
|
|
29
|
+
];
|
|
30
|
+
/** Frozen entwurf_resume_call record/liveness reject wire ids whose static hint text lives in the root file. */
|
|
31
|
+
const RESUME_REJECT_HINT_REASONS = [
|
|
32
|
+
"target-invalid",
|
|
33
|
+
"target-backend-unsupported",
|
|
34
|
+
"target-locked",
|
|
35
|
+
"target-live",
|
|
36
|
+
"target-indeterminate",
|
|
37
|
+
"target-receiver-absent",
|
|
38
|
+
"target-receiver-corrupt",
|
|
39
|
+
"target-receiver-mismatch",
|
|
40
|
+
"target-receiver-unbound",
|
|
41
|
+
"target-address-conflict",
|
|
42
|
+
"launch-refused",
|
|
43
|
+
];
|
|
44
|
+
/** Frozen entwurf_fresh_call tmux launch-stage reject wire ids whose static hint text lives in the root file. */
|
|
45
|
+
const FRESH_LAUNCH_REJECT_HINT_REASONS = [
|
|
46
|
+
"no-tmux-context",
|
|
47
|
+
"anchor-malformed",
|
|
48
|
+
"anchor-unresolved",
|
|
49
|
+
"anchor-mismatch",
|
|
50
|
+
"caller-identity-unavailable",
|
|
51
|
+
"cwd-not-absolute",
|
|
52
|
+
"cwd-format-token",
|
|
53
|
+
"cwd-missing",
|
|
54
|
+
"cwd-not-directory",
|
|
55
|
+
"tmux-session-name-invalid",
|
|
56
|
+
"tmux-session-missing",
|
|
57
|
+
"model-empty",
|
|
58
|
+
"task-empty",
|
|
59
|
+
"runtime-unresolved",
|
|
60
|
+
"runtime-not-absolute",
|
|
61
|
+
"runtime-path-whitespace",
|
|
62
|
+
"runtime-missing",
|
|
63
|
+
"runtime-not-regular-file",
|
|
64
|
+
"runtime-not-executable",
|
|
65
|
+
];
|
|
66
|
+
/** Frozen entwurf_resume_call tmux launch-stage reject wire ids whose static hint text lives in the root file. */
|
|
67
|
+
const RESUME_LAUNCH_REJECT_HINT_REASONS = [
|
|
68
|
+
"no-tmux-context",
|
|
69
|
+
"anchor-malformed",
|
|
70
|
+
"anchor-unresolved",
|
|
71
|
+
"anchor-mismatch",
|
|
72
|
+
"cwd-not-absolute",
|
|
73
|
+
"cwd-format-token",
|
|
74
|
+
"cwd-missing",
|
|
75
|
+
"cwd-not-directory",
|
|
76
|
+
"herdr-resume-unsupported",
|
|
77
|
+
"runtime-unresolved",
|
|
78
|
+
"runtime-not-absolute",
|
|
79
|
+
"runtime-path-whitespace",
|
|
80
|
+
"runtime-missing",
|
|
81
|
+
"runtime-not-regular-file",
|
|
82
|
+
"runtime-not-executable",
|
|
83
|
+
];
|
|
84
|
+
/** Frozen keys of the static delivery-result clauses whose text lives in the root file; the
|
|
85
|
+
* receipts around them (syntax, branch conditions, slots, punctuation) stay code-owned. */
|
|
86
|
+
const DELIVERY_RESULT_CLAUSE_KEYS = [
|
|
87
|
+
"finalized-rejected",
|
|
88
|
+
"dirty-lock",
|
|
89
|
+
"remote-accepted",
|
|
90
|
+
"remote-reported-error",
|
|
91
|
+
"remote-indeterminate",
|
|
92
|
+
];
|
|
93
|
+
/** Frozen wire ids of the reject hints whose authored text sits on either side of ONE code-owned
|
|
94
|
+
* slot, and each one's exact segment keys. The slot value, the reason branch and the
|
|
95
|
+
* concatenation stay code-owned; the root file holds only the text around the slot. */
|
|
96
|
+
const SLOTTED_REJECT_HINT_SEGMENTS = {
|
|
97
|
+
"record-less-socket": ["beforeFreshCutPrescription", "afterFreshCutPrescription"],
|
|
98
|
+
};
|
|
99
|
+
function record(value, at) {
|
|
100
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
101
|
+
throw new Error(`ENTWURF.json ${at}: expected an object`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function keys(value, expected, at) {
|
|
105
|
+
const actual = Object.keys(value).sort();
|
|
106
|
+
const wanted = [...expected].sort();
|
|
107
|
+
if (actual.length !== wanted.length || actual.some((key, i) => key !== wanted[i])) {
|
|
108
|
+
throw new Error(`ENTWURF.json ${at}: keys must be exactly ${wanted.join(", ")}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function text(value, at) {
|
|
112
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
113
|
+
throw new Error(`ENTWURF.json ${at}: expected a non-empty string`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* JSON.parse owns strict grammar, but its last-key-wins value is never enough.
|
|
118
|
+
* Walk the ORIGINAL, already grammar-checked tokens and reject duplicate DECODED
|
|
119
|
+
* object keys before returning data. Object scopes are independent; strings that
|
|
120
|
+
* are values (including punctuation and escaped quotes) are never key tokens.
|
|
121
|
+
*/
|
|
122
|
+
function rejectDuplicateKeys(source) {
|
|
123
|
+
let pos = 0;
|
|
124
|
+
const space = () => {
|
|
125
|
+
while (pos < source.length && /[\x20\t\r\n]/.test(source[pos]))
|
|
126
|
+
pos++;
|
|
127
|
+
};
|
|
128
|
+
const string = () => {
|
|
129
|
+
const start = pos++;
|
|
130
|
+
while (pos < source.length) {
|
|
131
|
+
if (source[pos] === "\\")
|
|
132
|
+
pos += 2;
|
|
133
|
+
else if (source[pos++] === '"')
|
|
134
|
+
return JSON.parse(source.slice(start, pos));
|
|
135
|
+
}
|
|
136
|
+
throw new Error("ENTWURF.json: unterminated string");
|
|
137
|
+
};
|
|
138
|
+
const value = () => {
|
|
139
|
+
space();
|
|
140
|
+
if (source[pos] === "{") {
|
|
141
|
+
pos++;
|
|
142
|
+
space();
|
|
143
|
+
const seen = new Set();
|
|
144
|
+
if (source[pos] !== "}") {
|
|
145
|
+
for (;;) {
|
|
146
|
+
space();
|
|
147
|
+
const key = string();
|
|
148
|
+
if (seen.has(key))
|
|
149
|
+
throw new Error(`ENTWURF.json: duplicate object key ${JSON.stringify(key)}`);
|
|
150
|
+
seen.add(key);
|
|
151
|
+
space();
|
|
152
|
+
pos++; // colon; JSON.parse already certified the grammar
|
|
153
|
+
value();
|
|
154
|
+
space();
|
|
155
|
+
if (source[pos] !== ",")
|
|
156
|
+
break;
|
|
157
|
+
pos++;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
pos++;
|
|
161
|
+
}
|
|
162
|
+
else if (source[pos] === "[") {
|
|
163
|
+
pos++;
|
|
164
|
+
space();
|
|
165
|
+
if (source[pos] !== "]") {
|
|
166
|
+
for (;;) {
|
|
167
|
+
value();
|
|
168
|
+
space();
|
|
169
|
+
if (source[pos] !== ",")
|
|
170
|
+
break;
|
|
171
|
+
pos++;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
pos++;
|
|
175
|
+
}
|
|
176
|
+
else if (source[pos] === '"') {
|
|
177
|
+
string();
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
while (pos < source.length && !/[\x20\t\r\n,}\]]/.test(source[pos]))
|
|
181
|
+
pos++;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
value();
|
|
185
|
+
}
|
|
186
|
+
function freeze(value) {
|
|
187
|
+
if (value === null || typeof value !== "object")
|
|
188
|
+
return;
|
|
189
|
+
for (const child of Object.values(value))
|
|
190
|
+
freeze(child);
|
|
191
|
+
Object.freeze(value);
|
|
192
|
+
}
|
|
193
|
+
/** Pure data-validation seam; production always reads the one fixed module URL below. */
|
|
194
|
+
export function parseEntwurfGuidance(source) {
|
|
195
|
+
const data = JSON.parse(source);
|
|
196
|
+
rejectDuplicateKeys(source);
|
|
197
|
+
record(data, "root");
|
|
198
|
+
if (data.schemaVersion !== 6)
|
|
199
|
+
throw new Error("ENTWURF.json: unsupported schemaVersion");
|
|
200
|
+
keys(data, [
|
|
201
|
+
"_about",
|
|
202
|
+
"schemaVersion",
|
|
203
|
+
"revision",
|
|
204
|
+
"history",
|
|
205
|
+
"tools",
|
|
206
|
+
"rejectHints",
|
|
207
|
+
"resumeRejectHints",
|
|
208
|
+
"freshLaunchRejectHints",
|
|
209
|
+
"resumeLaunchRejectHints",
|
|
210
|
+
"deliveryResultClauses",
|
|
211
|
+
"slottedRejectHints",
|
|
212
|
+
], "root");
|
|
213
|
+
if (!Number.isSafeInteger(data.revision) || data.revision < 1) {
|
|
214
|
+
throw new Error("ENTWURF.json: revision must be a positive safe integer");
|
|
215
|
+
}
|
|
216
|
+
record(data._about, "_about");
|
|
217
|
+
keys(data._about, ["purpose", "ownership", "scope", "editing"], "_about");
|
|
218
|
+
for (const [key, value] of Object.entries(data._about))
|
|
219
|
+
text(value, `_about.${key}`);
|
|
220
|
+
if (!Array.isArray(data.history) || data.history.length === 0) {
|
|
221
|
+
throw new Error("ENTWURF.json: history must be a non-empty array");
|
|
222
|
+
}
|
|
223
|
+
for (const entry of data.history) {
|
|
224
|
+
record(entry, "history entry");
|
|
225
|
+
keys(entry, ["revision", "source", "issue", "change"], "history entry");
|
|
226
|
+
if (!Number.isSafeInteger(entry.revision) ||
|
|
227
|
+
entry.revision < 1 ||
|
|
228
|
+
entry.revision > data.revision) {
|
|
229
|
+
throw new Error("ENTWURF.json: history revision must name an existing payload revision");
|
|
230
|
+
}
|
|
231
|
+
for (const key of ["source", "issue", "change"])
|
|
232
|
+
text(entry[key], `history.${key}`);
|
|
233
|
+
}
|
|
234
|
+
record(data.tools, "tools");
|
|
235
|
+
keys(data.tools, Object.keys(TOOL_SPEC), "tools");
|
|
236
|
+
for (const name of Object.keys(TOOL_SPEC)) {
|
|
237
|
+
const tool = data.tools[name];
|
|
238
|
+
record(tool, name);
|
|
239
|
+
keys(tool, ["descriptions", "parameters"], name);
|
|
240
|
+
record(tool.descriptions, `${name}.descriptions`);
|
|
241
|
+
keys(tool.descriptions, TOOL_SPEC[name].durable ? ["mcp", "durable"] : ["mcp"], `${name}.descriptions`);
|
|
242
|
+
for (const [rail, value] of Object.entries(tool.descriptions))
|
|
243
|
+
text(value, `${name}.descriptions.${rail}`);
|
|
244
|
+
record(tool.parameters, `${name}.parameters`);
|
|
245
|
+
keys(tool.parameters, TOOL_SPEC[name].parameters, `${name}.parameters`);
|
|
246
|
+
for (const [field, value] of Object.entries(tool.parameters))
|
|
247
|
+
text(value, `${name}.parameters.${field}`);
|
|
248
|
+
}
|
|
249
|
+
record(data.rejectHints, "rejectHints");
|
|
250
|
+
keys(data.rejectHints, REJECT_HINT_REASONS, "rejectHints");
|
|
251
|
+
for (const [reason, value] of Object.entries(data.rejectHints))
|
|
252
|
+
text(value, `rejectHints.${reason}`);
|
|
253
|
+
record(data.resumeRejectHints, "resumeRejectHints");
|
|
254
|
+
keys(data.resumeRejectHints, RESUME_REJECT_HINT_REASONS, "resumeRejectHints");
|
|
255
|
+
for (const [reason, value] of Object.entries(data.resumeRejectHints))
|
|
256
|
+
text(value, `resumeRejectHints.${reason}`);
|
|
257
|
+
record(data.freshLaunchRejectHints, "freshLaunchRejectHints");
|
|
258
|
+
keys(data.freshLaunchRejectHints, FRESH_LAUNCH_REJECT_HINT_REASONS, "freshLaunchRejectHints");
|
|
259
|
+
for (const [reason, value] of Object.entries(data.freshLaunchRejectHints))
|
|
260
|
+
text(value, `freshLaunchRejectHints.${reason}`);
|
|
261
|
+
record(data.resumeLaunchRejectHints, "resumeLaunchRejectHints");
|
|
262
|
+
keys(data.resumeLaunchRejectHints, RESUME_LAUNCH_REJECT_HINT_REASONS, "resumeLaunchRejectHints");
|
|
263
|
+
for (const [reason, value] of Object.entries(data.resumeLaunchRejectHints))
|
|
264
|
+
text(value, `resumeLaunchRejectHints.${reason}`);
|
|
265
|
+
record(data.deliveryResultClauses, "deliveryResultClauses");
|
|
266
|
+
keys(data.deliveryResultClauses, DELIVERY_RESULT_CLAUSE_KEYS, "deliveryResultClauses");
|
|
267
|
+
for (const [key, value] of Object.entries(data.deliveryResultClauses))
|
|
268
|
+
text(value, `deliveryResultClauses.${key}`);
|
|
269
|
+
record(data.slottedRejectHints, "slottedRejectHints");
|
|
270
|
+
keys(data.slottedRejectHints, Object.keys(SLOTTED_REJECT_HINT_SEGMENTS), "slottedRejectHints");
|
|
271
|
+
for (const reason of Object.keys(SLOTTED_REJECT_HINT_SEGMENTS)) {
|
|
272
|
+
const segments = data.slottedRejectHints[reason];
|
|
273
|
+
record(segments, `slottedRejectHints.${reason}`);
|
|
274
|
+
keys(segments, SLOTTED_REJECT_HINT_SEGMENTS[reason], `slottedRejectHints.${reason}`);
|
|
275
|
+
for (const [key, value] of Object.entries(segments))
|
|
276
|
+
text(value, `slottedRejectHints.${reason}.${key}`);
|
|
277
|
+
}
|
|
278
|
+
freeze(data);
|
|
279
|
+
return data;
|
|
280
|
+
}
|
|
281
|
+
const guidance = parseEntwurfGuidance(readFileSync(new URL("../../ENTWURF.json", import.meta.url), "utf8"));
|
|
282
|
+
/** Only these exact payload strings enter tool schemas; _about/history never do. */
|
|
283
|
+
export const ENTWURF_TOOL_GUIDANCE = guidance.tools;
|
|
284
|
+
/** Static next-step text appended to these entwurf_v2 rejections; the decider and wire ids stay code-owned. */
|
|
285
|
+
export const ENTWURF_REJECT_HINTS = guidance.rejectHints;
|
|
286
|
+
/** Static next-step text in these entwurf_resume_call record/liveness rejections; the verb and the shared ACP model hint stay code-owned. */
|
|
287
|
+
export const ENTWURF_RESUME_REJECT_HINTS = guidance.resumeRejectHints;
|
|
288
|
+
/** Static tmux launch-stage hints of entwurf_fresh_call; slot-bearing, ACP and preflight-leaf hints stay code-owned. */
|
|
289
|
+
export const ENTWURF_FRESH_LAUNCH_REJECT_HINTS = guidance.freshLaunchRejectHints;
|
|
290
|
+
/** Static tmux launch-stage hints of entwurf_resume_call, kept apart from the fresh verb even where the text matches. */
|
|
291
|
+
export const ENTWURF_RESUME_LAUNCH_REJECT_HINTS = guidance.resumeLaunchRejectHints;
|
|
292
|
+
/** Static delivery-result clauses of the entwurf_v2 dirty-lock and remote-pair receipts; the receipt syntax around them stays code-owned. */
|
|
293
|
+
export const ENTWURF_DELIVERY_RESULT_CLAUSES = guidance.deliveryResultClauses;
|
|
294
|
+
/** Text on either side of one code-owned slot per reject reason; the slot value and the concatenation stay with the consumer. */
|
|
295
|
+
export const ENTWURF_SLOTTED_REJECT_HINTS = guidance.slottedRejectHints;
|
|
296
|
+
export const ENTWURF_GUIDANCE_VERSION = Object.freeze({
|
|
297
|
+
schemaVersion: guidance.schemaVersion,
|
|
298
|
+
revision: guidance.revision,
|
|
299
|
+
});
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* surface never needs to name the `EntwurfV2RunResult` union (it only sees `{text,isError}`).
|
|
18
18
|
*/
|
|
19
19
|
import { basename } from "node:path";
|
|
20
|
+
import { ENTWURF_DELIVERY_RESULT_CLAUSES, ENTWURF_REJECT_HINTS, ENTWURF_SLOTTED_REJECT_HINTS, } from "./agent-guidance.js";
|
|
20
21
|
import { makeProductionEntwurfV2Deps } from "./entwurf-v2-production.js";
|
|
21
22
|
import { runEntwurfV2 } from "./entwurf-v2-runner.js";
|
|
22
23
|
import { FRESH_CUT_PRESCRIPTION } from "./meta-session.js";
|
|
@@ -36,7 +37,9 @@ export function toDispatchInput(params) {
|
|
|
36
37
|
* Detour B (B-a) — actionable rendering of an honest reject. The decider is UNCHANGED:
|
|
37
38
|
* a reject stays a reject (Hard Rule 3). This only appends a one-line "what to do instead"
|
|
38
39
|
* to the reject TEXT, so an honest reject stops reading as "delivery impossible". Returns
|
|
39
|
-
* undefined for rejects with no useful next step.
|
|
40
|
+
* undefined for rejects with no useful next step. Hint English is authored in root ENTWURF.json
|
|
41
|
+
* (#154); the reason switch stays here, and so do the record-less-socket slot value and its
|
|
42
|
+
* concatenation (#154 E1b).
|
|
40
43
|
*/
|
|
41
44
|
export function actionableRejectHint(reason) {
|
|
42
45
|
switch (reason) {
|
|
@@ -45,11 +48,7 @@ export function actionableRejectHint(reason) {
|
|
|
45
48
|
// state — the citizen exists and is not running — matters more than ever now that
|
|
46
49
|
// there is no verb behind it: a caller that reads "reject" as "wrong id" goes
|
|
47
50
|
// looking in the wrong place.
|
|
48
|
-
return
|
|
49
|
-
"The resume that used to answer here launched a hidden background child and was withdrawn under the " +
|
|
50
|
-
"visible-first rule; the visible same-id resume is the separate lifecycle verb entwurf_resume_call. " +
|
|
51
|
-
"Only on an explicit operator request, use entwurf_resume_call {target} for Pi or Claude Code; " +
|
|
52
|
-
"it submits no prompt. This capability hint is not permission to resume automatically.");
|
|
51
|
+
return ENTWURF_REJECT_HINTS["dormant-fire-forget-unsupported"];
|
|
53
52
|
case "indeterminate-no-spawn":
|
|
54
53
|
// The socket probe did not settle, so liveness is UNKNOWN — an unestablished probe
|
|
55
54
|
// is not a measured death, and dispatching into it could double-deliver. Say
|
|
@@ -60,31 +59,27 @@ export function actionableRejectHint(reason) {
|
|
|
60
59
|
// (never dispatch into an indeterminate target), not a capability that still
|
|
61
60
|
// exists. So the hint's job is to say plainly what did NOT happen: nothing
|
|
62
61
|
// delivered, nothing started.
|
|
63
|
-
return
|
|
64
|
-
"measured death. NOTHING was delivered and NO process was started (the reason id keeps its frozen " +
|
|
65
|
-
"'-no-spawn' wire spelling from an era when one could be; entwurf_v2 starts nothing on any rail). " +
|
|
66
|
-
"Re-run entwurf_peers to re-probe; if it stays indeterminate, check for a stale socket file left at " +
|
|
67
|
-
"that garden id by a session that died without cleaning up.");
|
|
62
|
+
return ENTWURF_REJECT_HINTS["indeterminate-no-spawn"];
|
|
68
63
|
case "native-push-target-dead":
|
|
69
64
|
// The adapter probe found no live native host serving this conversation/thread. Stays
|
|
70
65
|
// backend-neutral: agy speaks conversations over an LS port, Codex threads over an
|
|
71
66
|
// app-server socket, and this hint is read by callers of both.
|
|
72
|
-
return "
|
|
67
|
+
return ENTWURF_REJECT_HINTS["native-push-target-dead"];
|
|
73
68
|
case "native-push-probe-indeterminate":
|
|
74
69
|
// Native host up, but nothing served this conversation/thread — inconclusive, not a
|
|
75
70
|
// hard dead. Backend-neutral for the same reason as the dead cell above.
|
|
76
|
-
return "
|
|
71
|
+
return ENTWURF_REJECT_HINTS["native-push-probe-indeterminate"];
|
|
77
72
|
case "native-push-paused":
|
|
78
73
|
// #152: a policy refusal, not a liveness verdict — this dispatch probed and sent nothing.
|
|
79
|
-
return "native-push
|
|
80
|
-
case "record-less-socket":
|
|
74
|
+
return ENTWURF_REJECT_HINTS["native-push-paused"];
|
|
75
|
+
case "record-less-socket": {
|
|
81
76
|
// #50 C4: name the true cause AND the fix — a bare socket is a diagnostic
|
|
82
|
-
// state, not an addressable citizen (the record is the address).
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
77
|
+
// state, not an addressable citizen (the record is the address). The text on either
|
|
78
|
+
// side of the fix command is authored in root ENTWURF.json; the command itself stays
|
|
79
|
+
// with its owner (meta-session.ts), so the root file never copies it.
|
|
80
|
+
const hint = ENTWURF_SLOTTED_REJECT_HINTS["record-less-socket"];
|
|
81
|
+
return `${hint.beforeFreshCutPrescription}${FRESH_CUT_PRESCRIPTION}${hint.afterFreshCutPrescription}`;
|
|
82
|
+
}
|
|
88
83
|
default:
|
|
89
84
|
return undefined;
|
|
90
85
|
}
|
|
@@ -185,13 +180,14 @@ export function renderEntwurfV2Result(result) {
|
|
|
185
180
|
// the same acceptance vocabulary the ordinary receipt uses is named here too — a
|
|
186
181
|
// dirty lock is a reason to be MORE precise about what happened, not less, because
|
|
187
182
|
// the operator is about to decide by hand whether anything needs re-sending.
|
|
183
|
+
// The two static clauses are authored in root ENTWURF.json (#154 E1a); this receipt's
|
|
184
|
+
// syntax, branches and slots stay here.
|
|
188
185
|
const what = result.finalizedOutcome === "rejected"
|
|
189
|
-
? `rejected${result.finalizedRejectReason ? ` (${result.finalizedRejectReason})` : ""} —
|
|
186
|
+
? `rejected${result.finalizedRejectReason ? ` (${result.finalizedRejectReason})` : ""} — ${ENTWURF_DELIVERY_RESULT_CLAUSES["finalized-rejected"]}`
|
|
190
187
|
: `${result.finalizedBoundary ?? "mailbox-enqueued"}${result.finalizedMessagePath ? ` (enqueued ${basename(result.finalizedMessagePath)})` : ""}`;
|
|
191
188
|
return {
|
|
192
|
-
text: `entwurf_v2 ${result.transport} finalized as ${what}, and
|
|
193
|
-
|
|
194
|
-
`\n error: ${result.error}`,
|
|
189
|
+
text: `entwurf_v2 ${result.transport} finalized as ${what}, and ` +
|
|
190
|
+
`${ENTWURF_DELIVERY_RESULT_CLAUSES["dirty-lock"]}\n error: ${result.error}`,
|
|
195
191
|
isError: true,
|
|
196
192
|
};
|
|
197
193
|
}
|