@junghanacs/entwurf 0.41.1 → 0.45.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 +2 -1
- package/BASELINE.md +7 -5
- package/CHANGELOG.md +344 -0
- package/DELIVERY.md +37 -20
- package/ENTWURF.json +236 -0
- package/README.md +75 -65
- package/VERIFY.md +9 -9
- package/docs/external-mcp-host.md +1 -1
- package/docs/setup-clean-host.md +59 -33
- package/mcp/entwurf-bridge/dist/ENTWURF.json +236 -0
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +32 -190
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/agent-guidance.js +299 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +10 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +23 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +23 -24
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +15 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +21 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +4 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +5 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +29 -24
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +18 -16
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/register.js +13 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/omp-fresh-preflight.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-receive.js +25 -17
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-send.js +101 -32
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js +57 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +23 -45
- package/mcp/entwurf-bridge/dist/scripts/remote-pair-receive.js +12 -12
- package/mcp/entwurf-bridge/src/index.ts +126 -321
- package/native/win32-x64/entwurf_win.node +0 -0
- package/native/win32-x64/manifest.json +2 -2
- package/package.json +5 -4
- package/pi-extensions/lib/agent-guidance.ts +341 -0
- package/pi-extensions/lib/entwurf-self-address.ts +13 -0
- package/pi-extensions/lib/entwurf-v2-contract.ts +25 -2
- package/pi-extensions/lib/entwurf-v2-decider.ts +7 -0
- package/pi-extensions/lib/entwurf-v2-surface.ts +27 -30
- package/pi-extensions/lib/entwurf-v2-visible-resume.ts +15 -16
- package/pi-extensions/lib/fresh-call-composition.ts +21 -10
- package/pi-extensions/lib/herdr-fresh-call.ts +4 -2
- package/pi-extensions/lib/meta-sender-identity.ts +4 -0
- package/pi-extensions/lib/mux-fresh-call.ts +29 -30
- package/pi-extensions/lib/mux-resume-call.ts +18 -19
- package/pi-extensions/lib/native-push/register.ts +17 -2
- package/pi-extensions/lib/omp-fresh-preflight.ts +2 -2
- package/pi-extensions/lib/pi-durable-fresh-preflight.ts +1 -1
- package/pi-extensions/lib/remote-pair-receive.ts +27 -19
- package/pi-extensions/lib/remote-pair-send.ts +124 -34
- package/pi-extensions/meta-bridge-omp.ts +69 -7
- package/pi-extensions/meta-bridge-pi-durable.ts +23 -64
- package/run.sh +182 -22
- package/scripts/build-bridge.sh +10 -0
- package/scripts/check-agy-sender-identity.ts +6 -4
- package/scripts/check-capability-bundle-reach.ts +86 -0
- package/scripts/check-entwurf-self-address.ts +37 -6
- package/scripts/check-entwurf-v2-contract.ts +24 -2
- package/scripts/check-entwurf-v2-production.ts +45 -31
- package/scripts/check-entwurf-v2-surface.ts +94 -98
- package/scripts/check-gate-qualification.ts +17 -13
- package/scripts/check-mux-resume-call.ts +20 -3
- package/scripts/check-native-push-adapter.ts +4 -0
- package/scripts/check-native-push-register.ts +32 -82
- package/scripts/check-remote-pair-installed.ts +2 -2
- package/scripts/dev-bin.sh +112 -1
- package/scripts/lib/chain-terminus-publication.ts +58 -0
- package/scripts/lib/codex-fresh-live-protocol.ts +30 -0
- package/scripts/lib/codex-fresh-source-receipts.ts +112 -3
- package/scripts/lib/pi-durable-fresh-live-receipts.ts +29 -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/chain-terminus-publication.json +17 -0
- package/scripts/mutants/codex-native.json +98 -0
- package/scripts/mutants/control-send-receipt.json +23 -6
- package/scripts/mutants/mux-fresh-call.json +30 -4
- package/scripts/mutants/mux-resume-call.json +28 -3
- package/scripts/mutants/native-push-paused.json +17 -0
- package/scripts/mutants/omp-fresh.json +13 -2
- package/scripts/mutants/pack-install.json +46 -0
- package/scripts/mutants/pi-durable.json +17 -5
- package/scripts/mutants/remote-pair-display.json +3 -3
- package/scripts/mutants/remote-pair-receive.json +41 -11
- package/scripts/mutants/remote-pair-roundtrip.json +67 -7
- package/scripts/mutants/remote-pair-send.json +177 -19
- package/scripts/mutants/remote-pair-transport.json +4 -4
- package/scripts/mutants/self-address.json +10 -10
- package/scripts/mutants/source-install.json +66 -0
- package/scripts/mutants/v2-surface.json +50 -64
- package/scripts/mutants/v2-visible-resume.json +11 -0
- package/scripts/remote-pair-receive.ts +12 -12
- package/scripts/smoke-agy-install-state.sh +121 -0
- package/scripts/smoke-agy-native-push-live.ts +13 -0
- package/scripts/smoke-codex-fresh-live.ts +50 -27
- package/scripts/smoke-entwurf-chain-live.ts +7 -9
- package/scripts/smoke-herdr-fresh-call-live.ts +4 -4
- package/scripts/smoke-herdr-plugin-build-live.ts +179 -50
- package/scripts/smoke-mux-lifecycle-live.ts +140 -8
- package/scripts/smoke-omp-fresh-live.ts +5 -4
- package/scripts/smoke-pi-durable-fresh-live.ts +29 -7
- package/scripts/smoke-setup-verdict.sh +27 -2
|
@@ -75,6 +75,16 @@ export function computeSelfAddressability(facts) {
|
|
|
75
75
|
// default fail-closed row).
|
|
76
76
|
switch (facts.metaDeliveryDomain) {
|
|
77
77
|
case "native-push": {
|
|
78
|
+
// #152 policy, decided BEFORE the pure record+probe predicate and kept out of it:
|
|
79
|
+
// only a record-backed citizen can be "paused" (a record-less one keeps its own
|
|
80
|
+
// reason), and no probe was run for this answer, so none is reported.
|
|
81
|
+
if (facts.recordBacked === true && facts.nativePushPaused === true) {
|
|
82
|
+
return {
|
|
83
|
+
replyable: false,
|
|
84
|
+
socketState: "none",
|
|
85
|
+
reason: "native-push unreachable — native-push for this backend is paused (#152); no probe ran for this answer",
|
|
86
|
+
};
|
|
87
|
+
}
|
|
78
88
|
const push = nativePushDeliverable({ recordBacked: facts.recordBacked, probeAlive: facts.probeAlive });
|
|
79
89
|
return {
|
|
80
90
|
replyable: push.deliverable,
|
|
@@ -119,6 +119,24 @@ export const NATIVE_PUSH_BACKENDS = ["antigravity", "codex"];
|
|
|
119
119
|
export function nativePushSupported(backend) {
|
|
120
120
|
return NATIVE_PUSH_BACKENDS.includes(backend);
|
|
121
121
|
}
|
|
122
|
+
// ── Paused native-push backends (#152) ─────────────────────────────────────
|
|
123
|
+
// A native-push backend whose SEND is paused by product policy. On the external send path
|
|
124
|
+
// entwurf measured, Antigravity's language server refused the `agentapi` call with
|
|
125
|
+
// `Unauthenticated: missing CSRF token` (agy 1.2.16 in #128; agy 1.4.6 again for #152).
|
|
126
|
+
// Whether a first-party own-tool shell holds a token entwurf could legitimately present is
|
|
127
|
+
// UNMEASURED, not ruled out. So instead of probing a port that refused on that path and
|
|
128
|
+
// reporting it as an inconclusive liveness, the three public entries that would reach the agy LS —
|
|
129
|
+
// v2 dispatch, a sender's own `replyable`, and `entwurf_register_native` — refuse by name
|
|
130
|
+
// BEFORE any probe. This is a fixed constant, not a flag: no env, config or runtime toggle
|
|
131
|
+
// can lift it. The backend stays in NATIVE_PUSH_BACKENDS, so existing agy records, the V3
|
|
132
|
+
// reader and the peers listing are unchanged; only the native-push traffic stops.
|
|
133
|
+
// The one probe that is NOT behind this guard is the fresh-cut quiescence check
|
|
134
|
+
// (scripts/meta-bridge-fresh-cut.ts), which protects an operator-named archive and must keep
|
|
135
|
+
// failing closed rather than read the pause as a departed host.
|
|
136
|
+
export const NATIVE_PUSH_PAUSED_BACKENDS = ["antigravity"];
|
|
137
|
+
export function nativePushPaused(backend) {
|
|
138
|
+
return NATIVE_PUSH_PAUSED_BACKENDS.includes(backend);
|
|
139
|
+
}
|
|
122
140
|
/**
|
|
123
141
|
* Compose the 4-value FACT liveness from a backend and its socket probe.
|
|
124
142
|
* Out-of-domain backend → `unsupported` (NOT dead/indeterminate, R1). An
|
|
@@ -148,18 +166,20 @@ export const ENTWURF_V2_REJECT_REASONS = [
|
|
|
148
166
|
"mailbox-undeliverable", // F-mailbox: fire-and-forget to an unsupported citizen whose mailbox is not deliverable (fail-closed; future pi-backend non-drainable mailbox)
|
|
149
167
|
"native-push-target-dead", // 봉인 1: fire-and-forget to a native-push (agy) target whose adapter probe found NO live conversation. Post-probe; observedLiveness = dead.
|
|
150
168
|
"native-push-probe-indeterminate", // 봉인 1: fire-and-forget to a native-push target whose adapter probe was inconclusive (agy alive but no port served the conv, or a probe error). Post-probe; observedLiveness = indeterminate. Never coerced to dead.
|
|
169
|
+
"native-push-paused", // #152: fire-and-forget to a native-push backend whose send is paused by product policy (NATIVE_PUSH_PAUSED_BACKENDS — antigravity). Pre-probe, observedLiveness = null: nothing was probed, so no liveness is claimed — never coerced to dead or indeterminate.
|
|
151
170
|
"bad-target", // R2: absent/typo garden-id (no existing citizen); fresh creation out of v2 scope
|
|
152
171
|
"record-less-socket", // #50 C4: a gid-shaped non-symlink control socket exists but NO meta-record claims it. The record is the sole address authority (목표 ②), so a bare socket is not an addressable citizen — it is a diagnostic state, refused for EVERY intent before any lock/probe (pre-probe, observedLiveness=null). NOT `bad-target`: something real answers to this gid, and mislabeling it absent would hide the very state the reject exists to surface.
|
|
153
172
|
"target-locked", // R5 pre-claim for bucket B F2 per-gid lockfile conflict
|
|
154
173
|
"target-address-conflict", // F3: a quarantined citizen (garden-id-socket-conflict / symlinked socket) — the gid resolves to two different receivers (record vs socket), so dispatch refuses to pick. The ONLY in-band honest channel for a dispatch-level identity-split (the listing diagnostic channel is not visible to a v2 caller, who only gets a receipt). Pre-resolver, like bad-target/target-locked — NOT a RESOLVER_REJECT_REASONS member.
|
|
155
174
|
];
|
|
156
175
|
// ── Pre-probe reject reasons (?6 — observedLiveness = null) ────────────────
|
|
157
|
-
// These
|
|
176
|
+
// These five rejects are decided BEFORE any liveness probe runs, so there is no
|
|
158
177
|
// honest 4-value FactLiveness to stamp: `bad-target` (no citizen/backend),
|
|
159
178
|
// `target-locked` (5a lock conflict, before lstat/connect), `target-address-conflict`
|
|
160
179
|
// (address-subject conflict → probing is forbidden), `record-less-socket` (#50 C4:
|
|
161
180
|
// no record ⇒ not a citizen ⇒ no in-domain probe ever runs — the presence hint is
|
|
162
|
-
// a single lstat, not a liveness measurement)
|
|
181
|
+
// a single lstat, not a liveness measurement), `native-push-paused` (#152: the backend's
|
|
182
|
+
// native-push send is paused by policy, so its adapter is never probed). `indeterminate` means an
|
|
163
183
|
// in-domain probe was inconclusive (≠ "not looked yet"); `unsupported` means the
|
|
164
184
|
// backend has no predicate (≠ "pre-probe"). So a pre-probe reject's
|
|
165
185
|
// observedLiveness is `null`, NOT one of the four values. Every OTHER reject —
|
|
@@ -176,6 +196,7 @@ export const PRE_PROBE_REJECT_REASONS = [
|
|
|
176
196
|
"target-locked",
|
|
177
197
|
"target-address-conflict",
|
|
178
198
|
"record-less-socket",
|
|
199
|
+
"native-push-paused",
|
|
179
200
|
];
|
|
180
201
|
export function isPreProbeReject(reason) {
|
|
181
202
|
return PRE_PROBE_REJECT_REASONS.includes(reason);
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
* be bypassed.
|
|
45
45
|
*/
|
|
46
46
|
import { isOutOfSocketDomainGardenIdConflict } from "./entwurf-facts.js";
|
|
47
|
-
import { isLivenessSupported, makeRejectReceipt, nativePushSupported, resolveDispatch, resolveNativePushDispatch, } from "./entwurf-v2-contract.js";
|
|
47
|
+
import { isLivenessSupported, makeRejectReceipt, nativePushPaused, nativePushSupported, resolveDispatch, resolveNativePushDispatch, } from "./entwurf-v2-contract.js";
|
|
48
48
|
import { defaultMetaMailboxDir, defaultMetaSessionsDir, metaCapabilityFor, } from "./meta-session.js";
|
|
49
49
|
import { isValidSessionId } from "./session-id.js";
|
|
50
50
|
import { controlSocketPath, mapInspectionToLiveness } from "./socket-discovery.js";
|
|
@@ -121,6 +121,12 @@ export async function decideDispatch(input, deps) {
|
|
|
121
121
|
// branch is LOCK-FREE (봉인 4): the pi in-domain lock closes a socket TOCTOU, but a
|
|
122
122
|
// volatile probe route has no lock meaning (a duplicate-send idempotency is a D8 future).
|
|
123
123
|
if (nativePushSupported(identity.backend)) {
|
|
124
|
+
// #152: a paused native-push backend is refused by name BEFORE its adapter is probed —
|
|
125
|
+
// on the measured external path that language server refused the call (missing CSRF token),
|
|
126
|
+
// and a refusal must not be reported as a measured liveness. Pre-probe, observedLiveness null.
|
|
127
|
+
if (nativePushPaused(identity.backend)) {
|
|
128
|
+
return reject(makeRejectReceipt("native-push-paused", null));
|
|
129
|
+
}
|
|
124
130
|
const probe = await deps.nativePushProbe(identity);
|
|
125
131
|
const receipt = resolveNativePushDispatch(input.intent, probe.status);
|
|
126
132
|
if (!receipt.ok)
|
|
@@ -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,28 +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 "
|
|
77
|
-
case "
|
|
71
|
+
return ENTWURF_REJECT_HINTS["native-push-probe-indeterminate"];
|
|
72
|
+
case "native-push-paused":
|
|
73
|
+
// #152: a policy refusal, not a liveness verdict — this dispatch probed and sent nothing.
|
|
74
|
+
return ENTWURF_REJECT_HINTS["native-push-paused"];
|
|
75
|
+
case "record-less-socket": {
|
|
78
76
|
// #50 C4: name the true cause AND the fix — a bare socket is a diagnostic
|
|
79
|
-
// state, not an addressable citizen (the record is the address).
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
+
}
|
|
85
83
|
default:
|
|
86
84
|
return undefined;
|
|
87
85
|
}
|
|
@@ -182,13 +180,14 @@ export function renderEntwurfV2Result(result) {
|
|
|
182
180
|
// the same acceptance vocabulary the ordinary receipt uses is named here too — a
|
|
183
181
|
// dirty lock is a reason to be MORE precise about what happened, not less, because
|
|
184
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.
|
|
185
185
|
const what = result.finalizedOutcome === "rejected"
|
|
186
|
-
? `rejected${result.finalizedRejectReason ? ` (${result.finalizedRejectReason})` : ""} —
|
|
186
|
+
? `rejected${result.finalizedRejectReason ? ` (${result.finalizedRejectReason})` : ""} — ${ENTWURF_DELIVERY_RESULT_CLAUSES["finalized-rejected"]}`
|
|
187
187
|
: `${result.finalizedBoundary ?? "mailbox-enqueued"}${result.finalizedMessagePath ? ` (enqueued ${basename(result.finalizedMessagePath)})` : ""}`;
|
|
188
188
|
return {
|
|
189
|
-
text: `entwurf_v2 ${result.transport} finalized as ${what}, and
|
|
190
|
-
|
|
191
|
-
`\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}`,
|
|
192
191
|
isError: true,
|
|
193
192
|
};
|
|
194
193
|
}
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
* half reads the receiver marker and the record instead.
|
|
74
74
|
*/
|
|
75
75
|
import { ACP_MODEL_NOT_CURATED_HINT, AcpModelNotCuratedError } from "./acp/model-ids.js";
|
|
76
|
+
import { ENTWURF_RESUME_REJECT_HINTS } from "./agent-guidance.js";
|
|
76
77
|
import { buildResumePiArgs } from "./entwurf-resume-args.js";
|
|
77
78
|
import { ENTWURF_V2_LOCK_DIR, acquireLock as realAcquireLock, releaseLock as realReleaseLock, } from "./entwurf-v2-lock.js";
|
|
78
79
|
import { classifyMarkerOwner, metaReceiverMarkerPath, parseMetaReceiverMarker, probePidExistence, processStartKey, readStoreRecordFile, } from "./meta-session.js";
|
|
@@ -441,19 +442,22 @@ export async function observeSameGidSocket(targetGardenId, socketPath, deps, tim
|
|
|
441
442
|
await deps.sleep(intervalMs);
|
|
442
443
|
}
|
|
443
444
|
}
|
|
445
|
+
// The static record/liveness hint text is authored in root ENTWURF.json (#154 C1-C); the reasons,
|
|
446
|
+
// this total table and the renderer stay here. acp-model-not-curated keeps the shared ACP constant,
|
|
447
|
+
// and the launch-stage mux hints folded into `detail` stay behind the mux import fence.
|
|
444
448
|
const REJECT_HINT = {
|
|
445
|
-
"target-invalid": "
|
|
446
|
-
"target-backend-unsupported": "
|
|
449
|
+
"target-invalid": ENTWURF_RESUME_REJECT_HINTS["target-invalid"],
|
|
450
|
+
"target-backend-unsupported": ENTWURF_RESUME_REJECT_HINTS["target-backend-unsupported"],
|
|
447
451
|
"acp-model-not-curated": ACP_MODEL_NOT_CURATED_HINT,
|
|
448
|
-
"target-locked": "
|
|
449
|
-
"target-live": "
|
|
450
|
-
"target-indeterminate": "
|
|
451
|
-
"target-receiver-absent": "
|
|
452
|
-
"target-receiver-corrupt": "
|
|
453
|
-
"target-receiver-mismatch": "
|
|
454
|
-
"target-receiver-unbound": "
|
|
455
|
-
"target-address-conflict": "
|
|
456
|
-
"launch-refused": "
|
|
452
|
+
"target-locked": ENTWURF_RESUME_REJECT_HINTS["target-locked"],
|
|
453
|
+
"target-live": ENTWURF_RESUME_REJECT_HINTS["target-live"],
|
|
454
|
+
"target-indeterminate": ENTWURF_RESUME_REJECT_HINTS["target-indeterminate"],
|
|
455
|
+
"target-receiver-absent": ENTWURF_RESUME_REJECT_HINTS["target-receiver-absent"],
|
|
456
|
+
"target-receiver-corrupt": ENTWURF_RESUME_REJECT_HINTS["target-receiver-corrupt"],
|
|
457
|
+
"target-receiver-mismatch": ENTWURF_RESUME_REJECT_HINTS["target-receiver-mismatch"],
|
|
458
|
+
"target-receiver-unbound": ENTWURF_RESUME_REJECT_HINTS["target-receiver-unbound"],
|
|
459
|
+
"target-address-conflict": ENTWURF_RESUME_REJECT_HINTS["target-address-conflict"],
|
|
460
|
+
"launch-refused": ENTWURF_RESUME_REJECT_HINTS["launch-refused"],
|
|
457
461
|
};
|
|
458
462
|
/**
|
|
459
463
|
* ONE renderer — the same reason `mux-fresh-call` has one: it keeps the operator-visible answer
|
|
@@ -63,7 +63,7 @@ export const FRESH_CALL_CALLBACK_TOOL = {
|
|
|
63
63
|
copilot: "entwurf-bridge-entwurf_callback",
|
|
64
64
|
omp: "mcp__entwurf_bridge_entwurf_callback",
|
|
65
65
|
// Codex's tool process is the operator app-server, not the pane: the no-arg
|
|
66
|
-
// verb refuses there by name.
|
|
66
|
+
// verb refuses there by name. Its callback stays the delivery verb with args.
|
|
67
67
|
codex: "mcp__entwurf_bridge__entwurf_v2",
|
|
68
68
|
// The durable contact registers the bridge verbs as its OWN tools under their bare names, and the
|
|
69
69
|
// app offers a tool to the model by its registered name (`durable/harness/registry.ts:99`
|
|
@@ -348,8 +348,8 @@ export function composeBackendArgs(backend, composition, model, resolveCodexSock
|
|
|
348
348
|
* PLACEMENT SENTENCE. The first line names where the sibling was opened, and only the RAIL knows
|
|
349
349
|
* that — "the operator's tmux session" is true of a `new-window` launch and false of a herdr
|
|
350
350
|
* pane. It is therefore supplied by the caller rather than composed here. Everything BELOW that
|
|
351
|
-
* line is rail-independent and stays the contract: the callback
|
|
352
|
-
*
|
|
351
|
+
* line is rail-independent and stays the contract: the callback comes before the task, an optional
|
|
352
|
+
* read-only check of the caller may come before the callback, and the steering is stated as fact.
|
|
353
353
|
*/
|
|
354
354
|
/**
|
|
355
355
|
* The first-turn framing, WITHOUT the task.
|
|
@@ -361,8 +361,13 @@ export function composeBackendArgs(backend, composition, model, resolveCodexSock
|
|
|
361
361
|
* the halves separate is what lets the framing stay plain prose on both rails while the operator's
|
|
362
362
|
* task text keeps every byte it was written with.
|
|
363
363
|
*
|
|
364
|
-
* Order is the contract: the callback
|
|
365
|
-
*
|
|
364
|
+
* Order is the contract: the callback comes BEFORE THE TASK, so a sibling that gets stuck in the
|
|
365
|
+
* task has already told the caller who it is. It is not the literal first tool call. `[GLG 직접,
|
|
366
|
+
* 2026-10-24]` checking the caller and then calling back is the right order, and the framing used
|
|
367
|
+
* to say both "FIRST ACTION … call back" and "you can corroborate the caller first" — two
|
|
368
|
+
* sentences that cannot both be obeyed. What may come before the callback is the one read-only
|
|
369
|
+
* check offered below (and, where a dialect needs it, loading the tools); reading files, running
|
|
370
|
+
* commands or starting the task may not.
|
|
366
371
|
*
|
|
367
372
|
* WHAT THIS SAYS AND WHY IT STOPPED SAYING THE REST (#116). Until 2026-09-17 this framing carried
|
|
368
373
|
* three prohibitions, including "Do not inspect environment variables, do not call entwurf_self".
|
|
@@ -390,7 +395,7 @@ export function composeFreshCallFraming(params) {
|
|
|
390
395
|
}
|
|
391
396
|
const firstAction = params.backend === "codex"
|
|
392
397
|
? [
|
|
393
|
-
`
|
|
398
|
+
`BEFORE THE TASK, and before reading files or running commands: call ${birthTool} with ` +
|
|
394
399
|
`target=${params.callerGardenId}, intent=fire-and-forget, wants_reply=false, and ` +
|
|
395
400
|
`message set to exactly ${params.nonce} — that string alone, nothing added.`,
|
|
396
401
|
`${params.nonce} is a correlation tag this caller minted for this one launch: it names no`,
|
|
@@ -400,7 +405,7 @@ export function composeFreshCallFraming(params) {
|
|
|
400
405
|
"entwurf_callback is refused on this rail: the tool process is the operator app-server, not this pane.",
|
|
401
406
|
]
|
|
402
407
|
: [
|
|
403
|
-
`
|
|
408
|
+
`BEFORE THE TASK, and before reading files or running commands: call ${birthTool} with no arguments.`,
|
|
404
409
|
"That call is how the caller learns the garden id you were born with. There are no",
|
|
405
410
|
"parameters: a target or message you type would be a second address axis.",
|
|
406
411
|
"Your own record does not carry the caller's address, so reporting your identity here does not reach it — that tool call does.",
|
|
@@ -410,9 +415,15 @@ export function composeFreshCallFraming(params) {
|
|
|
410
415
|
"",
|
|
411
416
|
...firstAction,
|
|
412
417
|
"",
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
418
|
+
// THE CHECK IS THE SIBLING'S, AND SO IS ITS VERDICT. `[GLG 직접, 2026-10-24]` a capped listing that
|
|
419
|
+
// does not show the caller proves nothing about the caller, but a sibling whose check does not hold
|
|
420
|
+
// up may say so and hold back. Entwurf decides nothing here — no trust store, no fallback, no
|
|
421
|
+
// watcher — and the cost of holding back is stated, not hidden: no callback, no address.
|
|
422
|
+
`You may corroborate the caller before that call: ${peersTool} is a read-only listing of`,
|
|
423
|
+
`this garden's citizens and ${params.callerGardenId} is one of them. That listing is capped, so not`,
|
|
424
|
+
"seeing the caller in it does not show the caller is absent. If what you can check is not enough,",
|
|
425
|
+
"or it conflicts with this message, you may say so here and hold the callback and the task; the",
|
|
426
|
+
"caller then has no address for you.",
|
|
416
427
|
...hintBlock,
|
|
417
428
|
"",
|
|
418
429
|
// WHERE THE RESULT GOES, said once, as topology. `[GLG 직접, 2026-09-18]` a Sonnet sibling
|
|
@@ -899,10 +899,12 @@ export function renderHerdrFreshCall(result) {
|
|
|
899
899
|
`mean the sibling is running, that its first turn ran, or that the task was delivered. The tab and pane ` +
|
|
900
900
|
`coordinates are a view and can change under the sibling — they are not an address and nothing may be ` +
|
|
901
901
|
`dispatched to them.\n` +
|
|
902
|
-
`The sibling's garden id arrives separately — its
|
|
902
|
+
`The sibling's garden id arrives separately — before its task it calls the zero-argument callback verb ` +
|
|
903
|
+
`(a read-only look at the caller may come first), and ` +
|
|
903
904
|
`the sender envelope of THAT message is the address. The nonce above is the correlation tag it carries; ` +
|
|
904
905
|
`the sibling reads it from its own launch env, never from this receipt. Nothing is polling for it; if it ` +
|
|
905
|
-
`never comes,
|
|
906
|
+
`never comes — not started, stuck, or held back after its check; this receipt cannot tell which — the ` +
|
|
907
|
+
`pane is visible and can be read directly.`,
|
|
906
908
|
isError: false,
|
|
907
909
|
};
|
|
908
910
|
}
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
* rails never merge — `codex` is deliberately absent from {@link META_SENDER_BACKENDS}, since
|
|
47
47
|
* a backend listed there is one whose hook writes a pid marker, and codex birth writes none.
|
|
48
48
|
*/
|
|
49
|
+
import { nativePushPaused } from "./entwurf-v2-contract.js";
|
|
49
50
|
import { defaultMetaSessionsDir, describe, isPlausibleOwnerPid, listAllMetaIdentities, makeStoreRecordReader, metaRecordExistsByGardenId, parentPid, readActiveStoreEntries, readMetaIdentityByGardenId, readMetaSenderMarker, } from "./meta-session.js";
|
|
50
51
|
import { resolveNativePushAdapter } from "./native-push/adapter.js";
|
|
51
52
|
/** Every native backend that mints a garden-id from its own hook and writes a sender marker.
|
|
@@ -193,6 +194,10 @@ export function resolveTrustedMetaSenderIdentity(opts = {}) {
|
|
|
193
194
|
* exactly the Crash-Don't-Warn shape this lane exists to remove. Let them propagate.
|
|
194
195
|
*/
|
|
195
196
|
export async function probeNativeSenderAlive(identity, deps = {}) {
|
|
197
|
+
// #152: a paused native-push backend cannot receive a reply, so its `replyable` is false by
|
|
198
|
+
// POLICY — no adapter is resolved or probed. This is not a liveness verdict about the citizen.
|
|
199
|
+
if (nativePushPaused(identity.backend))
|
|
200
|
+
return false;
|
|
196
201
|
const resolveAdapter = deps.resolveAdapter ?? resolveNativePushAdapter;
|
|
197
202
|
const probe = await resolveAdapter(identity.backend).probe(identity.nativeSessionId);
|
|
198
203
|
return probe.status === "alive";
|
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
* there is no "session created" field because nothing here creates one.
|
|
87
87
|
*/
|
|
88
88
|
import { ACP_MODEL_NOT_CURATED_HINT } from "./acp/model-ids.js";
|
|
89
|
+
import { ENTWURF_FRESH_LAUNCH_REJECT_HINTS } from "./agent-guidance.js";
|
|
89
90
|
import { callbackEnvAssignments } from "./callback-env.js";
|
|
90
91
|
import { classifyTmuxCwd } from "./classify-tmux-cwd.js";
|
|
91
92
|
import { CODEX_CALLER_SEAT_HINT, resolveCodexCallerSeat, } from "./codex-caller-seat.js";
|
|
@@ -442,7 +443,9 @@ export function freshCall(params, env = process.env, nonce = mintNonce()) {
|
|
|
442
443
|
};
|
|
443
444
|
}
|
|
444
445
|
/** Why each refusal happened, in the caller's terms. A reason a caller cannot act on is a reason
|
|
445
|
-
* they will guess about.
|
|
446
|
+
* they will guess about. The static tmux launch-stage text is authored in root
|
|
447
|
+
* ENTWURF.json (#154 C1-D) and bound key by key, never spread, so a duplicate or missing reason
|
|
448
|
+
* still fails typecheck; slot-bearing, ACP and preflight-leaf hints stay code-owned. */
|
|
446
449
|
const REJECT_HINT = {
|
|
447
450
|
// The Copilot capability reasons keep their repair text on the leaf that decides them, so
|
|
448
451
|
// the sentence an operator reads cannot drift away from the predicate that produced it.
|
|
@@ -454,28 +457,28 @@ const REJECT_HINT = {
|
|
|
454
457
|
...CODEX_CALLER_PREFLIGHT_HINT,
|
|
455
458
|
...CODEX_CALLER_SEAT_HINT,
|
|
456
459
|
...FRESH_CALL_PROVIDER_HINT,
|
|
457
|
-
"no-tmux-context": "
|
|
458
|
-
"anchor-malformed": "
|
|
459
|
-
"anchor-unresolved": "
|
|
460
|
-
"anchor-mismatch": "
|
|
461
|
-
"caller-identity-unavailable": "
|
|
462
|
-
"cwd-not-absolute": "
|
|
463
|
-
"cwd-format-token": "
|
|
464
|
-
"cwd-missing": "
|
|
465
|
-
"cwd-not-directory": "
|
|
466
|
-
"tmux-session-name-invalid": "
|
|
467
|
-
"tmux-session-missing": "
|
|
468
|
-
"model-empty": "model
|
|
460
|
+
"no-tmux-context": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["no-tmux-context"],
|
|
461
|
+
"anchor-malformed": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["anchor-malformed"],
|
|
462
|
+
"anchor-unresolved": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["anchor-unresolved"],
|
|
463
|
+
"anchor-mismatch": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["anchor-mismatch"],
|
|
464
|
+
"caller-identity-unavailable": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["caller-identity-unavailable"],
|
|
465
|
+
"cwd-not-absolute": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["cwd-not-absolute"],
|
|
466
|
+
"cwd-format-token": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["cwd-format-token"],
|
|
467
|
+
"cwd-missing": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["cwd-missing"],
|
|
468
|
+
"cwd-not-directory": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["cwd-not-directory"],
|
|
469
|
+
"tmux-session-name-invalid": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["tmux-session-name-invalid"],
|
|
470
|
+
"tmux-session-missing": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["tmux-session-missing"],
|
|
471
|
+
"model-empty": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["model-empty"],
|
|
469
472
|
"model-invalid": `model must be one ${MODEL_MAX_CHARS}-character argv-safe id/alias without whitespace or tmux syntax`,
|
|
470
473
|
"acp-model-not-curated": ACP_MODEL_NOT_CURATED_HINT,
|
|
471
|
-
"task-empty": "task
|
|
474
|
+
"task-empty": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["task-empty"],
|
|
472
475
|
"task-too-long": `task exceeds ${TASK_MAX_CHARS} characters`,
|
|
473
|
-
"runtime-unresolved": "
|
|
474
|
-
"runtime-not-absolute": "
|
|
475
|
-
"runtime-path-whitespace": "
|
|
476
|
-
"runtime-missing": "
|
|
477
|
-
"runtime-not-regular-file": "
|
|
478
|
-
"runtime-not-executable": "
|
|
476
|
+
"runtime-unresolved": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["runtime-unresolved"],
|
|
477
|
+
"runtime-not-absolute": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["runtime-not-absolute"],
|
|
478
|
+
"runtime-path-whitespace": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["runtime-path-whitespace"],
|
|
479
|
+
"runtime-missing": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["runtime-missing"],
|
|
480
|
+
"runtime-not-regular-file": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["runtime-not-regular-file"],
|
|
481
|
+
"runtime-not-executable": ENTWURF_FRESH_LAUNCH_REJECT_HINTS["runtime-not-executable"],
|
|
479
482
|
};
|
|
480
483
|
/**
|
|
481
484
|
* ONE renderer, outside the registration. Not a convenience: it keeps the operator-visible answer
|
|
@@ -514,11 +517,13 @@ export function renderFreshCall(result) {
|
|
|
514
517
|
`\n` +
|
|
515
518
|
`This is a LAUNCH receipt: tmux created that window and was asked to start the runtime with the model above. It does ` +
|
|
516
519
|
`NOT mean the sibling is running, that its first turn ran, or that the task was delivered.\n` +
|
|
517
|
-
`The sibling's garden id arrives separately — its
|
|
518
|
-
`(codex is the one arm that still sends the nonce as arguments
|
|
520
|
+
`The sibling's garden id arrives separately — before its task it calls the zero-argument callback verb ` +
|
|
521
|
+
`(codex is the one arm that still sends the nonce as arguments; a read-only look at the caller may come ` +
|
|
522
|
+
`first), and the sender envelope of THAT ` +
|
|
519
523
|
`message is the address. The nonce above is the correlation tag it carries; the sibling reads it ` +
|
|
520
|
-
`from its own launch env, never from this receipt. Nothing is polling for it; if it never comes
|
|
521
|
-
`the window is visible
|
|
524
|
+
`from its own launch env, never from this receipt. Nothing is polling for it; if it never comes — ` +
|
|
525
|
+
`not started, stuck, or held back after its check; this receipt cannot tell which — the window is visible ` +
|
|
526
|
+
`and can be read directly.`,
|
|
522
527
|
isError: false,
|
|
523
528
|
};
|
|
524
529
|
}
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
* where the pane actually landed is a separate query against the stable pane id, and it belongs
|
|
49
49
|
* to acceptance, not to the product's launch receipt.
|
|
50
50
|
*/
|
|
51
|
+
import { ENTWURF_RESUME_LAUNCH_REJECT_HINTS } from "./agent-guidance.js";
|
|
51
52
|
import { classifyTmuxCwd } from "./classify-tmux-cwd.js";
|
|
52
53
|
import { assertLaunchTarget, LaunchPreconditionError, resolveRuntimeOnPath, SCRUBBED_INHERITED_ENV, } from "./mux-launch.js";
|
|
53
54
|
import { APPEND_FORMAT, assertSelector, assertTmuxOk, inspectPlacement, parseWindowFields, requireSameContext, runTmux, } from "./mux-placement.js";
|
|
@@ -136,21 +137,22 @@ export function resumeCall(params, env = process.env) {
|
|
|
136
137
|
};
|
|
137
138
|
}
|
|
138
139
|
/** Why each refusal happened, in the caller's terms. A reason a caller cannot act on is a reason
|
|
139
|
-
* they will guess about.
|
|
140
|
+
* they will guess about. The text is authored in root ENTWURF.json (#154 C1-D); each
|
|
141
|
+
* key is bound explicitly so the reason union stays exhaustive here. */
|
|
140
142
|
export const RESUME_CALL_REJECT_HINT = {
|
|
141
|
-
"no-tmux-context": "
|
|
142
|
-
"anchor-malformed": "
|
|
143
|
-
"anchor-unresolved": "
|
|
144
|
-
"anchor-mismatch": "
|
|
145
|
-
"cwd-not-absolute": "
|
|
146
|
-
"cwd-format-token": "
|
|
147
|
-
"cwd-missing": "
|
|
148
|
-
"cwd-not-directory": "
|
|
149
|
-
"herdr-resume-unsupported": "
|
|
150
|
-
"runtime-unresolved": "
|
|
151
|
-
"runtime-not-absolute": "
|
|
152
|
-
"runtime-path-whitespace": "
|
|
153
|
-
"runtime-missing": "
|
|
154
|
-
"runtime-not-regular-file": "
|
|
155
|
-
"runtime-not-executable": "
|
|
143
|
+
"no-tmux-context": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["no-tmux-context"],
|
|
144
|
+
"anchor-malformed": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["anchor-malformed"],
|
|
145
|
+
"anchor-unresolved": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["anchor-unresolved"],
|
|
146
|
+
"anchor-mismatch": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["anchor-mismatch"],
|
|
147
|
+
"cwd-not-absolute": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["cwd-not-absolute"],
|
|
148
|
+
"cwd-format-token": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["cwd-format-token"],
|
|
149
|
+
"cwd-missing": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["cwd-missing"],
|
|
150
|
+
"cwd-not-directory": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["cwd-not-directory"],
|
|
151
|
+
"herdr-resume-unsupported": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["herdr-resume-unsupported"],
|
|
152
|
+
"runtime-unresolved": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["runtime-unresolved"],
|
|
153
|
+
"runtime-not-absolute": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["runtime-not-absolute"],
|
|
154
|
+
"runtime-path-whitespace": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["runtime-path-whitespace"],
|
|
155
|
+
"runtime-missing": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["runtime-missing"],
|
|
156
|
+
"runtime-not-regular-file": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["runtime-not-regular-file"],
|
|
157
|
+
"runtime-not-executable": ENTWURF_RESUME_LAUNCH_REJECT_HINTS["runtime-not-executable"],
|
|
156
158
|
};
|
|
@@ -19,9 +19,11 @@
|
|
|
19
19
|
* deliverability semantics. `check-native-push-register` asserts this file references no
|
|
20
20
|
* receiver-marker writer. (A dedicated register-provenance slot is a future concern.)
|
|
21
21
|
*
|
|
22
|
-
* LEAF-adjacent + pi-free: imports only the native-push adapter (pi-free)
|
|
23
|
-
* upsert (pi-free), so the harness-neutral MCP
|
|
22
|
+
* LEAF-adjacent + pi-free: imports only the native-push adapter (pi-free), the meta-session
|
|
23
|
+
* upsert (pi-free) and the pure v2 contract's paused-backend policy, so the harness-neutral MCP
|
|
24
|
+
* bridge can reach it at boot.
|
|
24
25
|
*/
|
|
26
|
+
import { nativePushPaused } from "../entwurf-v2-contract.js";
|
|
25
27
|
import { upsertMetaSession } from "../meta-session.js";
|
|
26
28
|
import { resolveNativePushAdapter } from "./adapter.js";
|
|
27
29
|
/**
|
|
@@ -30,6 +32,15 @@ import { resolveNativePushAdapter } from "./adapter.js";
|
|
|
30
32
|
* an addressable citizen. On success returns the garden id + the create/attach action.
|
|
31
33
|
*/
|
|
32
34
|
export async function registerNativeConversation(input, deps = {}) {
|
|
35
|
+
// #152: a paused native-push backend is refused by name BEFORE any adapter is resolved or
|
|
36
|
+
// probed — registration would otherwise reach a language server that refused the measured
|
|
37
|
+
// external calls (missing CSRF token).
|
|
38
|
+
// Nothing is probed and no record is written.
|
|
39
|
+
if (nativePushPaused(input.backend)) {
|
|
40
|
+
throw new Error(`entwurf_register_native: refusing to register ${input.backend} conversation ${JSON.stringify(input.nativeSessionId)} — native-push for ${input.backend} is paused (#152: its language server refused ` +
|
|
41
|
+
"entwurf's measured external calls for a missing CSRF token). Nothing was probed and no garden id was minted; " +
|
|
42
|
+
"existing records stay readable.");
|
|
43
|
+
}
|
|
33
44
|
const resolveAdapter = deps.resolveAdapter ?? resolveNativePushAdapter;
|
|
34
45
|
const adapter = resolveAdapter(input.backend);
|
|
35
46
|
const probe = await adapter.probe(input.nativeSessionId);
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
* callback callable — THE OMP-SPECIFIC ONE. `tools.xdev` defaults to TRUE, which mounts MCP
|
|
21
21
|
* tools as `xd://` devices whose schemas never reach the prompt. The tool
|
|
22
22
|
* would be configured, the bridge would be running, and the model still
|
|
23
|
-
* could not call `mcp__entwurf_bridge_entwurf_v`. Step 9 clause 5
|
|
24
|
-
* callback the
|
|
23
|
+
* could not call `mcp__entwurf_bridge_entwurf_v`. Step 9 clause 5 puts the
|
|
24
|
+
* callback before the task, so a fresh call onto a default-config host
|
|
25
25
|
* opens a window that can never name itself. `[측정]` #87 A-lane: the
|
|
26
26
|
* vendor default wrapped the send tool and produced a false delivery report.
|
|
27
27
|
*
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* provider spelled out (the bootstrap refuses a model resolved by inference), so
|
|
16
16
|
* the model here is `<provider>/<model id>` split at the FIRST `/` — a catalog id
|
|
17
17
|
* that itself contains `/` keeps it. A model with no provider part is refused.
|
|
18
|
-
* first input the whole framing (callback
|
|
18
|
+
* first input the whole framing (callback before the task) rides the bootstrap's closed
|
|
19
19
|
* `{v:2,task}` payload, whose decoder caps `task` at 16000 characters. A framed
|
|
20
20
|
* task over that cap would open a window whose bootstrap refuses its own argv.
|
|
21
21
|
*
|