@eir-labs/coltrane 0.24.28 → 0.24.30
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/dist/src/cli.d.ts +4 -1
- package/dist/src/cli.js +19 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/mcp.d.ts +3 -0
- package/dist/src/mcp.js +89 -1
- package/dist/src/mcp.js.map +1 -1
- package/dist/src/reside.d.ts +150 -0
- package/dist/src/reside.js +380 -0
- package/dist/src/reside.js.map +1 -0
- package/dist/src/reside_backing.d.ts +64 -0
- package/dist/src/reside_backing.js +243 -0
- package/dist/src/reside_backing.js.map +1 -0
- package/dist/src/residency.d.ts +9 -5
- package/dist/src/residency.js +10 -10
- package/dist/src/residency.js.map +1 -1
- package/dist/src/server.js +7 -1
- package/dist/src/server.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +11 -1
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `coltrane reside` — the standing loop.
|
|
3
|
+
*
|
|
4
|
+
* The state machine this drives already exists (src/residency.ts, 43 laws): states, ops, the
|
|
5
|
+
* party-constrained transition table, the reflex, the reaper, the boot preflight. What did NOT exist
|
|
6
|
+
* was a CALLER. `runReside` was exported, tested and pinned as law I20 while `src/cli.ts`'s KNOWN
|
|
7
|
+
* table had no "reside" in it, so `coltrane reside` answered `unknown command` on a green suite —
|
|
8
|
+
* a mechanism proven to work that nothing could reach. This module is the loop, and the mount in
|
|
9
|
+
* cli.ts is the half that makes it reachable.
|
|
10
|
+
*
|
|
11
|
+
* THREE PROPERTIES THE SHAPE ENFORCES RATHER THAN REQUESTS:
|
|
12
|
+
*
|
|
13
|
+
* 1. NO MODEL ON THE REFLEX PATH. `onInbound` is SYNCHRONOUS. Not "does not call the cortex" as a
|
|
14
|
+
* matter of discipline — it cannot await, so it cannot reach one. An inbound message is acked
|
|
15
|
+
* and appended whether the cortex is busy, dead, or absent; an unacked message means the
|
|
16
|
+
* listener is down, which is a pager fact and must stay one.
|
|
17
|
+
*
|
|
18
|
+
* 2. EVERY SEAM IS INJECTED, AND AN UNWIRED SEAM IS A NAMED REFUSAL. The way `deps.queueGig`
|
|
19
|
+
* injects dispatch (src/server.ts:3436): the engine ships the loop, its refusals and its shape
|
|
20
|
+
* checks; a deployment supplies the backends. Absent must mean DECLINE — `{ok:false,
|
|
21
|
+
* refusal:"no_backend", seam}` in the gig_dispatch shape, naming which seam — never a throw and
|
|
22
|
+
* never a plausible default. `hosted_unsupported` is deliberately not reused: an unwired seam
|
|
23
|
+
* and an unsupported deployment are different facts.
|
|
24
|
+
*
|
|
25
|
+
* 3. THE ROUTER IS CODE. Determinism in the engine, inference in the prompt, no overlap. A clean
|
|
26
|
+
* pass dispatches every due (order, ordinal) and consults the cortex ZERO times. The cortex is
|
|
27
|
+
* reached on NAMED DEMAND only — a store refusal whose `law_ref` the deployment listed — and that
|
|
28
|
+
* is keyed on the law_ref, never on the refusal's prose, because prose is written for humans
|
|
29
|
+
* and gets reworded without notice.
|
|
30
|
+
*
|
|
31
|
+
* A NOTE ON THE IMPORT CYCLE. src/residency.ts re-exports `runReside` from here, and this module
|
|
32
|
+
* imports the state machine from there. The cycle is safe and deliberate: every binding crossing it
|
|
33
|
+
* is either a hoisted function declaration or read inside a function body at call time, never during
|
|
34
|
+
* module evaluation. The alternative was duplicating the transition table, which is the one thing
|
|
35
|
+
* the surface's identity laws exist to forbid.
|
|
36
|
+
*/
|
|
37
|
+
import { selectResidencyBacking, resolveSeatBacking } from "./reside_backing.js";
|
|
38
|
+
import { applyResidencyOp, bootResidency, reflexAck, } from "./residency.js";
|
|
39
|
+
export { selectResidencyBacking, resolveSeatBacking, fileSeatBacking, fileSeatSeed, SEAT_MEMBERS, RESIDENCY_DIR_VAR, RESIDENCY_MODULE_VAR, } from "./reside_backing.js";
|
|
40
|
+
/** The gig path is WORK's gig path. A second implementation fails by existing (I12, one level out). */
|
|
41
|
+
export { workOnce as resideGigPath } from "./worker.js";
|
|
42
|
+
export const RESIDE_REFUSALS = [
|
|
43
|
+
"no_backend",
|
|
44
|
+
"backing_conflict",
|
|
45
|
+
"nothing_claimable",
|
|
46
|
+
"gig_scoped_token",
|
|
47
|
+
"silent_wake",
|
|
48
|
+
"cursor_without_seal",
|
|
49
|
+
"needs_amendment",
|
|
50
|
+
"store_refused",
|
|
51
|
+
];
|
|
52
|
+
/**
|
|
53
|
+
* The exit code each refusal earns, TOTAL over the refusal set — `Record<ResideRefusal, number>`, so
|
|
54
|
+
* adding a refusal without deciding what it means to a supervisor is a type error rather than a
|
|
55
|
+
* silent 1. The set is read at runtime (not merely declared) so an undeclared refusal reaching here
|
|
56
|
+
* exits 1 rather than `undefined`.
|
|
57
|
+
*
|
|
58
|
+
* 2 misconfigured — including a seam no deployment wired · 3 nothing claimable ·
|
|
59
|
+
* 1 the loop ran and something failed · 0 is reserved for a clean release.
|
|
60
|
+
*/
|
|
61
|
+
const EXIT_FOR_REFUSAL = {
|
|
62
|
+
no_backend: 2,
|
|
63
|
+
backing_conflict: 2,
|
|
64
|
+
nothing_claimable: 3,
|
|
65
|
+
gig_scoped_token: 2,
|
|
66
|
+
silent_wake: 1,
|
|
67
|
+
cursor_without_seal: 1,
|
|
68
|
+
needs_amendment: 1,
|
|
69
|
+
store_refused: 1,
|
|
70
|
+
};
|
|
71
|
+
/** The supervisor-facing exit code for a refusal. An unknown name is a failure, never a success. */
|
|
72
|
+
export function resideExitCode(refusal) {
|
|
73
|
+
return RESIDE_REFUSALS.includes(refusal) ? EXIT_FOR_REFUSAL[refusal] : 1;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* WHICH refusals constitute NAMED DEMAND is configuration, not an engine constant. The engine ships
|
|
77
|
+
* the mechanism — branch on a refusal's `law_ref`, never on its prose, because prose is written for
|
|
78
|
+
* humans and gets reworded — and the deployment supplies the refs that mean "a human must rule".
|
|
79
|
+
* Hard-coding one institution's law reference here named someone else's governance inside the
|
|
80
|
+
* engine, which is the coupling the backing seam exists to prevent, one layer over.
|
|
81
|
+
*/
|
|
82
|
+
const NO_NAMED_DEMAND = [];
|
|
83
|
+
/** The seams boot cannot proceed without, in the order they are reported. `callVerb` is absent from
|
|
84
|
+
* this list on purpose: a residency that only listens and answers is a legitimate residency, and
|
|
85
|
+
* the router names its own seam when it is the thing that is unwired. */
|
|
86
|
+
const BOOT_SEAMS = ["claim", "channelListener", "cortex", "say", "sealOutput", "cursorAdvance", "release"];
|
|
87
|
+
const refuse = (refusal, message, extra = {}) => ({ ok: false, refusal, message, ...extra });
|
|
88
|
+
/**
|
|
89
|
+
* A lease token that is narrow. Narrow may not mint broad (L28): a token bound to one gig cannot
|
|
90
|
+
* hold a residency.
|
|
91
|
+
*
|
|
92
|
+
* THE SIGNAL IS gig_id, AND ONLY gig_id — corrected against the store twice, and the second
|
|
93
|
+
* correction is the interesting one. An earlier draft read `may_dispatch === false`, a shape the
|
|
94
|
+
* column (text[]) cannot hold. The fix to "an EMPTY allow-list is narrow" was then ALSO wrong, and
|
|
95
|
+
* would have been worse: the list stopped being the wildcard and became the exact standards a
|
|
96
|
+
* presence may reach, so `{}` is now a perfectly legitimate seating — a residency that dispatches
|
|
97
|
+
* (chair-gated) but reaches no gig it did not dispatch. Treating empty as narrowness would have
|
|
98
|
+
* refused valid seats, and refused them in the one direction nobody tests: the quiet one.
|
|
99
|
+
*
|
|
100
|
+
* The dispatch allow-list is an authorization gate in the STORE, in exactly one place. It is not
|
|
101
|
+
* the engine's business to re-derive it, and a residency's authority sits on its chair regardless.
|
|
102
|
+
*
|
|
103
|
+
* This is a second line either way. The claim door refuses a gig token itself with a message whose
|
|
104
|
+
* machine-readable prefix is `gig_scoped_token:` (see storeRefusalName); the engine still checks,
|
|
105
|
+
* because a local or hand-built backing has no such door in front of it.
|
|
106
|
+
*/
|
|
107
|
+
function isGigScoped(claim) {
|
|
108
|
+
return claim.gig_id != null;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The machine-readable name a store refusal begins with (`gig_scoped_token: …`, `not_holder: …`,
|
|
112
|
+
* `cursor_regression: …`). Branching on the PREFIX rather than the prose is the same discipline the
|
|
113
|
+
* router applies to law_ref: the human half of the message is written to be reworded.
|
|
114
|
+
*/
|
|
115
|
+
export function storeRefusalName(message) {
|
|
116
|
+
const m = /^([a-z][a-z0-9_]*):/.exec(message.trim());
|
|
117
|
+
return m ? m[1] : null;
|
|
118
|
+
}
|
|
119
|
+
/** A monotonic tick source so the reflex budget is measured on a simulated clock on every machine,
|
|
120
|
+
* never on a wall clock (the property REFLEX_BUDGET_TICKS exists to make machine-independent). */
|
|
121
|
+
function clockOf(deps) {
|
|
122
|
+
if (deps.clock)
|
|
123
|
+
return deps.clock;
|
|
124
|
+
let t = 0;
|
|
125
|
+
return { now: () => t, tick: (n = 1) => { t += n; } };
|
|
126
|
+
}
|
|
127
|
+
export function createResidency(opts, deps) {
|
|
128
|
+
const clock = clockOf(deps);
|
|
129
|
+
const escalateOn = opts.escalateOn ?? NO_NAMED_DEMAND;
|
|
130
|
+
const inbox = [];
|
|
131
|
+
let rec = null;
|
|
132
|
+
let residencyId = null;
|
|
133
|
+
/**
|
|
134
|
+
* THE FENCE on this grant, held for the life of the seat and PRESENTED ON EVERY OP.
|
|
135
|
+
*
|
|
136
|
+
* Presenting it is the whole point, and it was missing: applyResidencyOp gates on
|
|
137
|
+
* `op.fence !== undefined && op.fence < rec.fence`, so an op that omits the field is not gated at
|
|
138
|
+
* all. All six call sites below omitted it, which left law I9 — the fencing law, with a case named
|
|
139
|
+
* for a GC-paused host resuming post-lease — unable to fire anywhere on the live path. The store
|
|
140
|
+
* had the mirror of this defect: no `fence` column at all. A declared invariant enforced by
|
|
141
|
+
* neither side is the shape this repo keeps finding, and it survived two green suites.
|
|
142
|
+
*/
|
|
143
|
+
let fence = "";
|
|
144
|
+
let fenceNum = 0;
|
|
145
|
+
let claimed = false;
|
|
146
|
+
let released = false;
|
|
147
|
+
/** The boot refusal, held so a later step answers with the reason it never seated rather than a
|
|
148
|
+
* second, less specific one. */
|
|
149
|
+
let standing = null;
|
|
150
|
+
async function boot() {
|
|
151
|
+
// HOLD THE SEAT. Two boxes reading the same orders both dispatch (plan, seam 3), so the claim
|
|
152
|
+
// door is entered exactly once per instance — a second boot returns the seat already held.
|
|
153
|
+
if (claimed && rec && residencyId)
|
|
154
|
+
return { ok: true, residency_id: residencyId };
|
|
155
|
+
if (standing)
|
|
156
|
+
return standing;
|
|
157
|
+
for (const seam of BOOT_SEAMS) {
|
|
158
|
+
if (typeof deps[seam] !== "function") {
|
|
159
|
+
standing = refuse("no_backend", `reside has no ${seam} backend wired — a deployment supplies it (parallel to deps.queueGig); the engine ships the loop, its refusals and its shape checks.`, { seam });
|
|
160
|
+
return standing;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
claimed = true;
|
|
164
|
+
const claim = await deps.claim(opts.residency);
|
|
165
|
+
if (claim == null) {
|
|
166
|
+
// Not an error and not a seat: an empty roster is the ordinary answer to "is anything free".
|
|
167
|
+
standing = refuse("nothing_claimable", "no residency was claimable — nothing queued for this instance.");
|
|
168
|
+
return standing;
|
|
169
|
+
}
|
|
170
|
+
if (isGigScoped(claim)) {
|
|
171
|
+
// ENGINE-SIDE, BEFORE THE WIRE. The store would refuse this too (42501 at the dispatch door),
|
|
172
|
+
// but a token that cannot possibly dispatch should never travel to find that out.
|
|
173
|
+
standing = refuse("gig_scoped_token", `the claimed credential is scoped to a single gig${claim.gig_id ? ` (${claim.gig_id})` : ""} and cannot hold a residency — narrow may not mint broad.`);
|
|
174
|
+
return standing;
|
|
175
|
+
}
|
|
176
|
+
const booted = bootResidency({ agent_slug: claim.agent_slug, org: claim.org, venue_slug: claim.venue_slug, channel_id: claim.channel_id }, {
|
|
177
|
+
resolveAgent: (s) => (s === claim.agent_slug ? { slug: s } : null),
|
|
178
|
+
resolveVenue: (s) => (s === claim.venue_slug ? { slug: s, credential_surface: [] } : null),
|
|
179
|
+
cortexPresent: () => typeof deps.cortex === "function",
|
|
180
|
+
seatRow: (r) => { rec = r; },
|
|
181
|
+
});
|
|
182
|
+
if (!booted.ok) {
|
|
183
|
+
standing = refuse("no_backend", `boot refused: ${booted.refusal}`, { seam: booted.refusal });
|
|
184
|
+
return standing;
|
|
185
|
+
}
|
|
186
|
+
// The fence is taken from the claim FIRST — the record is built from it, so an ordering slip
|
|
187
|
+
// here would seat every residency at fence 0 and quietly hand the store a stale token on the
|
|
188
|
+
// first heartbeat.
|
|
189
|
+
fence = claim.fence ?? "";
|
|
190
|
+
fenceNum = Number(fence) || 0;
|
|
191
|
+
// The store's session and cursor are the durable ones — a thaw resumes the same life.
|
|
192
|
+
rec = { ...booted.rec, session_id: claim.session_id, cursor: claim.cursor, fence: fenceNum, lease_until: Number.MAX_SAFE_INTEGER };
|
|
193
|
+
residencyId = claim.residency_id;
|
|
194
|
+
const listening = applyResidencyOp(rec, { kind: "listen", fence: fenceNum });
|
|
195
|
+
// A refusal ends the STEP, never the process: an un-listened seat is still a seat.
|
|
196
|
+
if (listening.ok)
|
|
197
|
+
rec = listening.next;
|
|
198
|
+
return { ok: true, residency_id: residencyId };
|
|
199
|
+
}
|
|
200
|
+
/** THE REFLEX. Synchronous by construction — a path that cannot await cannot reach a model. */
|
|
201
|
+
function onInbound(msg) {
|
|
202
|
+
inbox.push(msg);
|
|
203
|
+
if (rec) {
|
|
204
|
+
const acked = applyResidencyOp(rec, { kind: "ack", fence: fenceNum });
|
|
205
|
+
if (acked.ok)
|
|
206
|
+
rec = acked.next;
|
|
207
|
+
}
|
|
208
|
+
// reflexAck is the existing, law-covered primitive (I10/I11) — not a second ack path.
|
|
209
|
+
return reflexAck(msg, { invoke: () => undefined, clock });
|
|
210
|
+
}
|
|
211
|
+
async function wake() {
|
|
212
|
+
if (!rec || !residencyId)
|
|
213
|
+
return standing ?? refuse("no_backend", "not seated", { seam: "claim" });
|
|
214
|
+
const playing = applyResidencyOp(rec, { kind: "play", fence: fenceNum });
|
|
215
|
+
if (playing.ok)
|
|
216
|
+
rec = playing.next;
|
|
217
|
+
const turn = await deps.cortex({ session_id: rec.session_id, inbox: [...inbox] });
|
|
218
|
+
const utterance = turn.utterance;
|
|
219
|
+
if (!utterance || !utterance.text) {
|
|
220
|
+
// THE ALWAYS-ANSWER LAW. A wake that consumed a message and answered nobody is a refusal, not
|
|
221
|
+
// a quiet success — and it seals nothing and moves no cursor, so the unanswered message is not
|
|
222
|
+
// silently dropped.
|
|
223
|
+
return refuse("silent_wake", "the wake produced no channel utterance — a wake that consumes a message must answer it.");
|
|
224
|
+
}
|
|
225
|
+
await deps.say(utterance);
|
|
226
|
+
const sealed = await deps.sealOutput({ residency_id: residencyId, channel_id: utterance.channel_id, text: utterance.text });
|
|
227
|
+
const sha = turn.sealed_output_sha ?? sealed.content_sha;
|
|
228
|
+
// The cursor advances ONLY as a consequence of the seal, in the same write (I1/I3) — the state
|
|
229
|
+
// machine owns that arithmetic, not this loop.
|
|
230
|
+
const advanced = applyResidencyOp(rec, { kind: "wake_seal", fence: fenceNum, message_index: rec.cursor, sealed_output_sha: sha });
|
|
231
|
+
if (!advanced.ok) {
|
|
232
|
+
return refuse(advanced.reason === "cursor_without_seal" ? "cursor_without_seal" : "silent_wake", `the seal did not earn a cursor: ${advanced.reason}`);
|
|
233
|
+
}
|
|
234
|
+
rec = advanced.next;
|
|
235
|
+
await deps.cursorAdvance(residencyId, fence, rec.cursor);
|
|
236
|
+
return { ok: true, utterance, cursor: rec.cursor };
|
|
237
|
+
}
|
|
238
|
+
async function tick() {
|
|
239
|
+
if (!rec || !residencyId)
|
|
240
|
+
return standing ?? refuse("no_backend", "not seated", { seam: "claim" });
|
|
241
|
+
if (typeof deps.callVerb !== "function") {
|
|
242
|
+
return refuse("no_backend", "reside has no callVerb backend wired — the router's hands are the governed verb surface, hydrated under the lease token.", { seam: "callVerb" });
|
|
243
|
+
}
|
|
244
|
+
const pin = { org: rec.org, venue: rec.venue_slug };
|
|
245
|
+
const dispatched = [];
|
|
246
|
+
const relayed = [];
|
|
247
|
+
const escalated = [];
|
|
248
|
+
const monitored = [];
|
|
249
|
+
// A row-routed READ forwards its args verbatim and reads the credential from the header, so it
|
|
250
|
+
// carries no pin. The dispatch is an ACT and names its org and venue in the act itself.
|
|
251
|
+
const due = await deps.callVerb("work-order-due", {});
|
|
252
|
+
if (!due.ok) {
|
|
253
|
+
await deps.say({ channel_id: rec.channel_id, text: due.message });
|
|
254
|
+
return { ok: true, dispatched, monitored, relayed: [{ refusal: due.refusal, message: due.message }], escalated };
|
|
255
|
+
}
|
|
256
|
+
const entries = (Array.isArray(due.data.due) ? due.data.due : []);
|
|
257
|
+
for (const entry of entries) {
|
|
258
|
+
const answer = await deps.callVerb("work-order-dispatch", {
|
|
259
|
+
work_order_id: entry.work_order_id,
|
|
260
|
+
schedule_ordinal: entry.schedule_ordinal,
|
|
261
|
+
mode: entry.mode ?? "live",
|
|
262
|
+
...(entry.input ? { input: entry.input } : {}),
|
|
263
|
+
pin,
|
|
264
|
+
});
|
|
265
|
+
if (answer.ok) {
|
|
266
|
+
// Re-dispatching a (work_order_id, schedule_ordinal) with a standing non-terminal gig
|
|
267
|
+
// returns that gig's id rather than refusing — so "once per wake" is safe, not merely tidy.
|
|
268
|
+
dispatched.push({ work_order_id: entry.work_order_id, schedule_ordinal: entry.schedule_ordinal, gig_id: String(answer.data["gig_id"] ?? "") });
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if (answer.law_ref !== undefined && escalateOn.includes(answer.law_ref)) {
|
|
272
|
+
// THE ONE NAMED DEMAND. Keyed on the law_ref — the identical prose without it is governance
|
|
273
|
+
// and must not reach a model.
|
|
274
|
+
escalated.push({ work_order_id: entry.work_order_id, schedule_ordinal: entry.schedule_ordinal, law_ref: answer.law_ref });
|
|
275
|
+
const turn = await deps.cortex({ session_id: rec.session_id, inbox: [...inbox] });
|
|
276
|
+
if (turn.utterance?.text)
|
|
277
|
+
await deps.say(turn.utterance);
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
// A refusal is information, and the residency's voice is where it is shown — VERBATIM. The
|
|
281
|
+
// engine does not judge a governance refusal; it raises it.
|
|
282
|
+
//
|
|
283
|
+
// The store names its refusals in a machine-readable PREFIX, so the relayed record carries
|
|
284
|
+
// that name when there is one. This is also the second place a gig-scoped token can surface:
|
|
285
|
+
// the seat check catches one that arrives in a claim, and this catches one the wire reports.
|
|
286
|
+
const named = storeRefusalName(answer.message);
|
|
287
|
+
relayed.push({ refusal: named ?? answer.refusal, message: answer.message });
|
|
288
|
+
await deps.say({ channel_id: rec.channel_id, text: answer.message });
|
|
289
|
+
}
|
|
290
|
+
for (const d of dispatched) {
|
|
291
|
+
const seen = await deps.callVerb("gig_monitor", { gig_id: d.gig_id });
|
|
292
|
+
if (seen.ok)
|
|
293
|
+
monitored.push(d.gig_id);
|
|
294
|
+
}
|
|
295
|
+
return { ok: true, dispatched, monitored, relayed, escalated };
|
|
296
|
+
}
|
|
297
|
+
async function beat() {
|
|
298
|
+
// Renewing after the seat has been handed back is how two boxes come to believe they hold it.
|
|
299
|
+
if (released || !rec || !residencyId)
|
|
300
|
+
return refuse("no_backend", "no live seat to renew", { seam: "claim" });
|
|
301
|
+
const beaten = applyResidencyOp(rec, { kind: "heartbeat", fence: fenceNum, cortex_alive: typeof deps.cortex === "function" });
|
|
302
|
+
if (beaten.ok)
|
|
303
|
+
rec = beaten.next;
|
|
304
|
+
await deps.heartbeat(residencyId, fence);
|
|
305
|
+
return { ok: true };
|
|
306
|
+
}
|
|
307
|
+
async function shutdown(_signal) {
|
|
308
|
+
// A redeploy HANDS THE SEAT OVER rather than racing for it. Idempotent: a second signal during
|
|
309
|
+
// a drain must not release twice, and a seat never held is not released at all.
|
|
310
|
+
if (released || !rec || !residencyId)
|
|
311
|
+
return { ok: true };
|
|
312
|
+
released = true;
|
|
313
|
+
const hibernating = applyResidencyOp(rec, { kind: "hibernate", fence: fenceNum, by: "holder" });
|
|
314
|
+
if (hibernating.ok)
|
|
315
|
+
rec = hibernating.next;
|
|
316
|
+
await deps.release(residencyId, fence, "hibernated");
|
|
317
|
+
return { ok: true };
|
|
318
|
+
}
|
|
319
|
+
return {
|
|
320
|
+
boot,
|
|
321
|
+
onInbound,
|
|
322
|
+
wake,
|
|
323
|
+
tick,
|
|
324
|
+
beat,
|
|
325
|
+
shutdown,
|
|
326
|
+
get inbox() { return inbox; },
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* The `reside` verb. REUSES work's env contract — src/worker_env.ts's WORKER_ENV_CONTRACT is the
|
|
331
|
+
* single source, and reside introduces no new credential class: the residency's hands materialize
|
|
332
|
+
* from the drain key that already exists. Missing COLTRANE_STORE_URL / COLTRANE_STORE_ANON is a
|
|
333
|
+
* usage refusal (exit 2), the same door `work` uses.
|
|
334
|
+
*
|
|
335
|
+
* Exit codes mirror work's: 0 released cleanly · 1 the cortex failed · 2 misconfigured, including a
|
|
336
|
+
* seam no deployment has wired · 3 nothing claimable.
|
|
337
|
+
*
|
|
338
|
+
* This reads `io.env` when given one and falls back to the process environment otherwise — the
|
|
339
|
+
* fallback is what makes the mounted command usable, and the injection is what makes law I20
|
|
340
|
+
* checkable. It reads no NAMED variable of its own, which is why src/reside.ts can join the
|
|
341
|
+
* WORKER_PATH scan in tests/spec_worker_environment.test.ts and stay honest there.
|
|
342
|
+
*/
|
|
343
|
+
export async function runReside(argv, io) {
|
|
344
|
+
const asIo = io;
|
|
345
|
+
const env = asIo?.env ?? process.env;
|
|
346
|
+
const say = (s) => { asIo?.err?.(s + "\n"); };
|
|
347
|
+
const at = argv.indexOf("--residency");
|
|
348
|
+
const residency = at >= 0 && argv[at + 1] ? String(argv[at + 1]) : "any";
|
|
349
|
+
// WHERE THE SEAT LIVES is selected FIRST, and never guessed: a local file roster, a backing you
|
|
350
|
+
// wrote, or a hosted one the DEPLOYMENT injects. The engine holds no hosted provider, so with the
|
|
351
|
+
// drain environment set this refuses and names what to wire.
|
|
352
|
+
//
|
|
353
|
+
// THE ORDER HERE IS THE FIX FOR A REAL DEFECT. This used to demand COLTRANE_STORE_URL/_ANON before
|
|
354
|
+
// selecting anything — and both are DRAIN_VARS, so setting COLTRANE_RESIDENCY_DIR produced a
|
|
355
|
+
// backing CONFLICT every time and the local roster could not be selected by any environment on
|
|
356
|
+
// earth. A store credential is the HOSTED backing's requirement, not the verb's: a local seat has
|
|
357
|
+
// no store to point at, and asking it for one made a whole provider unreachable.
|
|
358
|
+
const choice = selectResidencyBacking(env);
|
|
359
|
+
if (choice.backing === "hosted" && (!env["COLTRANE_STORE_URL"] || !env["COLTRANE_STORE_ANON"])) {
|
|
360
|
+
// Said, not silent. A bare exit 2 leaves an operator to guess which of five variables is
|
|
361
|
+
// missing — and leaves the reachability law with nothing only THIS command could have emitted.
|
|
362
|
+
say("reside refused: no_backend (seam: store-env) — the hosted backing needs COLTRANE_STORE_URL and COLTRANE_STORE_ANON.");
|
|
363
|
+
return 2;
|
|
364
|
+
}
|
|
365
|
+
const seat = await resolveSeatBacking(choice, {});
|
|
366
|
+
if (!seat.ok) {
|
|
367
|
+
say(`reside refused: ${seat.refusal} (seam: ${seat.seam}) — ${seat.message}`);
|
|
368
|
+
return resideExitCode(seat.refusal);
|
|
369
|
+
}
|
|
370
|
+
// The seat resolved; the remaining seams (channel, cortex, hands) are per-deployment on every
|
|
371
|
+
// backing, so an unwired one still refuses BY NAME rather than pretending to stand.
|
|
372
|
+
const r = createResidency({ residency }, { ...seat.seat });
|
|
373
|
+
const booted = await r.boot();
|
|
374
|
+
if (!booted.ok) {
|
|
375
|
+
say(`reside refused: ${booted.refusal}${booted.seam ? ` (seam: ${booted.seam})` : ""} — ${booted.message}`);
|
|
376
|
+
return resideExitCode(booted.refusal);
|
|
377
|
+
}
|
|
378
|
+
return 0;
|
|
379
|
+
}
|
|
380
|
+
//# sourceMappingURL=reside.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reside.js","sourceRoot":"","sources":["../../src/reside.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ResidencyClaim } from "./reside.js";
|
|
2
|
+
/** The local file-backed roster's root. Its PRESENCE selects the local backing. */
|
|
3
|
+
export declare const RESIDENCY_DIR_VAR = "COLTRANE_RESIDENCY_DIR";
|
|
4
|
+
/** A module you wrote, exporting the four seat operations. Its PRESENCE selects the module backing. */
|
|
5
|
+
export declare const RESIDENCY_MODULE_VAR = "COLTRANE_RESIDENCY_MODULE";
|
|
6
|
+
export type ResidencyBackingChoice = {
|
|
7
|
+
backing: "local";
|
|
8
|
+
root: string;
|
|
9
|
+
} | {
|
|
10
|
+
backing: "module";
|
|
11
|
+
spec: string;
|
|
12
|
+
} | {
|
|
13
|
+
backing: "hosted";
|
|
14
|
+
} | {
|
|
15
|
+
backing: "none";
|
|
16
|
+
why: string;
|
|
17
|
+
} | {
|
|
18
|
+
backing: "conflict";
|
|
19
|
+
why: string;
|
|
20
|
+
};
|
|
21
|
+
/** The four seat seams — where the residency ROW lives, and nothing else. */
|
|
22
|
+
export interface SeatBacking {
|
|
23
|
+
claim: (which: string | "any") => Promise<ResidencyClaim | null>;
|
|
24
|
+
heartbeat: (residencyId: string, fence: string) => Promise<void>;
|
|
25
|
+
release: (residencyId: string, fence: string, status: "hibernated" | "unseated") => Promise<void>;
|
|
26
|
+
cursorAdvance: (residencyId: string, fence: string, n: number) => Promise<number>;
|
|
27
|
+
}
|
|
28
|
+
/** Checked IN THIS ORDER, so a refusal names the first thing a hand-built backing forgot. */
|
|
29
|
+
export declare const SEAT_MEMBERS: readonly ["claim", "heartbeat", "release", "cursorAdvance"];
|
|
30
|
+
export type SeatResolution = {
|
|
31
|
+
ok: true;
|
|
32
|
+
seat: SeatBacking;
|
|
33
|
+
backing: "local" | "module" | "hosted";
|
|
34
|
+
} | {
|
|
35
|
+
ok: false;
|
|
36
|
+
refusal: "no_backend" | "backing_conflict";
|
|
37
|
+
seam: string;
|
|
38
|
+
message: string;
|
|
39
|
+
};
|
|
40
|
+
export interface SeatSeed {
|
|
41
|
+
agent_slug: string;
|
|
42
|
+
org: string;
|
|
43
|
+
venue_slug: string;
|
|
44
|
+
channel_id: string;
|
|
45
|
+
}
|
|
46
|
+
export declare function selectResidencyBacking(env: Record<string, string | undefined>): ResidencyBackingChoice;
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a choice to a usable seat, or refuse naming what is missing. A hosted choice resolves ONLY
|
|
49
|
+
* to what a deployment injected — the engine will not conjure one, which is the whole of its
|
|
50
|
+
* platform-agnosticism. `injected.module` is accepted so a hand-built backing can be handed in
|
|
51
|
+
* directly (and so a law can drive one without writing a file).
|
|
52
|
+
*/
|
|
53
|
+
export declare function resolveSeatBacking(choice: ResidencyBackingChoice, injected?: {
|
|
54
|
+
hosted?: Partial<SeatBacking>;
|
|
55
|
+
module?: Partial<SeatBacking>;
|
|
56
|
+
}): Promise<SeatResolution>;
|
|
57
|
+
/** Put one seat on a local roster so there is something to claim. The local counterpart of seating. */
|
|
58
|
+
export declare function fileSeatSeed(root: string, seed: SeatSeed): Promise<string>;
|
|
59
|
+
/**
|
|
60
|
+
* A real seat on disk — not a stub. It holds a lease (so two boxes cannot both claim one roster) and
|
|
61
|
+
* it refuses a cursor regression, because a local backing that were laxer than the hosted one it
|
|
62
|
+
* stands in for would let a bug pass locally and fail only in production.
|
|
63
|
+
*/
|
|
64
|
+
export declare function fileSeatBacking(root: string): SeatBacking;
|