@intx/workflow-host 0.2.2 → 0.4.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/README.md +77 -14
- package/dist/adapters/mail-part-store.d.ts +46 -0
- package/dist/adapters/mail-part-store.js +251 -0
- package/dist/adapters/repo-store.d.ts +22 -1
- package/dist/adapters/repo-store.js +56 -65
- package/dist/adapters/spawn-child.d.ts +109 -44
- package/dist/adapters/spawn-child.js +77 -81
- package/dist/adapters/step-invoker.d.ts +52 -2
- package/dist/adapters/step-invoker.js +284 -37
- package/dist/adapters/substrate-mailbox-store.d.ts +80 -0
- package/dist/adapters/substrate-mailbox-store.js +404 -0
- package/dist/child/child-mailbox-reader.d.ts +10 -0
- package/dist/child/child-mailbox-reader.js +23 -0
- package/dist/child/credential-cell.d.ts +8 -0
- package/dist/child/credential-cell.js +66 -0
- package/dist/child/env-bootstrap.d.ts +20 -6
- package/dist/child/env-bootstrap.js +9 -1
- package/dist/child/from-process-env.d.ts +12 -0
- package/dist/child/from-process-env.js +6 -0
- package/dist/child/index.d.ts +6 -2
- package/dist/child/index.js +4 -1
- package/dist/child/mailbox-mutation-bridge.d.ts +61 -0
- package/dist/child/mailbox-mutation-bridge.js +101 -0
- package/dist/child/mailbox-watch-registry.d.ts +17 -0
- package/dist/child/mailbox-watch-registry.js +61 -0
- package/dist/child/outbound-mail-bridge.d.ts +3 -2
- package/dist/child/outbound-mail-bridge.js +20 -32
- package/dist/child/parked-correlations.d.ts +42 -0
- package/dist/child/parked-correlations.js +80 -0
- package/dist/child/pending-request.d.ts +89 -0
- package/dist/child/pending-request.js +80 -0
- package/dist/child/proxy-repo-store.d.ts +3 -2
- package/dist/child/proxy-repo-store.js +2 -0
- package/dist/child/run-child.d.ts +170 -14
- package/dist/child/run-child.js +569 -155
- package/dist/child/self-discovery.d.ts +10 -0
- package/dist/child/self-discovery.js +25 -1
- package/dist/child/substrate-write-bridge.d.ts +3 -2
- package/dist/child/substrate-write-bridge.js +21 -38
- package/dist/child/supervisor-backed-transport.d.ts +52 -6
- package/dist/child/supervisor-backed-transport.js +205 -62
- package/dist/child/verified-definition-loader.d.ts +33 -0
- package/dist/child/verified-definition-loader.js +43 -0
- package/dist/child/warm-agent-cache.d.ts +44 -4
- package/dist/child/warm-agent-cache.js +41 -10
- package/dist/index.d.ts +6 -4
- package/dist/index.js +6 -4
- package/dist/ipc/control-channel.d.ts +151 -2
- package/dist/ipc/control-channel.js +222 -29
- package/dist/ipc/event-channel.d.ts +32 -1
- package/dist/ipc/index.d.ts +1 -1
- package/dist/ipc/index.js +1 -1
- package/dist/mail-bus/hub-transport-adapter.d.ts +12 -7
- package/dist/mail-bus/hub-transport-adapter.js +9 -5
- package/dist/run-body-then-cleanup.d.ts +17 -0
- package/dist/run-body-then-cleanup.js +38 -0
- package/dist/seams/scheduler.d.ts +16 -6
- package/dist/seams/scheduler.js +87 -97
- package/dist/supervisor/cancel-signing.d.ts +2 -2
- package/dist/supervisor/cancel-signing.js +4 -8
- package/dist/supervisor/credentials.d.ts +28 -15
- package/dist/supervisor/credentials.js +7 -7
- package/dist/supervisor/dispatch-attribution.js +1 -1
- package/dist/supervisor/drain-timeout.d.ts +2 -2
- package/dist/supervisor/drain-timeout.js +1 -1
- package/dist/supervisor/index.d.ts +3 -3
- package/dist/supervisor/index.js +2 -2
- package/dist/supervisor/recycle.d.ts +10 -3
- package/dist/supervisor/recycle.js +18 -7
- package/dist/supervisor/run-event-compaction.d.ts +5 -5
- package/dist/supervisor/run-event-compaction.js +14 -19
- package/dist/supervisor/run-event-recovery.d.ts +34 -0
- package/dist/supervisor/run-event-recovery.js +45 -0
- package/dist/supervisor/spawn-env.d.ts +2 -2
- package/dist/supervisor/spawn-env.js +1 -1
- package/dist/supervisor/supervisor.d.ts +106 -26
- package/dist/supervisor/supervisor.js +1903 -414
- package/dist/supervisor/terminal-commit.d.ts +36 -0
- package/dist/supervisor/terminal-commit.js +130 -0
- package/dist/supervisor/types.d.ts +180 -23
- package/dist/testing/change-notifier.d.ts +12 -0
- package/dist/testing/change-notifier.js +63 -0
- package/dist/testing/index.d.ts +8 -0
- package/dist/testing/index.js +16 -0
- package/dist/testing/log-capture.d.ts +52 -0
- package/dist/testing/log-capture.js +124 -0
- package/dist/testing/mail-bus.d.ts +22 -0
- package/dist/testing/mail-bus.js +78 -0
- package/dist/testing/memory-streams.d.ts +43 -0
- package/dist/testing/memory-streams.js +211 -0
- package/dist/testing/spawn-observer.d.ts +12 -0
- package/dist/testing/spawn-observer.js +36 -0
- package/dist/testing/stub-repo-store.d.ts +10 -0
- package/dist/testing/stub-repo-store.js +39 -0
- package/dist/testing/supervisor-reaper.d.ts +24 -0
- package/dist/testing/supervisor-reaper.js +49 -0
- package/dist/testing/upstream-frames.d.ts +47 -0
- package/dist/testing/upstream-frames.js +94 -0
- package/dist/workflow-definition-loader.d.ts +187 -0
- package/dist/workflow-definition-loader.js +422 -0
- package/package.json +18 -11
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
// prevented at the type level by the disjoint discriminated union.
|
|
36
36
|
import { type } from "arktype";
|
|
37
37
|
import { hexDecode, hexEncode } from "@intx/types";
|
|
38
|
-
import { InferenceSource, InterchangeType } from "@intx/types/runtime";
|
|
38
|
+
import { BoundedApprovalSnapshot, ControlParkKind, InferenceSource, InterchangeType, } from "@intx/types/runtime";
|
|
39
|
+
import { CredentialDelivery } from "@intx/types/sidecar";
|
|
39
40
|
import { decodeEnvelope, encodeEnvelope, FrameEnvelope, SignedEnvelope, } from "./envelope.js";
|
|
40
41
|
import { signEd25519, verifyEd25519 } from "./crypto.js";
|
|
41
42
|
/**
|
|
@@ -126,10 +127,43 @@ export const OutboundMessagePayload = type({
|
|
|
126
127
|
"summary?": "string",
|
|
127
128
|
"attachments?": OutboundAttachmentPayload.array(),
|
|
128
129
|
"inReplyTo?": "string",
|
|
130
|
+
"references?": "string[]",
|
|
129
131
|
"correlationId?": "string",
|
|
130
132
|
"sessionId?": "string",
|
|
131
133
|
"tenantId?": "string",
|
|
132
134
|
});
|
|
135
|
+
/**
|
|
136
|
+
* Wire shape of the parsed `MessageHeaders` the supervisor rides inline on a
|
|
137
|
+
* `mailbox.notify` frame. Mirrors `@intx/types/runtime`'s `MessageHeaders`
|
|
138
|
+
* field-for-field so a child watcher gets the arrived message's envelope for
|
|
139
|
+
* its `exists` `MailboxEvent` without a substrate round-trip. The four
|
|
140
|
+
* unconditionally-dereferenced fields (`from`, `to`, `date`, `messageId`) are
|
|
141
|
+
* required; the rest are optional, matching the runtime type.
|
|
142
|
+
*
|
|
143
|
+
* Duplicated here as an arktype validator (rather than importing the TypeScript
|
|
144
|
+
* `MessageHeaders` type) so the IPC module validates the header block at the
|
|
145
|
+
* wire boundary, exactly as `OutboundMessagePayload` does for outbound mail.
|
|
146
|
+
*/
|
|
147
|
+
export const MailboxNotifyHeaders = type({
|
|
148
|
+
from: "string",
|
|
149
|
+
to: "string[]",
|
|
150
|
+
"cc?": "string[]",
|
|
151
|
+
date: "string",
|
|
152
|
+
messageId: "string",
|
|
153
|
+
"inReplyTo?": "string",
|
|
154
|
+
"references?": "string[]",
|
|
155
|
+
"subject?": "string",
|
|
156
|
+
"listId?": "string",
|
|
157
|
+
"interchangeType?": InterchangeType,
|
|
158
|
+
"interchangeCorrelationId?": "string",
|
|
159
|
+
"interchangeTenantId?": "string",
|
|
160
|
+
"interchangeAgentId?": "string",
|
|
161
|
+
"interchangeSessionId?": "string",
|
|
162
|
+
"interchangeOfferingId?": "string",
|
|
163
|
+
"interchangeSchemaVersion?": "string",
|
|
164
|
+
"traceparent?": "string",
|
|
165
|
+
"tracestate?": "string",
|
|
166
|
+
});
|
|
133
167
|
/**
|
|
134
168
|
* Discriminated union of every control-channel payload kind. The
|
|
135
169
|
* `type` discriminator namespaces the control-plane vocabulary so a
|
|
@@ -137,35 +171,47 @@ export const OutboundMessagePayload = type({
|
|
|
137
171
|
* union and not by widening the envelope shape. Inference events
|
|
138
172
|
* NEVER appear here; they ride the event channel.
|
|
139
173
|
*/
|
|
140
|
-
export const ControlPayload = type({
|
|
174
|
+
export const ControlPayload = type.or({
|
|
141
175
|
type: "'trigger.fire'",
|
|
142
176
|
data: {
|
|
143
177
|
runId: "string",
|
|
144
178
|
messageId: "string",
|
|
145
179
|
receivedAt: "number",
|
|
180
|
+
// The run's inbound-mail input, resolved by the supervisor (the sole
|
|
181
|
+
// mail owner) before the frame: a decoded `Mail` (headers plus part
|
|
182
|
+
// descriptors that reference the part bytes committed to the workflow-run
|
|
183
|
+
// substrate). The child hands this straight to the runtime as the trigger
|
|
184
|
+
// payload. Refs, not raw mail bytes, ride here; the committed part files
|
|
185
|
+
// stay in the substrate. Typed `unknown` -- `Mail` is a nested structural
|
|
186
|
+
// type validated at the consumption boundary by `isMail`.
|
|
187
|
+
payload: "unknown",
|
|
146
188
|
},
|
|
147
|
-
},
|
|
189
|
+
}, {
|
|
148
190
|
type: "'signal.deliver'",
|
|
149
191
|
data: {
|
|
150
192
|
runId: "string",
|
|
151
193
|
signalName: "string",
|
|
152
194
|
signalId: "string",
|
|
195
|
+
// The resume decision in FINAL form -- the child commits it as the
|
|
196
|
+
// SignalReceived payload verbatim. Each sender owns any
|
|
197
|
+
// provenance-specific preparation BEFORE this frame: the dispatch loop
|
|
198
|
+
// resolves an inbound mail to a decoded `Mail` (headers plus part
|
|
199
|
+
// references, like the turn-1 trigger), while `deliverSignal` ships a
|
|
200
|
+
// structured signal payload unchanged -- so this field stays
|
|
201
|
+
// polymorphic. Do NOT ship raw inbound mail bytes through here.
|
|
153
202
|
payload: "unknown",
|
|
154
203
|
},
|
|
155
|
-
}
|
|
156
|
-
.or({
|
|
204
|
+
}, {
|
|
157
205
|
type: "'drain'",
|
|
158
206
|
data: {
|
|
159
207
|
deadlineMs: "number",
|
|
160
208
|
},
|
|
161
|
-
}
|
|
162
|
-
.or({
|
|
209
|
+
}, {
|
|
163
210
|
type: "'shutdown'",
|
|
164
211
|
data: {
|
|
165
212
|
reason: "string",
|
|
166
213
|
},
|
|
167
|
-
}
|
|
168
|
-
.or({
|
|
214
|
+
}, {
|
|
169
215
|
type: "'grants-updated'",
|
|
170
216
|
data: {
|
|
171
217
|
/**
|
|
@@ -187,12 +233,28 @@ export const ControlPayload = type({
|
|
|
187
233
|
*/
|
|
188
234
|
"stepHashes?": "Record<string, string>",
|
|
189
235
|
},
|
|
190
|
-
}
|
|
191
|
-
.or({
|
|
236
|
+
}, {
|
|
192
237
|
type: "'sources-updated'",
|
|
193
238
|
data: SourcesUpdatedData,
|
|
194
|
-
}
|
|
195
|
-
|
|
239
|
+
}, {
|
|
240
|
+
// Refreshed credential material for the deployment's inference sources and
|
|
241
|
+
// tools. The child MERGES this into its in-memory cell (see
|
|
242
|
+
// `mergeCredentialDelivery`): `delivery.materials` upsert by credentialId
|
|
243
|
+
// and `delivery.bindings` upsert by (consumer, handle); `revoke` names
|
|
244
|
+
// credentialIds to drop, and dropping one drops every binding referencing
|
|
245
|
+
// it. Merge rather than wholesale-replace because the cell has several
|
|
246
|
+
// independently-scoped producers (the deploy frame, an inference rotation,
|
|
247
|
+
// a tool-grant push), each carrying only its own slice -- a swap would let
|
|
248
|
+
// one evict another's credentials. Revocation is therefore explicit, never
|
|
249
|
+
// by omission. Carried inline like the grants and sources snapshots. The
|
|
250
|
+
// secret rides this frame and the in-memory cell only; it is never
|
|
251
|
+
// persisted.
|
|
252
|
+
type: "'credentials-updated'",
|
|
253
|
+
data: {
|
|
254
|
+
delivery: CredentialDelivery,
|
|
255
|
+
"revoke?": "string[]",
|
|
256
|
+
},
|
|
257
|
+
}, {
|
|
196
258
|
type: "'ready'",
|
|
197
259
|
data: {
|
|
198
260
|
childPid: "number",
|
|
@@ -204,8 +266,7 @@ export const ControlPayload = type({
|
|
|
204
266
|
*/
|
|
205
267
|
childPublicKey: "string",
|
|
206
268
|
},
|
|
207
|
-
}
|
|
208
|
-
.or({
|
|
269
|
+
}, {
|
|
209
270
|
// Child-initiated request to recycle the workflow-process. The
|
|
210
271
|
// child emits this when its own self-check decides it needs to be
|
|
211
272
|
// recycled (an internal consistency error it can't recover from,
|
|
@@ -219,8 +280,7 @@ export const ControlPayload = type({
|
|
|
219
280
|
data: {
|
|
220
281
|
reason: "string",
|
|
221
282
|
},
|
|
222
|
-
}
|
|
223
|
-
.or({
|
|
283
|
+
}, {
|
|
224
284
|
// Child-initiated `writeTreePreservingPrefix` request. The child
|
|
225
285
|
// does not hold a substrate write authority for the workflow-run
|
|
226
286
|
// repo (single-writer at the ref tip belongs to the supervisor);
|
|
@@ -243,8 +303,7 @@ export const ControlPayload = type({
|
|
|
243
303
|
preservePrefix: "string > 0",
|
|
244
304
|
message: "string > 0",
|
|
245
305
|
},
|
|
246
|
-
}
|
|
247
|
-
.or({
|
|
306
|
+
}, {
|
|
248
307
|
// Supervisor-initiated request for the child's merge bytes. Fired
|
|
249
308
|
// from inside the supervisor's `writeTreePreservingPrefix` merge
|
|
250
309
|
// callback while the per-repo lock is held; the child receives the
|
|
@@ -261,8 +320,7 @@ export const ControlPayload = type({
|
|
|
261
320
|
contentBase64: "string",
|
|
262
321
|
}).array(),
|
|
263
322
|
},
|
|
264
|
-
}
|
|
265
|
-
.or({
|
|
323
|
+
}, {
|
|
266
324
|
// Child's merge result. `requestId` correlates with the
|
|
267
325
|
// `substrate.write.request` that started the write; the supervisor
|
|
268
326
|
// resumes its merge callback with the supplied entries (or
|
|
@@ -281,8 +339,7 @@ export const ControlPayload = type({
|
|
|
281
339
|
reason: "string > 0",
|
|
282
340
|
}),
|
|
283
341
|
},
|
|
284
|
-
}
|
|
285
|
-
.or({
|
|
342
|
+
}, {
|
|
286
343
|
// Supervisor's terminal reply to a child's `substrate.write.request`.
|
|
287
344
|
// The `requestId` echoes the child's allocated correlation id so
|
|
288
345
|
// the child's pending-id map resolves the awaiter. A successful
|
|
@@ -302,8 +359,7 @@ export const ControlPayload = type({
|
|
|
302
359
|
reason: "string > 0",
|
|
303
360
|
}),
|
|
304
361
|
},
|
|
305
|
-
}
|
|
306
|
-
.or({
|
|
362
|
+
}, {
|
|
307
363
|
// Child-initiated outbound-mail request (OUTBOUND half of mailbox
|
|
308
364
|
// ownership, §3a). The workflow-process child never holds the
|
|
309
365
|
// agent's signing key and never calls `transport.send` itself. When
|
|
@@ -329,8 +385,7 @@ export const ControlPayload = type({
|
|
|
329
385
|
"mailbox?": "string",
|
|
330
386
|
message: OutboundMessagePayload,
|
|
331
387
|
},
|
|
332
|
-
}
|
|
333
|
-
.or({
|
|
388
|
+
}, {
|
|
334
389
|
// Supervisor's terminal reply to a child's `outbound.message`. The
|
|
335
390
|
// `requestId` echoes the child's correlation id so the child's
|
|
336
391
|
// pending mail-tool awaiter resolves. A successful send surfaces the
|
|
@@ -350,8 +405,7 @@ export const ControlPayload = type({
|
|
|
350
405
|
reason: "string > 0",
|
|
351
406
|
}),
|
|
352
407
|
},
|
|
353
|
-
}
|
|
354
|
-
.or({
|
|
408
|
+
}, {
|
|
355
409
|
// Child-initiated terminal-run notification. The workflow-process
|
|
356
410
|
// child emits this when one of its runs reaches a terminal phase
|
|
357
411
|
// (`RunCompleted`, `RunFailed`, `RunCancelled`) so the supervisor's
|
|
@@ -377,6 +431,145 @@ export const ControlPayload = type({
|
|
|
377
431
|
message: "string",
|
|
378
432
|
},
|
|
379
433
|
},
|
|
434
|
+
}, {
|
|
435
|
+
// Child-initiated control-plane suspension notification. The
|
|
436
|
+
// workflow-process child emits this when a workflow agent step parks
|
|
437
|
+
// on a reserved `signalName(correlationId)` channel (`env.onPark`),
|
|
438
|
+
// so the supervisor can register the correlation out-of-band before
|
|
439
|
+
// the parked run can be resumed. The supervisor stamps the
|
|
440
|
+
// deployment identity it owns (`runId` + `agentAddress`) and
|
|
441
|
+
// forwards a `signal.correlation.register` frame to the hub, which
|
|
442
|
+
// co-writes the run's routing + approval rows. Mirrors
|
|
443
|
+
// `terminal.event`: a peer-channel notification the supervisor fans
|
|
444
|
+
// out, distinct from the substrate commit the run also produces.
|
|
445
|
+
//
|
|
446
|
+
// `signalName` is deliberately NOT carried: it is a pure function of
|
|
447
|
+
// `correlationId` (`signalName(correlationId)`), recomputed by every
|
|
448
|
+
// consumer that needs it, so the two cannot drift.
|
|
449
|
+
type: "'park.notify'",
|
|
450
|
+
data: {
|
|
451
|
+
runId: "string > 0",
|
|
452
|
+
correlationId: "string > 0",
|
|
453
|
+
parkKind: ControlParkKind,
|
|
454
|
+
// Approver-facing snapshot of the parked tool call, size-capped at this
|
|
455
|
+
// process boundary. Optional: only an ask-rail suspension carries one.
|
|
456
|
+
"snapshot?": BoundedApprovalSnapshot,
|
|
457
|
+
},
|
|
458
|
+
}, {
|
|
459
|
+
// Supervisor-initiated request: enumerate the child's currently-parked
|
|
460
|
+
// approval correlations. The supervisor fires this after a
|
|
461
|
+
// re-establishment (child respawn, or hub-link reconnect fanned out to
|
|
462
|
+
// this deployment) so it can re-register at the hub every correlation
|
|
463
|
+
// whose original `park.notify`-driven register may have been lost while
|
|
464
|
+
// the hub was down. Modeled on `substrate.merge.request`: a
|
|
465
|
+
// supervisor->child request the child answers on `parked-correlations.
|
|
466
|
+
// response`, correlated by `requestId`. Carries no filter -- one child
|
|
467
|
+
// owns one deployment, so the child enumerates everything parked and the
|
|
468
|
+
// supervisor re-emits all; the hub co-write is idempotent.
|
|
469
|
+
type: "'parked-correlations.request'",
|
|
470
|
+
data: {
|
|
471
|
+
requestId: "string > 0",
|
|
472
|
+
},
|
|
473
|
+
}, {
|
|
474
|
+
// Child's reply to `parked-correlations.request`. Each entry mirrors
|
|
475
|
+
// `park.notify`'s data -- the child-supplied half of a
|
|
476
|
+
// `SuspensionRegistration` the supervisor stamps its deployment identity
|
|
477
|
+
// onto -- so the supervisor's re-emit path shares one transform with the
|
|
478
|
+
// `park.notify` arm. Only reduced-state approval parks appear here: the
|
|
479
|
+
// child enumerates steps whose reduced phase is `awaiting-signal` on a
|
|
480
|
+
// control-plane `signalName(correlationId)` channel, each of which carries
|
|
481
|
+
// a durable snapshot by construction (a snapshot-less correlated suspend
|
|
482
|
+
// reduces to `failed`, not `awaiting-signal`). `snapshot` is therefore
|
|
483
|
+
// required, and size-capped at this process boundary like `park.notify`.
|
|
484
|
+
type: "'parked-correlations.response'",
|
|
485
|
+
data: {
|
|
486
|
+
requestId: "string > 0",
|
|
487
|
+
parked: type({
|
|
488
|
+
runId: "string > 0",
|
|
489
|
+
correlationId: "string > 0",
|
|
490
|
+
parkKind: ControlParkKind,
|
|
491
|
+
// Snapshot is required for approval parks and absent for input parks.
|
|
492
|
+
"snapshot?": BoundedApprovalSnapshot,
|
|
493
|
+
}).array(),
|
|
494
|
+
},
|
|
495
|
+
}, {
|
|
496
|
+
// Child reports self-discovered runs after reconnect or recycle.
|
|
497
|
+
// The supervisor seeds its cohort tracking from these runIds so
|
|
498
|
+
// drain accumulators and dispatch routing account for runs the
|
|
499
|
+
// supervisor did not personally trigger.fire.
|
|
500
|
+
type: "'resumed.runs'",
|
|
501
|
+
data: {
|
|
502
|
+
runIds: type("string > 0").array(),
|
|
503
|
+
},
|
|
504
|
+
}, {
|
|
505
|
+
// Supervisor-to-child one-way notification that new mail landed in a
|
|
506
|
+
// deployment mailbox (INBOUND half of mailbox ownership, §3b). One-way
|
|
507
|
+
// like `grants-updated`/`sources-updated`: no correlation id, no response.
|
|
508
|
+
// The supervisor -- the sole mail owner -- commits the arrived message to
|
|
509
|
+
// the workflow-run substrate mailbox, then fires this frame so the child's
|
|
510
|
+
// warm-agent `watch`/`mail_wait` observes the arrival decoupled from the
|
|
511
|
+
// FIFO trigger dispatch that resolves a run's first input. `headers` rides
|
|
512
|
+
// inline so a watcher gets the `exists` `MailboxEvent`'s envelope without a
|
|
513
|
+
// substrate round-trip. The child reads the latest committed mailbox state
|
|
514
|
+
// regardless, so the frame carries no commit pin.
|
|
515
|
+
type: "'mailbox.notify'",
|
|
516
|
+
data: {
|
|
517
|
+
runId: "string > 0",
|
|
518
|
+
mailbox: "string > 0",
|
|
519
|
+
uid: "number >= 1",
|
|
520
|
+
headers: MailboxNotifyHeaders,
|
|
521
|
+
},
|
|
522
|
+
}, {
|
|
523
|
+
// Child-initiated mailbox-mutation request (INBOUND half of mailbox
|
|
524
|
+
// ownership, §3b). The supervisor is the sole writer to the
|
|
525
|
+
// workflow-run mailbox: a step agent reads its INBOX locally but
|
|
526
|
+
// every mutation -- flag writes and `expunge` -- routes up here so
|
|
527
|
+
// the supervisor applies it to its owned store. A child flushing the
|
|
528
|
+
// same ref would race the supervisor's in-memory mirror and break
|
|
529
|
+
// uid / modseq monotonicity.
|
|
530
|
+
//
|
|
531
|
+
// `data` is discriminated on `op`: an `addFlags` / `removeFlags`
|
|
532
|
+
// carries the target `uid` and the `flags` to change, so the wire
|
|
533
|
+
// boundary rejects a flag frame that omits them; an `expunge` sweeps
|
|
534
|
+
// every `\Deleted` message in the mailbox and the child constructs it
|
|
535
|
+
// with neither. `requestId` correlates the supervisor's
|
|
536
|
+
// `mailbox.mutate.response` reply.
|
|
537
|
+
type: "'mailbox.mutate.request'",
|
|
538
|
+
data: type({
|
|
539
|
+
requestId: "string > 0",
|
|
540
|
+
runId: "string > 0",
|
|
541
|
+
mailbox: "string > 0",
|
|
542
|
+
op: "'addFlags' | 'removeFlags'",
|
|
543
|
+
uid: "number >= 1",
|
|
544
|
+
flags: "string[]",
|
|
545
|
+
}, "|", {
|
|
546
|
+
requestId: "string > 0",
|
|
547
|
+
runId: "string > 0",
|
|
548
|
+
mailbox: "string > 0",
|
|
549
|
+
op: "'expunge'",
|
|
550
|
+
}),
|
|
551
|
+
}, {
|
|
552
|
+
// Supervisor's terminal reply to a child's `mailbox.mutate.request`.
|
|
553
|
+
// The `requestId` echoes the child's correlation id so the child's
|
|
554
|
+
// pending mail-tool awaiter resolves. The reply is sent only after
|
|
555
|
+
// the supervisor flushes the mutation, so the child's next committed
|
|
556
|
+
// read observes it -- the same flush-before-signal ordering
|
|
557
|
+
// `mailbox.notify` relies on. A successful `expunge` carries the
|
|
558
|
+
// `expungedUids` it swept so the agent tool can report the count; a
|
|
559
|
+
// flag write carries no operand echo. A failed mutation (unknown
|
|
560
|
+
// uid, substrate fault) surfaces a structured `{ ok: false, reason }`
|
|
561
|
+
// the child's bridge rethrows so the mail-tool call fails loudly.
|
|
562
|
+
type: "'mailbox.mutate.response'",
|
|
563
|
+
data: {
|
|
564
|
+
requestId: "string > 0",
|
|
565
|
+
result: type({
|
|
566
|
+
ok: "true",
|
|
567
|
+
"expungedUids?": "number[]",
|
|
568
|
+
}, "|", {
|
|
569
|
+
ok: "false",
|
|
570
|
+
reason: "string > 0",
|
|
571
|
+
}),
|
|
572
|
+
},
|
|
380
573
|
});
|
|
381
574
|
/**
|
|
382
575
|
* Construct the supervisor-side control-channel sender. The
|
|
@@ -29,7 +29,7 @@ export declare const EventPayload: import("arktype/internal/variants/object.ts")
|
|
|
29
29
|
index?: number;
|
|
30
30
|
};
|
|
31
31
|
} | {
|
|
32
|
-
type: "inference.
|
|
32
|
+
type: "inference.block.signature";
|
|
33
33
|
seq: number;
|
|
34
34
|
data: {
|
|
35
35
|
signature: string;
|
|
@@ -155,6 +155,7 @@ export declare const EventPayload: import("arktype/internal/variants/object.ts")
|
|
|
155
155
|
content: ({
|
|
156
156
|
type: "text";
|
|
157
157
|
text: string;
|
|
158
|
+
signature?: string;
|
|
158
159
|
} | {
|
|
159
160
|
type: "image";
|
|
160
161
|
source: {
|
|
@@ -170,6 +171,7 @@ export declare const EventPayload: import("arktype/internal/variants/object.ts")
|
|
|
170
171
|
mimeType: string;
|
|
171
172
|
url: string;
|
|
172
173
|
};
|
|
174
|
+
signature?: string;
|
|
173
175
|
} | {
|
|
174
176
|
type: "audio";
|
|
175
177
|
source: {
|
|
@@ -215,6 +217,8 @@ export declare const EventPayload: import("arktype/internal/variants/object.ts")
|
|
|
215
217
|
mimeType: string;
|
|
216
218
|
url: string;
|
|
217
219
|
};
|
|
220
|
+
title?: string;
|
|
221
|
+
context?: string;
|
|
218
222
|
} | {
|
|
219
223
|
type: "thinking";
|
|
220
224
|
thinking: string;
|
|
@@ -230,6 +234,7 @@ export declare const EventPayload: import("arktype/internal/variants/object.ts")
|
|
|
230
234
|
id: string;
|
|
231
235
|
name: string;
|
|
232
236
|
arguments: Record<string, unknown>;
|
|
237
|
+
signature?: string;
|
|
233
238
|
} | {
|
|
234
239
|
type: "citation";
|
|
235
240
|
citedText: string;
|
|
@@ -249,11 +254,15 @@ export declare const EventPayload: import("arktype/internal/variants/object.ts")
|
|
|
249
254
|
start: number;
|
|
250
255
|
end: number;
|
|
251
256
|
};
|
|
257
|
+
} | {
|
|
258
|
+
type: "safety_rating";
|
|
259
|
+
blockReason: string;
|
|
252
260
|
} | {
|
|
253
261
|
type: "code_execution_request";
|
|
254
262
|
id: string;
|
|
255
263
|
code: string;
|
|
256
264
|
language?: string;
|
|
265
|
+
signature?: string;
|
|
257
266
|
} | {
|
|
258
267
|
type: "code_execution_result";
|
|
259
268
|
requestId: string;
|
|
@@ -269,6 +278,7 @@ export declare const EventPayload: import("arktype/internal/variants/object.ts")
|
|
|
269
278
|
content: ({
|
|
270
279
|
type: "text";
|
|
271
280
|
text: string;
|
|
281
|
+
signature?: string;
|
|
272
282
|
} | {
|
|
273
283
|
type: "image";
|
|
274
284
|
source: {
|
|
@@ -284,6 +294,7 @@ export declare const EventPayload: import("arktype/internal/variants/object.ts")
|
|
|
284
294
|
mimeType: string;
|
|
285
295
|
url: string;
|
|
286
296
|
};
|
|
297
|
+
signature?: string;
|
|
287
298
|
} | {
|
|
288
299
|
type: "audio";
|
|
289
300
|
source: {
|
|
@@ -329,6 +340,8 @@ export declare const EventPayload: import("arktype/internal/variants/object.ts")
|
|
|
329
340
|
mimeType: string;
|
|
330
341
|
url: string;
|
|
331
342
|
};
|
|
343
|
+
title?: string;
|
|
344
|
+
context?: string;
|
|
332
345
|
})[];
|
|
333
346
|
detail?: unknown;
|
|
334
347
|
isError?: boolean;
|
|
@@ -411,6 +424,15 @@ export declare const EventPayload: import("arktype/internal/variants/object.ts")
|
|
|
411
424
|
};
|
|
412
425
|
index?: number;
|
|
413
426
|
};
|
|
427
|
+
} | {
|
|
428
|
+
type: "inference.safety_rating";
|
|
429
|
+
seq: number;
|
|
430
|
+
data: {
|
|
431
|
+
safetyRating: {
|
|
432
|
+
type: "safety_rating";
|
|
433
|
+
blockReason: string;
|
|
434
|
+
};
|
|
435
|
+
};
|
|
414
436
|
} | {
|
|
415
437
|
type: "inference.code_execution.start";
|
|
416
438
|
seq: number;
|
|
@@ -420,6 +442,7 @@ export declare const EventPayload: import("arktype/internal/variants/object.ts")
|
|
|
420
442
|
id: string;
|
|
421
443
|
code: string;
|
|
422
444
|
language?: string;
|
|
445
|
+
signature?: string;
|
|
423
446
|
};
|
|
424
447
|
index?: number;
|
|
425
448
|
};
|
|
@@ -466,6 +489,7 @@ export declare const EventPayload: import("arktype/internal/variants/object.ts")
|
|
|
466
489
|
mimeType: string;
|
|
467
490
|
url: string;
|
|
468
491
|
};
|
|
492
|
+
signature?: string;
|
|
469
493
|
};
|
|
470
494
|
index?: number;
|
|
471
495
|
};
|
|
@@ -574,6 +598,13 @@ export declare const EventPayload: import("arktype/internal/variants/object.ts")
|
|
|
574
598
|
data: {
|
|
575
599
|
reason: "approval" | "payment" | "credential" | "budget" | "child_completion" | "message_response";
|
|
576
600
|
gateId: string;
|
|
601
|
+
correlationId?: string;
|
|
602
|
+
approvalSnapshot?: {
|
|
603
|
+
name: string;
|
|
604
|
+
description: string;
|
|
605
|
+
inputSchema: Record<string, unknown>;
|
|
606
|
+
arguments: Record<string, unknown>;
|
|
607
|
+
};
|
|
577
608
|
};
|
|
578
609
|
} | {
|
|
579
610
|
type: "reactor.gate.cleared";
|
package/dist/ipc/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ControlPayload, OutboundAttachmentPayload, OutboundMessagePayload, SourcesUpdatedData, createControlChannelSender, receiveControlChannel, type ControlChannelSender, type ControlChannelSenderOpts, type ControlChannelReceiverOpts, type NdjsonReader, type NdjsonWriter, } from "./control-channel.js";
|
|
1
|
+
export { ControlPayload, MailboxNotifyHeaders, OutboundAttachmentPayload, OutboundMessagePayload, SourcesUpdatedData, createControlChannelSender, receiveControlChannel, type ControlChannelSender, type ControlChannelSenderOpts, type ControlChannelReceiverOpts, type NdjsonReader, type NdjsonWriter, } from "./control-channel.js";
|
|
2
2
|
export { DEFAULT_EVENT_BUFFER_LIMIT, EventPayload, createEventChannelSender, receiveEventChannel, type EventChannelSender, type EventChannelSenderOpts, type EventChannelReceiverOpts, type FrameReader, type FrameWriter, } from "./event-channel.js";
|
|
3
3
|
export { FrameEnvelope, MacedEnvelope, SignedEnvelope, decodeEnvelope, encodeEnvelope, } from "./envelope.js";
|
|
4
4
|
export { IPC_CRYPTO, generateChannelId, generateHmacKey, signEd25519, signHmac, verifyEd25519, verifyHmac, } from "./crypto.js";
|
package/dist/ipc/index.js
CHANGED
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
// from env, the supervisor enforces.
|
|
138
138
|
//
|
|
139
139
|
// =============================================================
|
|
140
|
-
export { ControlPayload, OutboundAttachmentPayload, OutboundMessagePayload, SourcesUpdatedData, createControlChannelSender, receiveControlChannel, } from "./control-channel.js";
|
|
140
|
+
export { ControlPayload, MailboxNotifyHeaders, OutboundAttachmentPayload, OutboundMessagePayload, SourcesUpdatedData, createControlChannelSender, receiveControlChannel, } from "./control-channel.js";
|
|
141
141
|
export { DEFAULT_EVENT_BUFFER_LIMIT, EventPayload, createEventChannelSender, receiveEventChannel, } from "./event-channel.js";
|
|
142
142
|
export { FrameEnvelope, MacedEnvelope, SignedEnvelope, decodeEnvelope, encodeEnvelope, } from "./envelope.js";
|
|
143
143
|
export { IPC_CRYPTO, generateChannelId, generateHmacKey, signEd25519, signHmac, verifyEd25519, verifyHmac, } from "./crypto.js";
|
|
@@ -10,14 +10,19 @@ import type { MailBusBindings } from "../supervisor/types.js";
|
|
|
10
10
|
*/
|
|
11
11
|
export interface HubTransportMailBusAdapter extends MailBusBindings {
|
|
12
12
|
/**
|
|
13
|
-
* Fan a delivered message out to every handler subscribed at
|
|
14
|
-
*
|
|
15
|
-
* the
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* Fan a delivered message out to every handler subscribed at `address` and
|
|
14
|
+
* return the combined durable settlement: the promise resolves once every
|
|
15
|
+
* subscribed handler has durably accepted the message and rejects if any
|
|
16
|
+
* handler rejected. The host propagates that settlement to the wire, so
|
|
17
|
+
* resolution is the durable-receipt ACK signal and rejection is WITHHOLD.
|
|
18
|
+
*
|
|
19
|
+
* An address with NO active subscriber rejects rather than resolving. The
|
|
20
|
+
* supervisor's lifecycle (`subscribeMailForAddress` returns a disposer it
|
|
21
|
+
* calls on teardown) is the authoritative source of which addresses are
|
|
22
|
+
* live; a message routed to an address with no live handler was not
|
|
23
|
+
* durably accepted, so it must withhold-and-be-redelivered, never ack.
|
|
19
24
|
*/
|
|
20
|
-
routeInbound(address: string, message: Uint8Array): void
|
|
25
|
+
routeInbound(address: string, message: Uint8Array): Promise<void>;
|
|
21
26
|
}
|
|
22
27
|
/**
|
|
23
28
|
* Wrap an existing `HubTransport` instance as the supervisor-facing
|
|
@@ -50,12 +50,16 @@ export function wrapHubTransportAsMailBus(transport) {
|
|
|
50
50
|
current?.delete(handler);
|
|
51
51
|
};
|
|
52
52
|
},
|
|
53
|
-
routeInbound(address, message) {
|
|
53
|
+
async routeInbound(address, message) {
|
|
54
54
|
const set = subscribers.get(address);
|
|
55
|
-
if (set === undefined)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
if (set === undefined || set.size === 0) {
|
|
56
|
+
// No live handler accepted the message, so it was not durably
|
|
57
|
+
// received. Reject so the caller withholds the ack and the hub
|
|
58
|
+
// redelivers, rather than silently dropping (which under the ack
|
|
59
|
+
// model would be an acked loss).
|
|
60
|
+
throw new Error(`no active mail subscriber for ${address}; message not durably accepted`);
|
|
61
|
+
}
|
|
62
|
+
await Promise.all([...set].map((handler) => handler(message)));
|
|
59
63
|
},
|
|
60
64
|
async sendOutbound(senderAddress, message) {
|
|
61
65
|
// OUTBOUND half of mailbox ownership (§3a): route the agent's
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run `body`, then always run `cleanup`, without letting a `cleanup`
|
|
3
|
+
* failure mask a `body` failure.
|
|
4
|
+
*
|
|
5
|
+
* When `body` throws, `cleanup` still runs and a `cleanup` failure is
|
|
6
|
+
* handed to `onCleanupErrorAfterBodyError` (to log) and then dropped, so
|
|
7
|
+
* the original `body` error is what propagates. When `body` succeeds, a
|
|
8
|
+
* `cleanup` failure propagates -- there is no primary error to protect, so
|
|
9
|
+
* a failing teardown is the error worth surfacing.
|
|
10
|
+
*
|
|
11
|
+
* This exists so teardown in a `finally` (agent close, warm-cache
|
|
12
|
+
* eviction) can surface its own failure without a `throw` inside a
|
|
13
|
+
* `finally` block, which `no-unsafe-finally` forbids precisely because it
|
|
14
|
+
* silently swallows the in-flight exception -- the masking bug this guards
|
|
15
|
+
* against.
|
|
16
|
+
*/
|
|
17
|
+
export declare function runBodyThenCleanup<T>(body: () => Promise<T>, cleanup: () => Promise<void>, onCleanupErrorAfterBodyError: (cause: unknown) => void): Promise<T>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run `body`, then always run `cleanup`, without letting a `cleanup`
|
|
3
|
+
* failure mask a `body` failure.
|
|
4
|
+
*
|
|
5
|
+
* When `body` throws, `cleanup` still runs and a `cleanup` failure is
|
|
6
|
+
* handed to `onCleanupErrorAfterBodyError` (to log) and then dropped, so
|
|
7
|
+
* the original `body` error is what propagates. When `body` succeeds, a
|
|
8
|
+
* `cleanup` failure propagates -- there is no primary error to protect, so
|
|
9
|
+
* a failing teardown is the error worth surfacing.
|
|
10
|
+
*
|
|
11
|
+
* This exists so teardown in a `finally` (agent close, warm-cache
|
|
12
|
+
* eviction) can surface its own failure without a `throw` inside a
|
|
13
|
+
* `finally` block, which `no-unsafe-finally` forbids precisely because it
|
|
14
|
+
* silently swallows the in-flight exception -- the masking bug this guards
|
|
15
|
+
* against.
|
|
16
|
+
*/
|
|
17
|
+
export async function runBodyThenCleanup(body, cleanup, onCleanupErrorAfterBodyError) {
|
|
18
|
+
let outcome;
|
|
19
|
+
try {
|
|
20
|
+
outcome = { ok: true, value: await body() };
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
outcome = { ok: false, error };
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
await cleanup();
|
|
27
|
+
}
|
|
28
|
+
catch (cleanupError) {
|
|
29
|
+
if (outcome.ok) {
|
|
30
|
+
throw cleanupError;
|
|
31
|
+
}
|
|
32
|
+
onCleanupErrorAfterBodyError(cleanupError);
|
|
33
|
+
}
|
|
34
|
+
if (!outcome.ok) {
|
|
35
|
+
throw outcome.error;
|
|
36
|
+
}
|
|
37
|
+
return outcome.value;
|
|
38
|
+
}
|
|
@@ -11,12 +11,10 @@ import type { Principal, RepoId, RepoStore } from "@intx/hub-sessions/substrate"
|
|
|
11
11
|
export declare const TimerEventEnvelope: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
12
12
|
seq: number;
|
|
13
13
|
type: "TimerSet" | "TimerFired";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
cron?: string | null;
|
|
19
|
-
};
|
|
14
|
+
timerId: string;
|
|
15
|
+
fireAt?: string;
|
|
16
|
+
stepId?: string | null;
|
|
17
|
+
cron?: string | null;
|
|
20
18
|
}, {}>;
|
|
21
19
|
export type TimerEventEnvelope = typeof TimerEventEnvelope.infer;
|
|
22
20
|
export type SchedulerOpts = {
|
|
@@ -54,6 +52,18 @@ export type SchedulerOpts = {
|
|
|
54
52
|
* callbacks and to skip past-due cron entries on recovery.
|
|
55
53
|
*/
|
|
56
54
|
clock: () => Date;
|
|
55
|
+
/**
|
|
56
|
+
* Arms a one-shot timer and returns its canceller. Defaults to the global
|
|
57
|
+
* timer, which is what production wants.
|
|
58
|
+
*
|
|
59
|
+
* The clock above decides WHEN a timer should fire; this decides what
|
|
60
|
+
* actually fires it. Without both, a caller can compute a deterministic
|
|
61
|
+
* delay and still have to wait out the real interval to observe the
|
|
62
|
+
* firing -- which left this module's own tests waiting past a `fireAt`,
|
|
63
|
+
* and, in one case, waiting longer than a cancelled timer's delay to argue
|
|
64
|
+
* from silence that the cancel had worked.
|
|
65
|
+
*/
|
|
66
|
+
scheduleTimeout?: (handler: () => void, ms: number) => () => void;
|
|
57
67
|
};
|
|
58
68
|
export type SchedulerHandle = {
|
|
59
69
|
/**
|