@nimbus-sh/fabric 0.1.0 → 0.2.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.
Files changed (103) hide show
  1. package/README.md +84 -55
  2. package/dist/bindings.js +5 -5
  3. package/dist/budgets.d.ts +132 -0
  4. package/dist/budgets.d.ts.map +1 -0
  5. package/dist/budgets.js +248 -0
  6. package/dist/composition.d.ts +87 -0
  7. package/dist/composition.d.ts.map +1 -0
  8. package/dist/composition.js +76 -0
  9. package/dist/connections.d.ts +81 -0
  10. package/dist/connections.d.ts.map +1 -0
  11. package/dist/connections.js +114 -0
  12. package/dist/derived.d.ts +65 -0
  13. package/dist/derived.d.ts.map +1 -0
  14. package/dist/derived.js +95 -0
  15. package/dist/do-calls.d.ts +94 -0
  16. package/dist/do-calls.d.ts.map +1 -0
  17. package/dist/do-calls.js +111 -0
  18. package/dist/facet-pool.d.ts +90 -0
  19. package/dist/facet-pool.d.ts.map +1 -0
  20. package/dist/facet-pool.js +113 -0
  21. package/dist/{fanout-pool.d.ts → fanout.d.ts} +20 -20
  22. package/dist/fanout.d.ts.map +1 -0
  23. package/dist/{fanout-pool.js → fanout.js} +20 -20
  24. package/dist/{launch-journal.d.ts → fenced-work.d.ts} +25 -13
  25. package/dist/fenced-work.d.ts.map +1 -0
  26. package/dist/{launch-journal.js → fenced-work.js} +47 -13
  27. package/dist/generation.d.ts +69 -0
  28. package/dist/generation.d.ts.map +1 -0
  29. package/dist/generation.js +118 -0
  30. package/dist/{facet-image-store.d.ts → image-store.d.ts} +8 -8
  31. package/dist/image-store.d.ts.map +1 -0
  32. package/dist/{facet-image-store.js → image-store.js} +4 -4
  33. package/dist/index.d.ts +16 -8
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +16 -8
  36. package/dist/{loader-pool.d.ts → isolate-pool.d.ts} +19 -19
  37. package/dist/isolate-pool.d.ts.map +1 -0
  38. package/dist/{loader-pool.js → isolate-pool.js} +20 -20
  39. package/dist/journal.d.ts +111 -0
  40. package/dist/journal.d.ts.map +1 -0
  41. package/dist/journal.js +177 -0
  42. package/dist/outbox.d.ts +249 -0
  43. package/dist/outbox.d.ts.map +1 -0
  44. package/dist/outbox.js +355 -0
  45. package/dist/process-fabric.d.ts +2 -14
  46. package/dist/process-fabric.d.ts.map +1 -1
  47. package/dist/process-fabric.js +6 -15
  48. package/dist/process-host.d.ts +1 -1
  49. package/dist/process-host.d.ts.map +1 -1
  50. package/dist/process-host.js +10 -9
  51. package/dist/sealed.d.ts +78 -0
  52. package/dist/sealed.d.ts.map +1 -0
  53. package/dist/sealed.js +145 -0
  54. package/dist/timers.d.ts +138 -0
  55. package/dist/timers.d.ts.map +1 -0
  56. package/dist/timers.js +231 -0
  57. package/dist/{launch-pacer.d.ts → turn-budget.d.ts} +19 -21
  58. package/dist/turn-budget.d.ts.map +1 -0
  59. package/dist/{launch-pacer.js → turn-budget.js} +22 -11
  60. package/dist/workerd-facet-host.d.ts +28 -67
  61. package/dist/workerd-facet-host.d.ts.map +1 -1
  62. package/dist/workerd-facet-host.js +49 -171
  63. package/examples/agent-core-adapter.ts +191 -0
  64. package/package.json +4 -2
  65. package/src/bindings.ts +6 -6
  66. package/src/budgets.ts +308 -0
  67. package/src/composition.ts +127 -0
  68. package/src/connections.ts +140 -0
  69. package/src/derived.ts +135 -0
  70. package/src/do-calls.ts +156 -0
  71. package/src/facet-pool.ts +157 -0
  72. package/src/{fanout-pool.ts → fanout.ts} +35 -35
  73. package/src/{launch-journal.ts → fenced-work.ts} +58 -22
  74. package/src/generation.ts +144 -0
  75. package/src/{facet-image-store.ts → image-store.ts} +9 -9
  76. package/src/index.ts +16 -8
  77. package/src/{loader-pool.ts → isolate-pool.ts} +34 -34
  78. package/src/journal.ts +242 -0
  79. package/src/node-async-hooks.d.ts +14 -0
  80. package/src/outbox.ts +520 -0
  81. package/src/process-fabric.ts +6 -33
  82. package/src/process-host.ts +10 -15
  83. package/src/sealed.ts +150 -0
  84. package/src/timers.ts +294 -0
  85. package/src/{launch-pacer.ts → turn-budget.ts} +30 -24
  86. package/src/workerd-facet-host.ts +67 -193
  87. package/dist/alarms.d.ts +0 -134
  88. package/dist/alarms.d.ts.map +0 -1
  89. package/dist/alarms.js +0 -214
  90. package/dist/ctx-exports.d.ts +0 -47
  91. package/dist/ctx-exports.d.ts.map +0 -1
  92. package/dist/ctx-exports.js +0 -54
  93. package/dist/facet-image-store.d.ts.map +0 -1
  94. package/dist/fanout-pool.d.ts.map +0 -1
  95. package/dist/launch-journal.d.ts.map +0 -1
  96. package/dist/launch-pacer.d.ts.map +0 -1
  97. package/dist/loader-ledger.d.ts +0 -57
  98. package/dist/loader-ledger.d.ts.map +0 -1
  99. package/dist/loader-ledger.js +0 -91
  100. package/dist/loader-pool.d.ts.map +0 -1
  101. package/src/alarms.ts +0 -275
  102. package/src/ctx-exports.ts +0 -77
  103. package/src/loader-ledger.ts +0 -112
package/dist/outbox.js ADDED
@@ -0,0 +1,355 @@
1
+ /**
2
+ * outbox.ts — a durable retry outbox over a Durable Object's own SQLite.
3
+ *
4
+ * Proteus built this discipline twice by hand and says so: its email outbox
5
+ * (`cf-backend/src/email/outbox.ts:7` — "The discipline mirrors the peer
6
+ * outbox", 8 attempts from a 30s base) and its peer transport
7
+ * (`core/src/events/ingress/peer.ts` — 8 attempts from a 5s base, per-receiver
8
+ * ordering, an ask/reply waiter over the same rows). Both carry the same
9
+ * mechanism: a write-ahead intent row in `state='pending'`, an
10
+ * `attempt_count`, a `next_attempt_at` with a partial index over pending rows,
11
+ * backoff `next = now + base * 2**(attempts-1)`, and a `nextRetryAt()` folded
12
+ * into the object's single alarm. This module is that mechanism once.
13
+ *
14
+ * What the consumers proved and this keeps:
15
+ * - WRITE-AHEAD. The row commits before send runs. On a Durable Object the
16
+ * output gate orders it: an outbound message cannot leave before the
17
+ * writes of its turn are durable, so no explicit sync is needed here.
18
+ * - DISPOSITION, not a boolean. peer.ts:476-526 separates three failures: a
19
+ * RESOLVED refusal is permanent (dlq now), a THROWN send is transport
20
+ * trouble (backoff), a malformed row is poison (dlq now, no send). A
21
+ * boolean cannot carry that, and retrying a refusal re-offends.
22
+ * - ORDERING per key. Rows drain in id order. A transient failure blocks
23
+ * later rows with the same order key until the head clears (peer.ts:465
24
+ * head-of-line set); a dead-lettered head does not block, and keyless rows
25
+ * never block each other (the email outbox has no ordering at all).
26
+ * - IDEMPOTENCY. A dedupe key already queued or sent is refused with the
27
+ * existing id, and a sent key never reaches send again.
28
+ *
29
+ * What the consumers lacked and this adds:
30
+ * - The drain registers with `timers` (one reason in the shared map) instead
31
+ * of owning an alarm, and the dispatch-side handler re-arms through its
32
+ * RETURN value — calling schedule() from inside the dispatcher's chain
33
+ * would deadlock on the chain that serializes the reason map. A host
34
+ * whose alarm belongs to another framework (the Agents SDK, agent-core's
35
+ * reconciler) uses the scheduler-seam form instead: fabric hands every
36
+ * next due time to the policy's `schedule` and touches no timer storage.
37
+ * - The drain is turn-bounded through a {@link TurnBudget}: both consumer
38
+ * drains iterate every due row in one turn, which is the same
39
+ * thread-holding shape the pacer exists to end.
40
+ *
41
+ * ADOPTION IS A DATA MIGRATION for a live consumer. The rows live in
42
+ * fabric's own table (`outbox_<name>`), and nothing moves itself: a consumer
43
+ * with pending intents or a sent-key dedupe history in its old table keeps
44
+ * them there. The ported EmailOutbox went from `email_outbox` to
45
+ * `outbox_email` — deployed live without a migration, its pending mail would
46
+ * never send and every sent key would deliver a second time.
47
+ */
48
+ import { z } from 'zod/v4';
49
+ import { timers } from './timers.js';
50
+ const PendingRowSchema = z.object({
51
+ id: z.string(),
52
+ message: z.string(),
53
+ order_key: z.string().nullable(),
54
+ attempt_count: z.number(),
55
+ next_attempt_at: z.number(),
56
+ });
57
+ const DlqRowSchema = z.object({
58
+ id: z.string(),
59
+ message: z.string(),
60
+ dedupe_key: z.string().nullable(),
61
+ attempt_count: z.number(),
62
+ last_error: z.string().nullable(),
63
+ });
64
+ const RecordRowSchema = z.object({
65
+ id: z.string(),
66
+ state: z.enum(['pending', 'sent', 'dlq']),
67
+ message: z.string(),
68
+ dedupe_key: z.string().nullable(),
69
+ attempt_count: z.number(),
70
+ last_error: z.string().nullable(),
71
+ });
72
+ const NAME_PATTERN = /^[a-z][a-z0-9_]{0,40}$/;
73
+ export function outbox(...args) {
74
+ if (args.length === 4) {
75
+ const [host, ctx, name, policy] = args;
76
+ return new Outbox(ctx, name, policy, { kind: 'timers', host, ctx });
77
+ }
78
+ const [ctx, name, policy] = args;
79
+ return new Outbox(ctx, name, policy, { kind: 'schedule', schedule: (at) => policy.schedule(at) });
80
+ }
81
+ export class Outbox {
82
+ ctx;
83
+ policy;
84
+ scheduling;
85
+ /** The timer reason this outbox arms in the shared map. */
86
+ reason;
87
+ table;
88
+ schemaReady = false;
89
+ draining = false;
90
+ /** Largest id ever seen, so a replacement instance mints above it. */
91
+ lastId = '';
92
+ seq = 0;
93
+ constructor(ctx, name, policy, scheduling) {
94
+ this.ctx = ctx;
95
+ this.policy = policy;
96
+ this.scheduling = scheduling;
97
+ if (!NAME_PATTERN.test(name)) {
98
+ throw new Error(`fabric: outbox name '${name}' must match ${NAME_PATTERN}`);
99
+ }
100
+ this.table = `outbox_${name}`;
101
+ this.reason = `outbox:${name}`;
102
+ }
103
+ ensureSchema() {
104
+ if (this.schemaReady)
105
+ return;
106
+ const sql = this.ctx.storage.sql;
107
+ sql.exec(`CREATE TABLE IF NOT EXISTS ${this.table} (
108
+ id TEXT PRIMARY KEY,
109
+ dedupe_key TEXT,
110
+ order_key TEXT,
111
+ message TEXT NOT NULL,
112
+ state TEXT NOT NULL DEFAULT 'pending'
113
+ CHECK (state IN ('pending', 'sent', 'dlq')),
114
+ attempt_count INTEGER NOT NULL DEFAULT 0,
115
+ next_attempt_at INTEGER NOT NULL,
116
+ created_at INTEGER NOT NULL,
117
+ sent_at INTEGER,
118
+ last_error TEXT
119
+ )`);
120
+ sql.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_${this.table}_dedupe
121
+ ON ${this.table} (dedupe_key) WHERE dedupe_key IS NOT NULL`);
122
+ // The recovery read (`nextRetryAt`, the drain) must not table-scan sent
123
+ // history, which only grows — the same reason both consumer schemas carry
124
+ // exactly this partial index.
125
+ sql.exec(`CREATE INDEX IF NOT EXISTS idx_${this.table}_pending
126
+ ON ${this.table} (next_attempt_at) WHERE state = 'pending'`);
127
+ const rows = [...sql.exec(`SELECT MAX(id) AS id FROM ${this.table}`)];
128
+ this.lastId = rows[0]?.id ?? '';
129
+ this.schemaReady = true;
130
+ }
131
+ /**
132
+ * Ids order the drain, so they must grow: time-prefixed, tie-broken by a
133
+ * per-instance counter, and forced above the largest stored id so a
134
+ * replacement instance with a lagging clock cannot mint into the past.
135
+ */
136
+ mintId(now) {
137
+ let id = `${now.toString(36).padStart(9, '0')}-${(this.seq++).toString(36).padStart(6, '0')}`;
138
+ if (this.lastId !== '' && id <= this.lastId)
139
+ id = `${this.lastId}0`;
140
+ this.lastId = id;
141
+ return id;
142
+ }
143
+ /**
144
+ * Write the intent ahead of any send. Returns `admitted: false` with the
145
+ * existing id when the dedupe key is already queued, sent, or dead-lettered
146
+ * — a sent key never reaches send again (the email outbox's short-circuit).
147
+ *
148
+ * `onDuplicate: 'retry-now'` treats a re-ask for an UNSENT key as new
149
+ * intent: the row's retry instant is clamped to `now` and a dead letter
150
+ * returns to 'pending', its attempt count kept — so every re-ask buys one
151
+ * more delivery attempt, past the attempt budget. The monitor's alert-once
152
+ * contract needs exactly this: a failed alert is retried by each sweep
153
+ * until it lands. A sent key stays final either way.
154
+ *
155
+ * Arms the scheduler at `now`, so delivery is owed by the alarm even
156
+ * when the caller never drains inline.
157
+ */
158
+ async queue(message, opts = {}) {
159
+ this.ensureSchema();
160
+ const now = opts.now ?? Date.now();
161
+ const sql = this.ctx.storage.sql;
162
+ if (opts.dedupeKey !== undefined) {
163
+ const existing = [...sql.exec(`SELECT id, state FROM ${this.table} WHERE dedupe_key = ?`, opts.dedupeKey)];
164
+ if (existing.length > 0) {
165
+ if (opts.onDuplicate === 'retry-now' && existing[0].state !== 'sent') {
166
+ sql.exec(`UPDATE ${this.table} SET state = 'pending', next_attempt_at = ? WHERE id = ?`, now, existing[0].id);
167
+ await this.arm(now);
168
+ }
169
+ return { id: existing[0].id, admitted: false };
170
+ }
171
+ }
172
+ const id = this.mintId(now);
173
+ sql.exec(`INSERT INTO ${this.table} (id, dedupe_key, order_key, message, next_attempt_at, created_at)
174
+ VALUES (?, ?, ?, ?, ?, ?)`, id, opts.dedupeKey ?? null, this.policy.orderBy?.(message) ?? null, JSON.stringify(message), now, now);
175
+ await this.arm(now);
176
+ return { id, admitted: true };
177
+ }
178
+ /** Hand a due time to whichever scheduler this outbox was built on. */
179
+ async arm(at) {
180
+ if (this.scheduling.kind === 'timers') {
181
+ await timers(this.scheduling.host, this.scheduling.ctx).schedule(this.reason, at);
182
+ }
183
+ else {
184
+ await this.scheduling.schedule(at);
185
+ }
186
+ }
187
+ /** The single-alarm fold: the earliest pending deadline, or null. */
188
+ nextRetryAt() {
189
+ this.ensureSchema();
190
+ const rows = [...this.ctx.storage.sql.exec(`SELECT MIN(next_attempt_at) AS next FROM ${this.table} WHERE state = 'pending'`)];
191
+ return rows[0]?.next ?? null;
192
+ }
193
+ /** The row queue() named, by its id. Null when no such row exists. */
194
+ status(id) {
195
+ return this.record('id', id);
196
+ }
197
+ /** The row a dedupe key admitted, whatever its state. Null when none. */
198
+ find(dedupeKey) {
199
+ return this.record('dedupe_key', dedupeKey);
200
+ }
201
+ record(column, value) {
202
+ this.ensureSchema();
203
+ const rows = [...this.ctx.storage.sql.exec(`SELECT id, state, message, dedupe_key, attempt_count, last_error
204
+ FROM ${this.table} WHERE ${column} = ?`, value)];
205
+ if (rows.length === 0)
206
+ return null;
207
+ const row = RecordRowSchema.parse(rows[0]);
208
+ let message = null;
209
+ try {
210
+ message = JSON.parse(row.message);
211
+ }
212
+ catch { /* poison-parse row */ }
213
+ return {
214
+ id: row.id,
215
+ state: row.state,
216
+ message,
217
+ dedupeKey: row.dedupe_key,
218
+ attemptCount: row.attempt_count,
219
+ lastError: row.last_error,
220
+ };
221
+ }
222
+ /** Dead-lettered rows, for inspection. Terminal: nothing retries out. */
223
+ dlq() {
224
+ this.ensureSchema();
225
+ const rows = [...this.ctx.storage.sql.exec(`SELECT id, message, dedupe_key, attempt_count, last_error FROM ${this.table} WHERE state = 'dlq' ORDER BY id`)].map((row) => DlqRowSchema.parse(row));
226
+ return rows.map((row) => {
227
+ let message = null;
228
+ try {
229
+ message = JSON.parse(row.message);
230
+ }
231
+ catch { /* poison-parse row */ }
232
+ return {
233
+ id: row.id,
234
+ message,
235
+ dedupeKey: row.dedupe_key,
236
+ attemptCount: row.attempt_count,
237
+ lastError: row.last_error ?? '',
238
+ };
239
+ });
240
+ }
241
+ /**
242
+ * Deliver every due pending row, in id order, honouring per-key blocking.
243
+ * Reentrancy-guarded: the alarm and an inline post-queue drain overlap on
244
+ * the same activation (peer.ts:452 carries the same guard).
245
+ *
246
+ * `budget` bounds the turn: the drain spends each processed row's payload
247
+ * size and suspends when a chunk is full, so a large backlog crosses turns
248
+ * instead of holding the actor's only thread.
249
+ *
250
+ * `context` is handed to every `send` this drain makes — the seam for
251
+ * per-call state such as a transport binding resolved by the caller.
252
+ */
253
+ async drain(now = Date.now(), ...rest) {
254
+ const opts = rest[0] ?? {};
255
+ const result = { sent: 0, retried: 0, deadLettered: 0 };
256
+ if (this.draining)
257
+ return result;
258
+ this.draining = true;
259
+ try {
260
+ this.ensureSchema();
261
+ const sql = this.ctx.storage.sql;
262
+ const rows = [...sql.exec(`SELECT id, message, order_key, attempt_count, next_attempt_at
263
+ FROM ${this.table} WHERE state = 'pending' ORDER BY id`)].map((row) => PendingRowSchema.parse(row));
264
+ const blocked = new Set();
265
+ for (const row of rows) {
266
+ if (row.order_key !== null && blocked.has(row.order_key))
267
+ continue;
268
+ if (row.next_attempt_at > now) {
269
+ // A backed-off head still blocks the rows queued behind it.
270
+ if (row.order_key !== null)
271
+ blocked.add(row.order_key);
272
+ continue;
273
+ }
274
+ let message;
275
+ try {
276
+ message = JSON.parse(row.message);
277
+ }
278
+ catch (e) {
279
+ // Unparseable is poison: it can never succeed, so it never blocks.
280
+ this.deadLetter(row.id, row.attempt_count, `outbox row does not parse: ${errorText(e)}`);
281
+ result.deadLettered++;
282
+ continue;
283
+ }
284
+ const attempt = row.attempt_count + 1;
285
+ let disposition;
286
+ try {
287
+ disposition = await this.policy.send(message, { id: row.id, attempt }, opts.context);
288
+ }
289
+ catch (e) {
290
+ disposition = { status: 'retry', reason: errorText(e) };
291
+ }
292
+ if (disposition.status === 'sent') {
293
+ sql.exec(`UPDATE ${this.table} SET state = 'sent', attempt_count = ?, sent_at = ?, last_error = NULL WHERE id = ?`, attempt, now, row.id);
294
+ result.sent++;
295
+ }
296
+ else if (disposition.status === 'poison') {
297
+ this.deadLetter(row.id, attempt, disposition.reason);
298
+ result.deadLettered++;
299
+ }
300
+ else if (attempt >= this.policy.maxAttempts) {
301
+ this.deadLetter(row.id, attempt, `undeliverable after ${attempt} attempts: ${disposition.reason}`);
302
+ result.deadLettered++;
303
+ }
304
+ else {
305
+ const next = now + this.policy.baseMs * 2 ** (attempt - 1);
306
+ sql.exec(`UPDATE ${this.table} SET attempt_count = ?, next_attempt_at = ?, last_error = ? WHERE id = ?`, attempt, next, disposition.reason, row.id);
307
+ result.retried++;
308
+ if (row.order_key !== null)
309
+ blocked.add(row.order_key);
310
+ }
311
+ // Code-unit length: equal to UTF-8 bytes for the ASCII JSON the rows
312
+ // hold, an undercount otherwise — the bound names where a turn ends,
313
+ // it is not an admission ceiling (same reading as budgets.ts).
314
+ await opts.budget?.spend(row.message.length);
315
+ }
316
+ // A seam-scheduled outbox re-arms itself: there is no dispatcher whose
317
+ // return value could, and the ported consumer re-armed by hand after
318
+ // every drain — delivery stays owed either way.
319
+ if (this.scheduling.kind === 'schedule') {
320
+ const next = this.nextRetryAt();
321
+ if (next !== null)
322
+ await this.scheduling.schedule(next);
323
+ }
324
+ return result;
325
+ }
326
+ finally {
327
+ this.draining = false;
328
+ }
329
+ }
330
+ deadLetter(id, attempts, error) {
331
+ this.ctx.storage.sql.exec(`UPDATE ${this.table} SET state = 'dlq', attempt_count = ?, last_error = ? WHERE id = ?`, attempts, error, id);
332
+ }
333
+ /**
334
+ * The dispatch-side entry for the embedder's timer handler map. Re-arms
335
+ * through the RETURN value: the dispatcher runs handlers inside the chain
336
+ * that serializes the reason map, so a schedule() call from here would
337
+ * deadlock on its own chain.
338
+ *
339
+ * An alarm fires with no caller, so a policy that declares a drain context
340
+ * receives it here once, closed over every alarm-driven drain.
341
+ */
342
+ handler(...context) {
343
+ if (this.scheduling.kind === 'schedule') {
344
+ throw new Error(`fabric: outbox '${this.reason}' uses a scheduler seam — there is no timer dispatcher to hand this handler to`);
345
+ }
346
+ return async (now) => {
347
+ await this.drain(now, ...[{ context: context[0] }]);
348
+ const next = this.nextRetryAt();
349
+ return next === null ? undefined : { rearmAt: next };
350
+ };
351
+ }
352
+ }
353
+ function errorText(error) {
354
+ return error instanceof Error ? error.message : String(error);
355
+ }
@@ -5,7 +5,7 @@
5
5
  * Every long-lived process Nimbus runs — node servers, python/ruby socket
6
6
  * servers, an agent TUI and its headless server — runs as a **DO Facet**:
7
7
  * a named child actor whose class comes from a dynamic worker, opened by
8
- * `openResidentFacet` in `workerd-facet-host.ts`.
8
+ * `processes(ctx, env).spawn` in `workerd-facet-host.ts`.
9
9
  *
10
10
  * ctx.facets.get(`proc-${pid}`, () => ({
11
11
  * class: env.LOADER.get(workerKey, buildConfig)
@@ -141,18 +141,6 @@ export type ResidentBootSpec = {
141
141
  kind: 'code';
142
142
  code: ResidentCodeSpec;
143
143
  };
144
- /**
145
- * Assemble a complete Worker Loader config from a staged-artifact spec. The
146
- * embedder supplies this: a stage names artifact sources only the embedder
147
- * knows how to fetch (Nimbus's largest staged artifact is a ~23 MB module map from
148
- * ASSETS), and the assembler runs inside the loader's cache-miss callback so
149
- * those sources are materialized only while the facet actually loads.
150
- * `env` is whichever hosting actor's env the facet is opened with.
151
- */
152
- export type StagedBootAssembler = (env: unknown, stage: unknown) => Promise<object>;
153
- /** Registered once at composition time, first-write-wins. */
154
- export declare function setStagedBootAssembler(assembler: StagedBootAssembler): void;
155
- export declare function requireStagedBootAssembler(): StagedBootAssembler;
156
144
  /**
157
145
  * Where a generated module source is materialized so a boot spec can name it.
158
146
  *
@@ -324,7 +312,7 @@ export interface ProcessImageDelivery {
324
312
  * ends: the source exists and is populated before, the destination is
325
313
  * non-empty after. A blocklist of bad names would pass a typo straight
326
314
  * through and wipe a process's filesystem while returning ok. Enforced by
327
- * `cloneFacetStorage` in the workerd host, which is the one way the fabric
315
+ * `cloneStorage` in the workerd host, which is the one way the fabric
328
316
  * calls clone.
329
317
  */
330
318
  readonly reflink: 'same-object' | 'impossible';
@@ -1 +1 @@
1
- {"version":3,"file":"process-fabric.d.ts","sourceRoot":"","sources":["../src/process-fabric.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"process-fabric.d.ts","sourceRoot":"","sources":["../src/process-fabric.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@
5
5
  * Every long-lived process Nimbus runs — node servers, python/ruby socket
6
6
  * servers, an agent TUI and its headless server — runs as a **DO Facet**:
7
7
  * a named child actor whose class comes from a dynamic worker, opened by
8
- * `openResidentFacet` in `workerd-facet-host.ts`.
8
+ * `processes(ctx, env).spawn` in `workerd-facet-host.ts`.
9
9
  *
10
10
  * ctx.facets.get(`proc-${pid}`, () => ({
11
11
  * class: env.LOADER.get(workerKey, buildConfig)
@@ -131,20 +131,11 @@ export function residentBootSpecSchema(stageSchema) {
131
131
  z.object({ kind: z.literal('code'), code: ResidentCodeSpecSchema }),
132
132
  ]);
133
133
  }
134
- let _stagedBootAssembler = null;
135
- /** Registered once at composition time, first-write-wins. */
136
- export function setStagedBootAssembler(assembler) {
137
- if (_stagedBootAssembler)
138
- return;
139
- _stagedBootAssembler = assembler;
140
- }
141
- export function requireStagedBootAssembler() {
142
- if (!_stagedBootAssembler) {
143
- throw new Error('fabric: no staged-boot assembler registered; a \'staged\' boot spec '
144
- + 'cannot be assembled without one (setStagedBootAssembler)');
145
- }
146
- return _stagedBootAssembler;
147
- }
134
+ // ── Staged boots ────────────────────────────────────────────────────────────
135
+ //
136
+ // A 'staged' boot spec assembles through the embedder's composed
137
+ // StagedBootAssembler — see composition.ts. What a stage IS belongs to
138
+ // whoever composed the assembler.
148
139
  // ── Boot-image store ────────────────────────────────────────────────────────
149
140
  /**
150
141
  * Where a generated module source is materialized so a boot spec can name it.
@@ -9,7 +9,7 @@
9
9
  * peer — the process is a named child actor of a SIBLING session DO, and
10
10
  * the coordinator reaches it over one held-open RPC.
11
11
  *
12
- * Both call the same `openResidentFacet`. The peer leg is not a second process
12
+ * Both call the same `processes(ctx, env).spawn`. The peer leg is not a second process
13
13
  * implementation; it is the same call made on a different actor, which is why
14
14
  * the runner, the boot spec, the class name, the writer handshake, the start
15
15
  * contract and the lifecycle are shared code rather than parallel paths.
@@ -1 +1 @@
1
- {"version":3,"file":"process-host.d.ts","sourceRoot":"","sources":["../src/process-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAKH,OAAO,EAGL,KAAK,WAAW,EAIhB,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;AAU7B,4DAA4D;AAC5D,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,eAAe,EACrB,GAAG,EAAE,kBAAkB,EACvB,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,kBAAkB,GAC7B,WAAW,CAIb;AAgFD,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;CAC7B;AA2BD;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,8BAA8B,CAAC;AACvE,eAAO,MAAM,kCAAkC,yCAAyC,CAAC;AA0RzF;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAWhE"}
1
+ {"version":3,"file":"process-host.d.ts","sourceRoot":"","sources":["../src/process-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAKH,OAAO,EAGL,KAAK,WAAW,EAIhB,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;AAS7B,4DAA4D;AAC5D,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,eAAe,EACrB,GAAG,EAAE,kBAAkB,EACvB,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,kBAAkB,GAC7B,WAAW,CAIb;AA8ED,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;CAC7B;AA2BD;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,8BAA8B,CAAC;AACvE,eAAO,MAAM,kCAAkC,yCAAyC,CAAC;AAwRzF;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAWhE"}
@@ -9,7 +9,7 @@
9
9
  * peer — the process is a named child actor of a SIBLING session DO, and
10
10
  * the coordinator reaches it over one held-open RPC.
11
11
  *
12
- * Both call the same `openResidentFacet`. The peer leg is not a second process
12
+ * Both call the same `processes(ctx, env).spawn`. The peer leg is not a second process
13
13
  * implementation; it is the same call made on a different actor, which is why
14
14
  * the runner, the boot spec, the class name, the writer handshake, the start
15
15
  * contract and the lifecycle are shared code rather than parallel paths.
@@ -63,10 +63,11 @@
63
63
  * {@link HOSTED_WEBSOCKET_KEY_HEADER} — and a per-process capability makes
64
64
  * that pair unforgeable by anything that did not open the process.
65
65
  */
66
- import { disposeRpcResource } from '@nimbus-sh/core/_shared/rpc-dispose.js';
67
- import { isTransientDoReset } from '@nimbus-sh/core/observability/oom-classify.js';
68
- import { PEER_RETRY_BACKOFF_MS, PEER_TRANSIENT_RESET_RETRIES } from './fanout-pool.js';
69
- import { DYNAMIC_WORKER_CODE_LIMIT_BYTES, openResidentFacet, residentFacetName, runOneShotWorker, } from './workerd-facet-host.js';
66
+ import { disposeRpcResource } from '@nimbus-sh/platform/rpc-dispose.js';
67
+ import { isTransientDoReset } from '@nimbus-sh/platform/oom-classify.js';
68
+ import { PEER_RETRY_BACKOFF_MS, PEER_TRANSIENT_RESET_RETRIES } from './fanout.js';
69
+ import { DYNAMIC_WORKER_CODE_LIMIT_BYTES } from './budgets.js';
70
+ import { processes, residentFacetName, } from './workerd-facet-host.js';
70
71
  import { BindingError } from './vendor/errors.js';
71
72
  /**
72
73
  * The substrate for this deployment, resolved once. The mode arrives already
@@ -105,7 +106,7 @@ class FacetProcessHost {
105
106
  this.coordDoId = ctx.id.toString();
106
107
  }
107
108
  runOnce(params, consume) {
108
- return runOneShotWorker(this.ctx, this.env, { doId: this.coordDoId, pid: params.pid, writerId: params.writerId }, params, consume);
109
+ return processes(this.ctx, this.env).run({ doId: this.coordDoId, pid: params.pid, writerId: params.writerId }, params, consume);
109
110
  }
110
111
  async open(params) {
111
112
  const supervisor = {
@@ -113,7 +114,7 @@ class FacetProcessHost {
113
114
  pid: params.pid,
114
115
  writerId: params.writerId,
115
116
  };
116
- const { slot, ...facet } = openResidentFacet(this.ctx, this.env, this.disk, supervisor, params);
117
+ const { slot, ...facet } = processes(this.ctx, this.env).spawn(this.disk, supervisor, params);
117
118
  return {
118
119
  ...facet,
119
120
  describe: () => `facet '${residentFacetName(slot)}' (pid ${params.pid})`
@@ -231,7 +232,7 @@ class PeerProcessHost {
231
232
  * worker of the coordinator here exactly as it does on `facet`.
232
233
  */
233
234
  runOnce(params, consume) {
234
- return runOneShotWorker(this.ctx, this.env, { doId: this.coordDoId, pid: params.pid, writerId: params.writerId }, params, consume);
235
+ return processes(this.ctx, this.env).run({ doId: this.coordDoId, pid: params.pid, writerId: params.writerId }, params, consume);
235
236
  }
236
237
  async open(params) {
237
238
  const placement = await this._place(params.pid);
@@ -273,7 +274,7 @@ class PeerProcessHost {
273
274
  catch (error) {
274
275
  this.tokensInUse.delete(params.pid);
275
276
  // Awaited, not fired off: a spawn that rejects must mean nothing was
276
- // left running, which is what a throw from `openResidentFacet` means on
277
+ // left running, which is what a throw from `processes().spawn` means on
277
278
  // the other substrate.
278
279
  try {
279
280
  await this._cancel(params.workerKey, placement.peerName);
@@ -0,0 +1,78 @@
1
+ /**
2
+ * sealed.ts — prototype-chain RPC sealing, and the versioned surface
3
+ * constants that make it safe to run over the Agents SDK.
4
+ *
5
+ * Cloudflare resolves `stub.foo(...)` on the receiver's PROTOTYPE CHAIN.
6
+ * Proteus verified the three consequences against real workerd
7
+ * (`cf-backend/src/rpc-surface.ts:4-22`, catalog `rpc.prototype_chain`,
8
+ * proven-by-probe): TypeScript `private` is erased, so private methods ARE
9
+ * callable over RPC; superclass methods are reachable too — inherited
10
+ * `Agent.sql` hands any stub-holder arbitrary SQL against the receiver's
11
+ * storage, and `Agent` + `Server` alone contribute hundreds of reachable
12
+ * names; and OWN instance properties are NOT reachable — workerd rejects
13
+ * them exactly as it rejects a missing name, including when the own
14
+ * property shadows a prototype method.
15
+ *
16
+ * That third consequence is the primitive: {@link sealRpcSurface} copies
17
+ * every reachable member that is NOT on the declared surface down onto the
18
+ * instance as a non-enumerable own property. In-process behaviour is
19
+ * unchanged — `this.x(...)` finds the same function object, `super.x()`
20
+ * still reaches the prototype, accessors stay accessors. From outside, the
21
+ * name has ceased to exist. Call it as the LAST statement of the
22
+ * constructor, after every base class installed its wrappers.
23
+ *
24
+ * The surface constants are the part that breaks whenever the SDK moves,
25
+ * which is why fabric owns them: Proteus reverse-engineered its facet
26
+ * surface from `agents/dist` by hand, and a fabric test diffs these
27
+ * constants against the INSTALLED packages so drift is caught by CI, not by
28
+ * a leak. Verified against agents@0.20.1 and partyserver@0.5.10
29
+ * (2026-08-19); an SDK upgrade that changes the cross-stub set fails that
30
+ * test and demands re-derivation, which is the intended failure.
31
+ */
32
+ /**
33
+ * Every name RPC can resolve on `target`: own property names of every
34
+ * prototype up to (and excluding) Object.prototype, minus `constructor` and
35
+ * minus anything `target` already carries as an own property — own
36
+ * properties are not RPC-reachable, so they need no seal. The single
37
+ * definition {@link sealRpcSurface} and its tests both work from.
38
+ */
39
+ export declare function rpcReachableNames(target: object): string[];
40
+ /**
41
+ * Shadow every RPC-reachable member not on `surface` with a non-enumerable
42
+ * own property carrying the SAME descriptor — the same function object, the
43
+ * same accessor pair — so in-process behaviour cannot change while the name
44
+ * stops resolving over RPC. Surface names the class lacks are ignored: a
45
+ * surface is a ceiling, and the runtime already denies what does not exist.
46
+ */
47
+ export declare function sealRpcSurface<Instance extends object>(instance: Instance, surface: readonly string[]): void;
48
+ /**
49
+ * The platform half of any surface built on partyserver's `Server` (and
50
+ * therefore the Agents SDK's `Agent`): what the RUNTIME and the SDK's own
51
+ * routing must still reach after sealing. From the consumer's verified list
52
+ * (`rpc-surface.ts:63-86`): `setName` is called by `getServerByName` before
53
+ * the stub is returned; `_initAndFetch` is `setName` plus `fetch`, so it
54
+ * exposes nothing new; the WebSocket handlers' arguments cannot cross an
55
+ * RPC boundary anyway.
56
+ */
57
+ export declare const PLATFORM_RPC_SURFACE: readonly ["fetch", "setName", "_initAndFetch", "alarm", "webSocketMessage", "webSocketClose", "webSocketError"];
58
+ /**
59
+ * The Agents SDK's own cross-stub facet protocol at agents@0.20.1: every
60
+ * `_cf_` method the SDK calls on a receiver that is not `this`, derived
61
+ * from `agents/dist` (the SDK's ACTUAL cross-stub surface, not a prefix
62
+ * rule — Agent's prototype defines 58 `_cf_` methods and only these are
63
+ * cross-called). An Agent that hosts SDK facets must keep these; one that
64
+ * does not (Proteus's UserDO) should not carry them at all.
65
+ */
66
+ export declare const AGENTS_FACET_RPC_SURFACE: readonly ["_cf_acquireFacetKeepAlive", "_cf_broadcastToSubAgent", "_cf_cancelScheduleForFacet", "_cf_checkRunFibersForFacet", "_cf_cleanupFacetPrefix", "_cf_closeSubAgentConnection", "_cf_destroyDescendantFacet", "_cf_dispatchScheduledCallback", "_cf_getScheduleForFacet", "_cf_handleSubAgentWebSocketClose", "_cf_handleSubAgentWebSocketConnect", "_cf_handleSubAgentWebSocketMessage", "_cf_initAsFacet", "_cf_listSchedulesForFacet", "_cf_registerFacetRun", "_cf_releaseFacetKeepAlive", "_cf_scheduleEveryForFacet", "_cf_scheduleForFacet", "_cf_sendToSubAgentConnection", "_cf_setSubAgentConnectionState", "_cf_subAgentConnectionMetas", "_cf_unregisterFacetRun"];
67
+ /**
68
+ * Cross-stub `_cf_` members deliberately absent from EVERY surface: each
69
+ * takes a method NAME and calls it on the receiver, which would re-open
70
+ * everything sealing closes. Sealing them fail-closes the SDK features
71
+ * built on them (`parentAgent()` proxies, workflow-to-agent bridges) — the
72
+ * consumer accepts exactly that cost, and fail-closed is the right default
73
+ * for a bridge that dispatches arbitrary names. (`_cf_invokeStubMethod` is
74
+ * only ever self-called in the pinned dist, but it is prototype-defined, so
75
+ * it is named here and sealed.)
76
+ */
77
+ export declare const AGENTS_INVOKE_BRIDGES: readonly ["_cf_invokeAgentPath", "_cf_invokeStubMethod", "_cf_invokeSubAgent", "_cf_invokeSubAgentPath"];
78
+ //# sourceMappingURL=sealed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sealed.d.ts","sourceRoot":"","sources":["../src/sealed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAY1D;AAYD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,SAAS,MAAM,EACpD,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,IAAI,CAON;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,iHAQvB,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,upBAuB3B,CAAC;AAEX;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,0GAKxB,CAAC"}