@intx/workflow-host 0.2.2 → 0.3.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 (53) hide show
  1. package/README.md +56 -10
  2. package/dist/adapters/repo-store.d.ts +22 -1
  3. package/dist/adapters/repo-store.js +53 -53
  4. package/dist/adapters/spawn-child.d.ts +71 -42
  5. package/dist/adapters/spawn-child.js +83 -77
  6. package/dist/adapters/step-invoker.js +84 -7
  7. package/dist/child/env-bootstrap.d.ts +20 -6
  8. package/dist/child/env-bootstrap.js +9 -1
  9. package/dist/child/index.d.ts +2 -1
  10. package/dist/child/parked-correlations.d.ts +42 -0
  11. package/dist/child/parked-correlations.js +80 -0
  12. package/dist/child/proxy-repo-store.d.ts +3 -2
  13. package/dist/child/proxy-repo-store.js +2 -0
  14. package/dist/child/run-child.d.ts +107 -13
  15. package/dist/child/run-child.js +290 -108
  16. package/dist/child/self-discovery.d.ts +10 -0
  17. package/dist/child/self-discovery.js +25 -1
  18. package/dist/child/verified-definition-loader.d.ts +33 -0
  19. package/dist/child/verified-definition-loader.js +43 -0
  20. package/dist/conversation-text.d.ts +23 -0
  21. package/dist/conversation-text.js +56 -0
  22. package/dist/index.d.ts +4 -3
  23. package/dist/index.js +3 -2
  24. package/dist/ipc/control-channel.d.ts +58 -0
  25. package/dist/ipc/control-channel.js +94 -1
  26. package/dist/ipc/event-channel.d.ts +32 -1
  27. package/dist/mail-bus/hub-transport-adapter.d.ts +12 -7
  28. package/dist/mail-bus/hub-transport-adapter.js +9 -5
  29. package/dist/seams/scheduler.d.ts +4 -6
  30. package/dist/seams/scheduler.js +74 -93
  31. package/dist/supervisor/cancel-signing.d.ts +2 -2
  32. package/dist/supervisor/cancel-signing.js +1 -1
  33. package/dist/supervisor/credentials.d.ts +11 -10
  34. package/dist/supervisor/credentials.js +7 -7
  35. package/dist/supervisor/dispatch-attribution.js +1 -1
  36. package/dist/supervisor/drain-timeout.d.ts +2 -2
  37. package/dist/supervisor/drain-timeout.js +1 -1
  38. package/dist/supervisor/index.d.ts +3 -3
  39. package/dist/supervisor/index.js +2 -2
  40. package/dist/supervisor/recycle.d.ts +5 -2
  41. package/dist/supervisor/recycle.js +18 -7
  42. package/dist/supervisor/run-event-compaction.d.ts +5 -5
  43. package/dist/supervisor/run-event-compaction.js +5 -5
  44. package/dist/supervisor/spawn-env.d.ts +2 -2
  45. package/dist/supervisor/spawn-env.js +1 -1
  46. package/dist/supervisor/supervisor.d.ts +82 -25
  47. package/dist/supervisor/supervisor.js +1313 -410
  48. package/dist/supervisor/terminal-commit.d.ts +36 -0
  49. package/dist/supervisor/terminal-commit.js +134 -0
  50. package/dist/supervisor/types.d.ts +150 -23
  51. package/dist/workflow-definition-loader.d.ts +131 -0
  52. package/dist/workflow-definition-loader.js +316 -0
  53. package/package.json +12 -11
@@ -0,0 +1,33 @@
1
+ import type { WorkflowDefinition } from "@intx/workflow";
2
+ export interface LoadVerifiedWorkflowDefinitionFromClosureOpts {
3
+ /**
4
+ * Sidecar-local directory of the materialized workflow-definition closure:
5
+ * the package dir holding `package.json` (with `interchange.workflow`) and
6
+ * its laid-out `node_modules/`. The sidecar computes this when it applies
7
+ * the frozen closure and threads it through the child's spawn env.
8
+ */
9
+ packageDir: string;
10
+ /**
11
+ * Hub-approved wire hash the re-verify must match. Sourced from the hub
12
+ * authority (`SpawnTimeEnv.definitionHash`). The recompute projects the
13
+ * evaluated LIVE definition back to its inert form and hashes that; a value
14
+ * that differs throws.
15
+ */
16
+ approvedHash: string;
17
+ /**
18
+ * Test seam forwarded to `loadWorkflowDefinitionFromClosure` for the
19
+ * workflow entry's dynamic import. Production omits it and the entry is
20
+ * imported natively.
21
+ */
22
+ importModule?: (importUrl: string) => Promise<unknown>;
23
+ }
24
+ /**
25
+ * Evaluate a source-ref deployment's pinned code closure to a live
26
+ * `WorkflowDefinition` and re-verify it by project-then-hash before returning
27
+ * it. The inert projection is a non-executable approval surface, so the runtime
28
+ * needs the live definition the closure evaluates to. The re-verify projects
29
+ * that live definition back to its inert form and hashes it
30
+ * (`computeLiveDefinitionHash`), matching the hub-approved wire hash by byte
31
+ * equality; a divergent closure fails closed here and never runs.
32
+ */
33
+ export declare function loadVerifiedWorkflowDefinitionFromClosure(opts: LoadVerifiedWorkflowDefinitionFromClosureOpts): Promise<WorkflowDefinition>;
@@ -0,0 +1,43 @@
1
+ // Source-ref definition load + re-verify barrier for the workflow-process child.
2
+ //
3
+ // A source-ref deployment's runnable definition is the evaluated pinned code
4
+ // closure, not an on-disk `workflow.json`. `loadVerifiedWorkflowDefinitionFromClosure`
5
+ // evaluates that closure to a live `WorkflowDefinition` and re-verifies it by
6
+ // project-then-hash: it projects the live definition back to its inert form,
7
+ // hashes it (`computeLiveDefinitionHash`), and refuses to return a definition
8
+ // whose recompute does not match the hub-approved hash. A mismatch throws --
9
+ // fail closed, no fallback and no coercion.
10
+ //
11
+ // The re-verify barrier is load-bearing because the approved hash arrives
12
+ // OUT-OF-BAND from the bytes being checked (a signed spawn env the closure
13
+ // materializer cannot forge). It lives at the LOAD boundary, not at run start:
14
+ // a resumed run reuses the definition this loader returned at child boot, so
15
+ // gating the load covers fresh runs, resumed runs, and referenced onTrigger
16
+ // bodies (which the child extracts from the same re-verified closure) with a
17
+ // single check. `RunStarted.definitionHash` is deliberately NOT the barrier: it
18
+ // hashes a different projection and never fires when the run log already carries
19
+ // a `RunStarted`, so it is skipped on resume.
20
+ import { computeLiveDefinitionHash } from "@intx/workflow";
21
+ import { loadWorkflowDefinitionFromClosure } from "../workflow-definition-loader.js";
22
+ /**
23
+ * Evaluate a source-ref deployment's pinned code closure to a live
24
+ * `WorkflowDefinition` and re-verify it by project-then-hash before returning
25
+ * it. The inert projection is a non-executable approval surface, so the runtime
26
+ * needs the live definition the closure evaluates to. The re-verify projects
27
+ * that live definition back to its inert form and hashes it
28
+ * (`computeLiveDefinitionHash`), matching the hub-approved wire hash by byte
29
+ * equality; a divergent closure fails closed here and never runs.
30
+ */
31
+ export async function loadVerifiedWorkflowDefinitionFromClosure(opts) {
32
+ const definition = await loadWorkflowDefinitionFromClosure({
33
+ packageDir: opts.packageDir,
34
+ ...(opts.importModule !== undefined
35
+ ? { importModule: opts.importModule }
36
+ : {}),
37
+ });
38
+ const recomputed = await computeLiveDefinitionHash(definition);
39
+ if (recomputed !== opts.approvedHash) {
40
+ throw new Error(`workflow-host verified-definition loader: recomputed wire hash ${recomputed} for the source-ref closure at ${opts.packageDir} does not match the approved hash ${opts.approvedHash}; refusing to run a definition that no longer projects to the hub-approved content`);
41
+ }
42
+ return definition;
43
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Extract the conversation body text from a raw inbound MIME message.
3
+ *
4
+ * Three on-wire shapes are handled, matching every producer the mail
5
+ * bus accepts:
6
+ * 1. The Interchange assembler's `multipart/signed` envelope whose
7
+ * first part is a `multipart/mixed` body carrying the text at part
8
+ * path `1.1`.
9
+ * 2. A `multipart/signed` envelope wrapping a bare `text/plain` part
10
+ * (a sender that signs without the `multipart/mixed` wrapper); the
11
+ * text is at part path `1`.
12
+ * 3. A flat top-level `text/plain` message (no multipart structure at
13
+ * all); the body is the bytes after the header section.
14
+ *
15
+ * The top-level `Content-Type` selects the shape: only a `multipart/*`
16
+ * root walks into parts; anything else reads the single body directly.
17
+ * This mirrors the conversation branch of mail-memory's `fetchFull`
18
+ * while also tolerating the flat single-part case the in-process agent
19
+ * accepts, so a non-standard inbound mail still delivers its text to
20
+ * the agent rather than crashing the run. `messageId` is used only for
21
+ * error attribution.
22
+ */
23
+ export declare function extractConversationText(raw: Uint8Array, messageId: string): string;
@@ -0,0 +1,56 @@
1
+ // Shared inbound-mail conversation-text extraction. Two call sites resolve a
2
+ // run's mail input to the text the agent actually sees, each at the layer that
3
+ // knows its bytes are inbound mail: the child's `resolveTriggerPayload` (turn
4
+ // 1, the trigger) reads the mail from the substrate, and the supervisor's
5
+ // dispatch loop (turn 2+, a mail delivered to a parked run as a signal) inlines
6
+ // it. Both must produce the SAME shape -- extracted body text, not the raw MIME
7
+ // envelope -- so the extractor lives here rather than in either caller.
8
+ import { extractPartByPath, parseHeaderSection, parseMimePart, } from "@intx/mime";
9
+ /**
10
+ * Extract the conversation body text from a raw inbound MIME message.
11
+ *
12
+ * Three on-wire shapes are handled, matching every producer the mail
13
+ * bus accepts:
14
+ * 1. The Interchange assembler's `multipart/signed` envelope whose
15
+ * first part is a `multipart/mixed` body carrying the text at part
16
+ * path `1.1`.
17
+ * 2. A `multipart/signed` envelope wrapping a bare `text/plain` part
18
+ * (a sender that signs without the `multipart/mixed` wrapper); the
19
+ * text is at part path `1`.
20
+ * 3. A flat top-level `text/plain` message (no multipart structure at
21
+ * all); the body is the bytes after the header section.
22
+ *
23
+ * The top-level `Content-Type` selects the shape: only a `multipart/*`
24
+ * root walks into parts; anything else reads the single body directly.
25
+ * This mirrors the conversation branch of mail-memory's `fetchFull`
26
+ * while also tolerating the flat single-part case the in-process agent
27
+ * accepts, so a non-standard inbound mail still delivers its text to
28
+ * the agent rather than crashing the run. `messageId` is used only for
29
+ * error attribution.
30
+ */
31
+ export function extractConversationText(raw, messageId) {
32
+ const { headers, bodyOffset } = parseHeaderSection(raw);
33
+ const rootMime = (headers.get("content-type") ?? "")
34
+ .split(";")[0]
35
+ ?.trim()
36
+ .toLowerCase();
37
+ if (rootMime === undefined || !rootMime.startsWith("multipart/")) {
38
+ // Flat single-part message: the body is everything after the
39
+ // header section.
40
+ return new TextDecoder("utf-8", { fatal: false }).decode(raw.subarray(bodyOffset));
41
+ }
42
+ let part1;
43
+ try {
44
+ part1 = parseMimePart(extractPartByPath(raw, "1"));
45
+ }
46
+ catch (cause) {
47
+ throw new Error(`conversation-text: cannot parse inbound mail part 1 for messageId ${messageId}`, { cause });
48
+ }
49
+ const part1Mime = (part1.contentType.split(";")[0] ?? "")
50
+ .trim()
51
+ .toLowerCase();
52
+ const bodyBytes = part1Mime.startsWith("multipart/")
53
+ ? parseMimePart(extractPartByPath(raw, "1.1")).body
54
+ : part1.body;
55
+ return new TextDecoder("utf-8", { fatal: false }).decode(bodyBytes);
56
+ }
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
+ export { loadWorkflowDefinitionFromClosure, loadWorkflowDirectorRegistryFromClosure, loadWorkflowPluginFactoriesFromClosure, loadWorkflowPluginToolDefinitionsFromClosure, type LoadWorkflowDefinitionFromClosureArgs, type LoadWorkflowDirectorRegistryFromClosureArgs, type LoadWorkflowPluginsFromClosureArgs, } from "./workflow-definition-loader.js";
1
2
  export { createWorkflowRunRepoStore, type WorkflowRunRepoStoreOpts, } from "./adapters/repo-store.js";
2
3
  export { createWorkflowRunBlobSubstrate, type WorkflowRunBlobSubstrateOpts, } from "./adapters/blob-substrate.js";
3
4
  export { createWorkflowStepInvoker, type StepEnvBase, type WorkflowStepInvokerOpts, } from "./adapters/step-invoker.js";
4
- export { createWorkflowSpawnChild, type ChildTerminalStatus, type RunChildWorkflow, type WorkflowSpawnChildOpts, } from "./adapters/spawn-child.js";
5
- export { createWorkflowSupervisor, assembleCredentialsSnapshot, commitCancelRequested, createDrainTimeoutAccumulator, createRecyclePolicy, defaultStepRepoId, hashGrants, triggerRecycle, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_KILL_TIMEOUT_MS, DEFAULT_POLICY_INTERVAL_MS, DEFAULT_TERMINAL_WRITE_WATCHDOG_MS, MAX_BUFFERED_MAIL, STEP_GRANTS_PATH, STEP_GRANTS_REF, SUPERVISOR_PRINCIPAL_KIND, type AssembleCredentialsSnapshotOpts, type CancelCommitInfo, type CancelRequestOpts, type CommitCancelRequestedOpts, type CommitCancelRequestedResult, type CredentialsSnapshot, type CredentialsSnapshotStep, type DeliverSignalOpts, type DeliverSourcesOpts, type DeriveMailAuditRef, type DeriveStepAddress, type DeriveStepRepoId, type DrainOpts, type DrainTimeoutAccumulator, type DrainTimeoutAccumulatorFactory, type DrainTimeoutOpts, type InboxPrimitives, type MailAuditRef, type MailBusBindings, type PrincipalSigner, type RecycleAttempt, type RecycleContext, type RecycleOpts, type RecycleOrigin, type RecyclePolicy, type RecyclePolicyBounds, type RecyclePolicyOpts, type SignedPayload, type SpawnOpts, type SpawnResult, type SubprocessHandle, type SubprocessSpawner, type TerminalEventSource, type TerminalRunEvent, type TriggerRecycleOpts, type DispatchTimingMark, type DispatchStructuralCounters, type DispatchSubstrateLeg, type WorkflowSupervisor, type WorkflowSupervisorBindings, type WorkflowSupervisorPrincipalKind, } from "./supervisor/index.js";
5
+ export { createInMemorySpawnChild, createInMemorySpawnSuspendableChild, type ChildTerminalStatus, type RunChildWorkflow, type RunSuspendableChild, } from "./adapters/spawn-child.js";
6
+ export { createWorkflowSupervisor, assembleCredentialsSnapshot, isErrnoNotFound, commitCancelRequested, createDrainTimeoutAccumulator, createRecyclePolicy, defaultStepRepoId, hashGrants, triggerRecycle, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_KILL_TIMEOUT_MS, DEFAULT_POLICY_INTERVAL_MS, MAX_BUFFERED_MAIL, STEP_GRANTS_PATH, STEP_GRANTS_REF, SUPERVISOR_PRINCIPAL_KIND, type AssembleCredentialsSnapshotOpts, type CancelCommitInfo, type CancelRequestOpts, type CommitCancelRequestedOpts, type CommitCancelRequestedResult, type CredentialsSnapshot, type CredentialsSnapshotStep, type DeliverSignalOpts, type DeliverSourcesOpts, type DeriveMailAuditRef, type DeriveStepAddress, type DeriveStepRepoId, type DrainOpts, type DrainTimeoutAccumulator, type DrainTimeoutAccumulatorFactory, type DrainTimeoutOpts, type InboxPrimitives, type MailAuditRef, type MailBusBindings, type PrincipalSigner, type RecycleAttempt, type RecycleContext, type RecycleOpts, type RecycleOrigin, type RecyclePolicy, type RecyclePolicyBounds, type RecyclePolicyOpts, type SignedPayload, type SpawnOpts, type SpawnResult, type SubprocessHandle, type SubprocessSpawner, type SuspensionRegistration, type TerminalEventSource, type TerminalRunEvent, type TriggerRecycleOpts, type DispatchTimingMark, type DispatchStructuralCounters, type DispatchSubstrateLeg, type WorkflowSupervisor, type WorkflowSupervisorBindings, type WorkflowSupervisorPrincipalKind, } from "./supervisor/index.js";
6
7
  export { createWorkflowHostDrainController, type WorkflowHostDrainController, type CreateWorkflowHostDrainControllerOpts, } from "./drain-controller.js";
7
8
  export { wrapHubTransportAsMailBus, type HubTransportMailBusAdapter, } from "./mail-bus/index.js";
8
9
  export { ControlPayload, DEFAULT_EVENT_BUFFER_LIMIT, EventPayload, FrameEnvelope, IPC_CRYPTO, SourcesUpdatedData, MacedEnvelope, OutboundAttachmentPayload, OutboundMessagePayload, SignedEnvelope, createControlChannelSender, createEventChannelSender, decodeEnvelope, encodeEnvelope, generateChannelId, generateHmacKey, receiveControlChannel, receiveEventChannel, signEd25519, signHmac, verifyEd25519, verifyHmac, type ControlChannelReceiverOpts, type ControlChannelSender, type ControlChannelSenderOpts, type EventChannelReceiverOpts, type EventChannelSender, type EventChannelSenderOpts, type FrameReader, type FrameWriter, type NdjsonReader, type NdjsonWriter, } from "./ipc/index.js";
9
- export { EVENT_CHANNEL_FD, createChildOutboundMailBridge, createChildSubstrateWriteBridge, createCredentialsBackedAuthorize, createProxyWorkflowRunRepoStore, createSupervisorBackedTransport, createWarmAgentCache, discoverInFlightRuns, parseSpawnTimeEnv, runWorkflowChild, runWorkflowChildFromProcessEnv, type ChildOutboundMailBridge, type ChildStepInvoker, type ChildSubstrateWriteBridge, type CreateChildOutboundMailBridgeOpts, type CreateChildSubstrateWriteBridgeOpts, type CreateProxyWorkflowRunRepoStoreOpts, type CredentialsSnapshotRef, type DiscoverRunsOpts, type DiscoveredRun, type DrainController, type GrantEvaluator, type RunWorkflowChildBindings, type RunWorkflowChildFromProcessEnvOpts, type RunWorkflowChildOpts, type RunWorkflowChildResult, type SourcesSnapshotRef, type SpawnTimeEnv, type SubstrateFactory, type SubstrateFactoryEnv, type SubstrateWriteRequest, type SubstrateWriteResponseSink, type WarmAgentCache, type WarmEventSinkRef, } from "./child/index.js";
10
+ export { EVENT_CHANNEL_FD, createChildOutboundMailBridge, createChildSubstrateWriteBridge, createCredentialsBackedAuthorize, createProxyWorkflowRunRepoStore, createSupervisorBackedTransport, createWarmAgentCache, discoverInFlightRuns, parseSpawnTimeEnv, runWorkflowChild, runWorkflowChildFromProcessEnv, type ChildOutboundMailBridge, type ChildStepInvoker, type ChildSubstrateWriteBridge, type CreateChildOutboundMailBridgeOpts, type CreateChildSubstrateWriteBridgeOpts, type CreateProxyWorkflowRunRepoStoreOpts, type CredentialsSnapshotRef, type CredentialWiring, type DiscoverRunsOpts, type DiscoveredRun, type DrainController, type GrantEvaluator, type LoadParkedApproval, type RunWorkflowChildBindings, type RunWorkflowChildFromProcessEnvOpts, type RunWorkflowChildOpts, type RunWorkflowChildResult, type SourcesSnapshotRef, type SpawnTimeEnv, type SubstrateFactory, type SubstrateFactoryEnv, type SubstrateWriteRequest, type SubstrateWriteResponseSink, type WarmAgentCache, type WarmEventSinkRef, } from "./child/index.js";
10
11
  export { adaptHostScheduler, createWorkflowHostScheduler, createWorkflowHostSignalChannel, SignalReceivedEnvelope, type SchedulerHandle, type SchedulerOpts, type SignalChannelHandle, type SignalChannelOpts, } from "./seams/index.js";
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
1
+ export { loadWorkflowDefinitionFromClosure, loadWorkflowDirectorRegistryFromClosure, loadWorkflowPluginFactoriesFromClosure, loadWorkflowPluginToolDefinitionsFromClosure, } from "./workflow-definition-loader.js";
1
2
  export { createWorkflowRunRepoStore, } from "./adapters/repo-store.js";
2
3
  export { createWorkflowRunBlobSubstrate, } from "./adapters/blob-substrate.js";
3
4
  export { createWorkflowStepInvoker, } from "./adapters/step-invoker.js";
4
- export { createWorkflowSpawnChild, } from "./adapters/spawn-child.js";
5
- export { createWorkflowSupervisor, assembleCredentialsSnapshot, commitCancelRequested, createDrainTimeoutAccumulator, createRecyclePolicy, defaultStepRepoId, hashGrants, triggerRecycle, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_KILL_TIMEOUT_MS, DEFAULT_POLICY_INTERVAL_MS, DEFAULT_TERMINAL_WRITE_WATCHDOG_MS, MAX_BUFFERED_MAIL, STEP_GRANTS_PATH, STEP_GRANTS_REF, SUPERVISOR_PRINCIPAL_KIND, } from "./supervisor/index.js";
5
+ export { createInMemorySpawnChild, createInMemorySpawnSuspendableChild, } from "./adapters/spawn-child.js";
6
+ export { createWorkflowSupervisor, assembleCredentialsSnapshot, isErrnoNotFound, commitCancelRequested, createDrainTimeoutAccumulator, createRecyclePolicy, defaultStepRepoId, hashGrants, triggerRecycle, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_KILL_TIMEOUT_MS, DEFAULT_POLICY_INTERVAL_MS, MAX_BUFFERED_MAIL, STEP_GRANTS_PATH, STEP_GRANTS_REF, SUPERVISOR_PRINCIPAL_KIND, } from "./supervisor/index.js";
6
7
  export { createWorkflowHostDrainController, } from "./drain-controller.js";
7
8
  export { wrapHubTransportAsMailBus, } from "./mail-bus/index.js";
8
9
  export { ControlPayload, DEFAULT_EVENT_BUFFER_LIMIT, EventPayload, FrameEnvelope, IPC_CRYPTO, SourcesUpdatedData, MacedEnvelope, OutboundAttachmentPayload, OutboundMessagePayload, SignedEnvelope, createControlChannelSender, createEventChannelSender, decodeEnvelope, encodeEnvelope, generateChannelId, generateHmacKey, receiveControlChannel, receiveEventChannel, signEd25519, signHmac, verifyEd25519, verifyHmac, } from "./ipc/index.js";
@@ -51,6 +51,7 @@ export declare const SourcesUpdatedData: import("arktype/internal/variants/objec
51
51
  providerOptions?: Record<string, unknown>;
52
52
  };
53
53
  capabilities?: string[];
54
+ quirks?: Record<string, unknown>;
54
55
  }[];
55
56
  defaultSource: string;
56
57
  }, {}>;
@@ -158,9 +159,27 @@ export declare const ControlPayload: import("arktype/internal/variants/object.ts
158
159
  providerOptions?: Record<string, unknown>;
159
160
  };
160
161
  capabilities?: string[];
162
+ quirks?: Record<string, unknown>;
161
163
  }[];
162
164
  defaultSource: string;
163
165
  };
166
+ } | {
167
+ type: "credentials-updated";
168
+ data: {
169
+ delivery: {
170
+ bindings: {
171
+ handle: string;
172
+ credentialId: string;
173
+ consumer: string;
174
+ }[];
175
+ materials: {
176
+ credentialId: string;
177
+ providerKey: string;
178
+ origin: string;
179
+ secret: string;
180
+ }[];
181
+ };
182
+ };
164
183
  } | {
165
184
  type: "ready";
166
185
  data: {
@@ -269,6 +288,45 @@ export declare const ControlPayload: import("arktype/internal/variants/object.ts
269
288
  message: string;
270
289
  };
271
290
  };
291
+ } | {
292
+ type: "park.notify";
293
+ data: {
294
+ runId: string;
295
+ correlationId: string;
296
+ parkKind: "approval" | "input" | "signal-relay";
297
+ snapshot?: {
298
+ name: string;
299
+ description: string;
300
+ inputSchema: Record<string, unknown>;
301
+ arguments: Record<string, unknown>;
302
+ };
303
+ };
304
+ } | {
305
+ type: "parked-correlations.request";
306
+ data: {
307
+ requestId: string;
308
+ };
309
+ } | {
310
+ type: "parked-correlations.response";
311
+ data: {
312
+ requestId: string;
313
+ parked: {
314
+ runId: string;
315
+ correlationId: string;
316
+ parkKind: "approval" | "input" | "signal-relay";
317
+ snapshot?: {
318
+ name: string;
319
+ description: string;
320
+ inputSchema: Record<string, unknown>;
321
+ arguments: Record<string, unknown>;
322
+ };
323
+ }[];
324
+ };
325
+ } | {
326
+ type: "resumed.runs";
327
+ data: {
328
+ runIds: string[];
329
+ };
272
330
  }, {}>;
273
331
  export type ControlPayload = typeof ControlPayload.infer;
274
332
  export interface NdjsonWriter {
@@ -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
  /**
@@ -150,6 +151,12 @@ export const ControlPayload = type({
150
151
  runId: "string",
151
152
  signalName: "string",
152
153
  signalId: "string",
154
+ // The resume decision in FINAL form -- the child commits it as the
155
+ // SignalReceived payload verbatim. Each sender owns any
156
+ // provenance-specific preparation BEFORE this frame: the dispatch loop
157
+ // resolves an inbound mail to conversation text (like the turn-1
158
+ // trigger), while `deliverSignal` ships a structured signal payload
159
+ // unchanged. Do NOT ship raw inbound mail bytes through here.
153
160
  payload: "unknown",
154
161
  },
155
162
  })
@@ -191,6 +198,18 @@ export const ControlPayload = type({
191
198
  .or({
192
199
  type: "'sources-updated'",
193
200
  data: SourcesUpdatedData,
201
+ })
202
+ .or({
203
+ // Refreshed credential material for the deployment's tools. The child
204
+ // replaces its in-memory material cell wholesale on receive; a revoked
205
+ // credential arrives by omission (its material entry is absent) so the
206
+ // swap evicts it. Carried inline like the grants and sources snapshots --
207
+ // single-producer supervisor, single-consumer child. The secret rides
208
+ // this frame and the in-memory cell only; it is never persisted.
209
+ type: "'credentials-updated'",
210
+ data: {
211
+ delivery: CredentialDelivery,
212
+ },
194
213
  })
195
214
  .or({
196
215
  type: "'ready'",
@@ -377,6 +396,80 @@ export const ControlPayload = type({
377
396
  message: "string",
378
397
  },
379
398
  },
399
+ })
400
+ .or({
401
+ // Child-initiated control-plane suspension notification. The
402
+ // workflow-process child emits this when a workflow agent step parks
403
+ // on a reserved `signalName(correlationId)` channel (`env.onPark`),
404
+ // so the supervisor can register the correlation out-of-band before
405
+ // the parked run can be resumed. The supervisor stamps the
406
+ // deployment identity it owns (`runId` + `agentAddress`) and
407
+ // forwards a `signal.correlation.register` frame to the hub, which
408
+ // co-writes the run's routing + approval rows. Mirrors
409
+ // `terminal.event`: a peer-channel notification the supervisor fans
410
+ // out, distinct from the substrate commit the run also produces.
411
+ //
412
+ // `signalName` is deliberately NOT carried: it is a pure function of
413
+ // `correlationId` (`signalName(correlationId)`), recomputed by every
414
+ // consumer that needs it, so the two cannot drift.
415
+ type: "'park.notify'",
416
+ data: {
417
+ runId: "string > 0",
418
+ correlationId: "string > 0",
419
+ parkKind: ControlParkKind,
420
+ // Approver-facing snapshot of the parked tool call, size-capped at this
421
+ // process boundary. Optional: only an ask-rail suspension carries one.
422
+ "snapshot?": BoundedApprovalSnapshot,
423
+ },
424
+ })
425
+ .or({
426
+ // Supervisor-initiated request: enumerate the child's currently-parked
427
+ // approval correlations. The supervisor fires this after a
428
+ // re-establishment (child respawn, or hub-link reconnect fanned out to
429
+ // this deployment) so it can re-register at the hub every correlation
430
+ // whose original `park.notify`-driven register may have been lost while
431
+ // the hub was down. Modeled on `substrate.merge.request`: a
432
+ // supervisor->child request the child answers on `parked-correlations.
433
+ // response`, correlated by `requestId`. Carries no filter -- one child
434
+ // owns one deployment, so the child enumerates everything parked and the
435
+ // supervisor re-emits all; the hub co-write is idempotent.
436
+ type: "'parked-correlations.request'",
437
+ data: {
438
+ requestId: "string > 0",
439
+ },
440
+ })
441
+ .or({
442
+ // Child's reply to `parked-correlations.request`. Each entry mirrors
443
+ // `park.notify`'s data -- the child-supplied half of a
444
+ // `SuspensionRegistration` the supervisor stamps its deployment identity
445
+ // onto -- so the supervisor's re-emit path shares one transform with the
446
+ // `park.notify` arm. Only reduced-state approval parks appear here: the
447
+ // child enumerates steps whose reduced phase is `awaiting-signal` on a
448
+ // control-plane `signalName(correlationId)` channel, each of which carries
449
+ // a durable snapshot by construction (a snapshot-less correlated suspend
450
+ // reduces to `failed`, not `awaiting-signal`). `snapshot` is therefore
451
+ // required, and size-capped at this process boundary like `park.notify`.
452
+ type: "'parked-correlations.response'",
453
+ data: {
454
+ requestId: "string > 0",
455
+ parked: type({
456
+ runId: "string > 0",
457
+ correlationId: "string > 0",
458
+ parkKind: ControlParkKind,
459
+ // Snapshot is required for approval parks and absent for input parks.
460
+ "snapshot?": BoundedApprovalSnapshot,
461
+ }).array(),
462
+ },
463
+ })
464
+ .or({
465
+ // Child reports self-discovered runs after reconnect or recycle.
466
+ // The supervisor seeds its cohort tracking from these runIds so
467
+ // drain accumulators and dispatch routing account for runs the
468
+ // supervisor did not personally trigger.fire.
469
+ type: "'resumed.runs'",
470
+ data: {
471
+ runIds: type("string > 0").array(),
472
+ },
380
473
  });
381
474
  /**
382
475
  * 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.thinking.signature";
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";
@@ -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
- * `address`. Returns immediately if no handler is registered --
15
- * the supervisor's lifecycle (`subscribeMailForAddress` returns a
16
- * disposer the supervisor calls on teardown) is the authoritative
17
- * source of which addresses are live; addresses without an active
18
- * subscriber drop the message silently.
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
- return;
57
- for (const handler of set)
58
- handler(message);
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
@@ -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
- data: {
15
- timerId: string;
16
- fireAt?: string;
17
- stepId?: string | null;
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 = {