@intx/workflow-host 0.3.0 → 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.
Files changed (78) hide show
  1. package/README.md +21 -4
  2. package/dist/adapters/mail-part-store.d.ts +46 -0
  3. package/dist/adapters/mail-part-store.js +251 -0
  4. package/dist/adapters/repo-store.js +5 -14
  5. package/dist/adapters/spawn-child.d.ts +42 -6
  6. package/dist/adapters/spawn-child.js +8 -18
  7. package/dist/adapters/step-invoker.d.ts +52 -2
  8. package/dist/adapters/step-invoker.js +230 -60
  9. package/dist/adapters/substrate-mailbox-store.d.ts +80 -0
  10. package/dist/adapters/substrate-mailbox-store.js +404 -0
  11. package/dist/child/child-mailbox-reader.d.ts +10 -0
  12. package/dist/child/child-mailbox-reader.js +23 -0
  13. package/dist/child/credential-cell.d.ts +8 -0
  14. package/dist/child/credential-cell.js +66 -0
  15. package/dist/child/from-process-env.d.ts +12 -0
  16. package/dist/child/from-process-env.js +6 -0
  17. package/dist/child/index.d.ts +4 -1
  18. package/dist/child/index.js +4 -1
  19. package/dist/child/mailbox-mutation-bridge.d.ts +61 -0
  20. package/dist/child/mailbox-mutation-bridge.js +101 -0
  21. package/dist/child/mailbox-watch-registry.d.ts +17 -0
  22. package/dist/child/mailbox-watch-registry.js +61 -0
  23. package/dist/child/outbound-mail-bridge.d.ts +3 -2
  24. package/dist/child/outbound-mail-bridge.js +20 -32
  25. package/dist/child/pending-request.d.ts +89 -0
  26. package/dist/child/pending-request.js +80 -0
  27. package/dist/child/run-child.d.ts +69 -7
  28. package/dist/child/run-child.js +307 -75
  29. package/dist/child/substrate-write-bridge.d.ts +3 -2
  30. package/dist/child/substrate-write-bridge.js +21 -38
  31. package/dist/child/supervisor-backed-transport.d.ts +52 -6
  32. package/dist/child/supervisor-backed-transport.js +205 -62
  33. package/dist/child/warm-agent-cache.d.ts +44 -4
  34. package/dist/child/warm-agent-cache.js +41 -10
  35. package/dist/index.d.ts +4 -3
  36. package/dist/index.js +4 -3
  37. package/dist/ipc/control-channel.d.ts +93 -2
  38. package/dist/ipc/control-channel.js +147 -47
  39. package/dist/ipc/index.d.ts +1 -1
  40. package/dist/ipc/index.js +1 -1
  41. package/dist/run-body-then-cleanup.d.ts +17 -0
  42. package/dist/run-body-then-cleanup.js +38 -0
  43. package/dist/seams/scheduler.d.ts +12 -0
  44. package/dist/seams/scheduler.js +13 -4
  45. package/dist/supervisor/cancel-signing.js +3 -7
  46. package/dist/supervisor/credentials.d.ts +17 -5
  47. package/dist/supervisor/recycle.d.ts +5 -1
  48. package/dist/supervisor/run-event-compaction.d.ts +2 -2
  49. package/dist/supervisor/run-event-compaction.js +11 -16
  50. package/dist/supervisor/run-event-recovery.d.ts +34 -0
  51. package/dist/supervisor/run-event-recovery.js +45 -0
  52. package/dist/supervisor/supervisor.d.ts +27 -4
  53. package/dist/supervisor/supervisor.js +644 -58
  54. package/dist/supervisor/terminal-commit.js +3 -7
  55. package/dist/supervisor/types.d.ts +30 -0
  56. package/dist/testing/change-notifier.d.ts +12 -0
  57. package/dist/testing/change-notifier.js +63 -0
  58. package/dist/testing/index.d.ts +8 -0
  59. package/dist/testing/index.js +16 -0
  60. package/dist/testing/log-capture.d.ts +52 -0
  61. package/dist/testing/log-capture.js +124 -0
  62. package/dist/testing/mail-bus.d.ts +22 -0
  63. package/dist/testing/mail-bus.js +78 -0
  64. package/dist/testing/memory-streams.d.ts +43 -0
  65. package/dist/testing/memory-streams.js +211 -0
  66. package/dist/testing/spawn-observer.d.ts +12 -0
  67. package/dist/testing/spawn-observer.js +36 -0
  68. package/dist/testing/stub-repo-store.d.ts +10 -0
  69. package/dist/testing/stub-repo-store.js +39 -0
  70. package/dist/testing/supervisor-reaper.d.ts +24 -0
  71. package/dist/testing/supervisor-reaper.js +49 -0
  72. package/dist/testing/upstream-frames.d.ts +47 -0
  73. package/dist/testing/upstream-frames.js +94 -0
  74. package/dist/workflow-definition-loader.d.ts +56 -0
  75. package/dist/workflow-definition-loader.js +106 -0
  76. package/package.json +17 -11
  77. package/dist/conversation-text.d.ts +0 -23
  78. package/dist/conversation-text.js +0 -56
@@ -45,11 +45,18 @@ export function createWarmAgentCache() {
45
45
  const entry = entries.get(key);
46
46
  return entry === undefined ? null : entry.agent;
47
47
  }
48
- function store(key, agent, eventSinkRef, eventForward) {
48
+ function store(key, agent, eventSinkRef, eventForward, replyDrive) {
49
49
  if (entries.has(key)) {
50
50
  throw new Error(`warm-agent cache: an entry already exists for ${key}; the step-invoker must reuse the cached agent rather than rebuild it`);
51
51
  }
52
- entries.set(key, { agent, eventSinkRef, eventForward });
52
+ entries.set(key, { agent, eventSinkRef, eventForward, replyDrive });
53
+ }
54
+ function getReplyDrive(key) {
55
+ const entry = entries.get(key);
56
+ if (entry === undefined) {
57
+ throw new Error(`warm-agent cache: getReplyDrive for ${key} with no cached entry; the step-invoker must store the warm agent before fetching its reply barrier`);
58
+ }
59
+ return entry.replyDrive;
53
60
  }
54
61
  function setEventSink(key, onEvent) {
55
62
  const entry = entries.get(key);
@@ -65,8 +72,24 @@ export function createWarmAgentCache() {
65
72
  entry.eventSinkRef.current = null;
66
73
  }
67
74
  function applySources(sources, defaultSource) {
75
+ // Rotate every retained agent before surfacing any failure: one agent
76
+ // rejecting the rotation (an invalid source, or a closed agent racing
77
+ // eviction) must not skip the rest. Collect failures and throw them
78
+ // together. (Warm-keep is single-step today, so the cache holds 0 or 1
79
+ // entry; this keeps the contract honest if warm-keep ever spans steps.)
80
+ const failures = [];
68
81
  for (const entry of entries.values()) {
69
- entry.agent.setSources(sources, defaultSource);
82
+ try {
83
+ entry.agent.setSources(sources, defaultSource);
84
+ }
85
+ catch (cause) {
86
+ const message = cause instanceof Error ? cause.message : String(cause);
87
+ logger.error `warm-agent rotation: setSources failed: ${message}`;
88
+ failures.push(cause);
89
+ }
90
+ }
91
+ if (failures.length > 0) {
92
+ throw new AggregateError(failures, `warm-agent rotation: ${String(failures.length)} agent(s) rejected the source rotation`);
70
93
  }
71
94
  }
72
95
  async function evictAll(reason) {
@@ -74,24 +97,28 @@ export function createWarmAgentCache() {
74
97
  return;
75
98
  const toEvict = [...entries.values()];
76
99
  entries.clear();
100
+ // Close every entry before surfacing any failure. The wrapped close
101
+ // (see `createToolBearingAgentFactory`) runs the agent's own close and
102
+ // then the plugin + tool-bundle disposers, killing the LSP subprocess,
103
+ // and it rejects when a disposer fails. One entry's close rejecting
104
+ // must not strand the remaining entries' teardown -- that would leak
105
+ // exactly the LSP subprocesses warm-keep risks. Collect failures and
106
+ // throw them together once every agent has been closed and drained.
107
+ // (Warm-keep is single-step today, so the cache holds 0 or 1 entry;
108
+ // this keeps the contract honest if warm-keep ever spans steps.)
109
+ const failures = [];
77
110
  for (const entry of toEvict) {
78
111
  // Clear the sink first so any event emitted during the agent's
79
112
  // shutdown window is dropped rather than delivered to a per-run
80
113
  // channel the run-loop is tearing down.
81
114
  entry.eventSinkRef.current = null;
82
115
  try {
83
- // The wrapped close (see `createToolBearingAgentFactory`) runs
84
- // the agent's own close and then the plugin + tool-bundle
85
- // disposers, killing the LSP subprocess. A close failure must
86
- // surface, not be swallowed -- a leaked LSP subprocess is
87
- // exactly the failure warm-keep risks -- so it propagates after
88
- // we have drained what we can.
89
116
  await entry.agent.close();
90
117
  }
91
118
  catch (cause) {
92
119
  const message = cause instanceof Error ? cause.message : String(cause);
93
120
  logger.error `warm-agent eviction (${reason}): agent.close failed: ${message}`;
94
- throw cause instanceof Error ? cause : new Error(message);
121
+ failures.push(cause);
95
122
  }
96
123
  finally {
97
124
  // `agent.close()` terminates the stream iterator, so the
@@ -100,10 +127,14 @@ export function createWarmAgentCache() {
100
127
  await entry.eventForward;
101
128
  }
102
129
  }
130
+ if (failures.length > 0) {
131
+ throw new AggregateError(failures, `warm-agent eviction (${reason}): ${String(failures.length)} agent(s) failed to close; an LSP subprocess may be leaked`);
132
+ }
103
133
  }
104
134
  return {
105
135
  acquire,
106
136
  store,
137
+ getReplyDrive,
107
138
  setEventSink,
108
139
  clearEventSink,
109
140
  applySources,
package/dist/index.d.ts CHANGED
@@ -1,11 +1,12 @@
1
- export { loadWorkflowDefinitionFromClosure, loadWorkflowDirectorRegistryFromClosure, loadWorkflowPluginFactoriesFromClosure, loadWorkflowPluginToolDefinitionsFromClosure, type LoadWorkflowDefinitionFromClosureArgs, type LoadWorkflowDirectorRegistryFromClosureArgs, type LoadWorkflowPluginsFromClosureArgs, } from "./workflow-definition-loader.js";
1
+ export { loadWorkflowDefinitionFromClosure, loadWorkflowDirectorRegistryFromClosure, loadWorkflowLoopFnsFromClosure, loadWorkflowPluginFactoriesFromClosure, loadWorkflowPluginToolDefinitionsFromClosure, type LoadWorkflowDefinitionFromClosureArgs, type LoadWorkflowDirectorRegistryFromClosureArgs, type LoadWorkflowLoopFnsFromClosureArgs, type LoadWorkflowPluginsFromClosureArgs, } from "./workflow-definition-loader.js";
2
2
  export { createWorkflowRunRepoStore, type WorkflowRunRepoStoreOpts, } from "./adapters/repo-store.js";
3
3
  export { createWorkflowRunBlobSubstrate, type WorkflowRunBlobSubstrateOpts, } from "./adapters/blob-substrate.js";
4
+ export { createSubstrateMailboxStore, MAILBOX_PREFIX, MAILBOX_INBOX_DIR, MAILBOX_INDEX_FILE, MAILBOX_EML_SUFFIX, MAILBOX_INBOX_PREFIX, type SubstrateMailboxStore, type SubstrateMailboxStoreOpts, type MailboxSyncKnownState, type MailboxSyncResult, } from "./adapters/substrate-mailbox-store.js";
4
5
  export { createWorkflowStepInvoker, type StepEnvBase, type WorkflowStepInvokerOpts, } from "./adapters/step-invoker.js";
5
6
  export { createInMemorySpawnChild, createInMemorySpawnSuspendableChild, type ChildTerminalStatus, type RunChildWorkflow, type RunSuspendableChild, } from "./adapters/spawn-child.js";
6
7
  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";
7
8
  export { createWorkflowHostDrainController, type WorkflowHostDrainController, type CreateWorkflowHostDrainControllerOpts, } from "./drain-controller.js";
8
9
  export { wrapHubTransportAsMailBus, type HubTransportMailBusAdapter, } from "./mail-bus/index.js";
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";
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
+ export { ControlPayload, DEFAULT_EVENT_BUFFER_LIMIT, EventPayload, FrameEnvelope, IPC_CRYPTO, MailboxNotifyHeaders, 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";
11
+ export { EVENT_CHANNEL_FD, createChildMailboxReader, createChildMailboxMutationBridge, createChildOutboundMailBridge, createChildSubstrateWriteBridge, createCredentialsBackedAuthorize, createMailboxWatchRegistry, createProxyWorkflowRunRepoStore, createSupervisorBackedTransport, createWarmAgentCache, discoverInFlightRuns, parseSpawnTimeEnv, runWorkflowChild, runWorkflowChildFromProcessEnv, type ChildMailboxMutationBridge, type ChildMailboxReader, type ChildOutboundMailBridge, type ChildStepInvoker, type ChildSubstrateWriteBridge, type CreateChildMailboxMutationBridgeOpts, type CreateChildOutboundMailBridgeOpts, type CreateChildSubstrateWriteBridgeOpts, type CreateProxyWorkflowRunRepoStoreOpts, type CredentialsSnapshotRef, type CredentialWiring, type DiscoverRunsOpts, type DiscoveredRun, type DrainController, type GrantEvaluator, type LoadParkedApproval, type MailboxWatchRegistry, type RunWorkflowChildBindings, type RunWorkflowChildFromProcessEnvOpts, type RunWorkflowChildOpts, type RunWorkflowChildResult, type SourcesSnapshotRef, type SpawnTimeEnv, type SubstrateFactory, type SubstrateFactoryEnv, type SubstrateWriteRequest, type SubstrateWriteResponseSink, type SupervisorBackedTransportInbound, type WarmAgentCache, type WarmEventSinkRef, } from "./child/index.js";
11
12
  export { adaptHostScheduler, createWorkflowHostScheduler, createWorkflowHostSignalChannel, SignalReceivedEnvelope, type SchedulerHandle, type SchedulerOpts, type SignalChannelHandle, type SignalChannelOpts, } from "./seams/index.js";
package/dist/index.js CHANGED
@@ -1,11 +1,12 @@
1
- export { loadWorkflowDefinitionFromClosure, loadWorkflowDirectorRegistryFromClosure, loadWorkflowPluginFactoriesFromClosure, loadWorkflowPluginToolDefinitionsFromClosure, } from "./workflow-definition-loader.js";
1
+ export { loadWorkflowDefinitionFromClosure, loadWorkflowDirectorRegistryFromClosure, loadWorkflowLoopFnsFromClosure, loadWorkflowPluginFactoriesFromClosure, loadWorkflowPluginToolDefinitionsFromClosure, } from "./workflow-definition-loader.js";
2
2
  export { createWorkflowRunRepoStore, } from "./adapters/repo-store.js";
3
3
  export { createWorkflowRunBlobSubstrate, } from "./adapters/blob-substrate.js";
4
+ export { createSubstrateMailboxStore, MAILBOX_PREFIX, MAILBOX_INBOX_DIR, MAILBOX_INDEX_FILE, MAILBOX_EML_SUFFIX, MAILBOX_INBOX_PREFIX, } from "./adapters/substrate-mailbox-store.js";
4
5
  export { createWorkflowStepInvoker, } from "./adapters/step-invoker.js";
5
6
  export { createInMemorySpawnChild, createInMemorySpawnSuspendableChild, } from "./adapters/spawn-child.js";
6
7
  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";
7
8
  export { createWorkflowHostDrainController, } from "./drain-controller.js";
8
9
  export { wrapHubTransportAsMailBus, } from "./mail-bus/index.js";
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";
10
- export { EVENT_CHANNEL_FD, createChildOutboundMailBridge, createChildSubstrateWriteBridge, createCredentialsBackedAuthorize, createProxyWorkflowRunRepoStore, createSupervisorBackedTransport, createWarmAgentCache, discoverInFlightRuns, parseSpawnTimeEnv, runWorkflowChild, runWorkflowChildFromProcessEnv, } from "./child/index.js";
10
+ export { ControlPayload, DEFAULT_EVENT_BUFFER_LIMIT, EventPayload, FrameEnvelope, IPC_CRYPTO, MailboxNotifyHeaders, SourcesUpdatedData, MacedEnvelope, OutboundAttachmentPayload, OutboundMessagePayload, SignedEnvelope, createControlChannelSender, createEventChannelSender, decodeEnvelope, encodeEnvelope, generateChannelId, generateHmacKey, receiveControlChannel, receiveEventChannel, signEd25519, signHmac, verifyEd25519, verifyHmac, } from "./ipc/index.js";
11
+ export { EVENT_CHANNEL_FD, createChildMailboxReader, createChildMailboxMutationBridge, createChildOutboundMailBridge, createChildSubstrateWriteBridge, createCredentialsBackedAuthorize, createMailboxWatchRegistry, createProxyWorkflowRunRepoStore, createSupervisorBackedTransport, createWarmAgentCache, discoverInFlightRuns, parseSpawnTimeEnv, runWorkflowChild, runWorkflowChildFromProcessEnv, } from "./child/index.js";
11
12
  export { adaptHostScheduler, createWorkflowHostScheduler, createWorkflowHostSignalChannel, SignalReceivedEnvelope, } from "./seams/index.js";
@@ -44,7 +44,7 @@ export declare const SourcesUpdatedData: import("arktype/internal/variants/objec
44
44
  id: string;
45
45
  provider: string;
46
46
  baseURL: string;
47
- apiKey: string;
47
+ credentialId: string;
48
48
  model: string;
49
49
  defaults?: {
50
50
  maxTokens?: number;
@@ -95,11 +95,45 @@ export declare const OutboundMessagePayload: import("arktype/internal/variants/o
95
95
  dataBase64: string;
96
96
  }[];
97
97
  inReplyTo?: string;
98
+ references?: string[];
98
99
  correlationId?: string;
99
100
  sessionId?: string;
100
101
  tenantId?: string;
101
102
  }, {}>;
102
103
  export type OutboundMessagePayload = typeof OutboundMessagePayload.infer;
104
+ /**
105
+ * Wire shape of the parsed `MessageHeaders` the supervisor rides inline on a
106
+ * `mailbox.notify` frame. Mirrors `@intx/types/runtime`'s `MessageHeaders`
107
+ * field-for-field so a child watcher gets the arrived message's envelope for
108
+ * its `exists` `MailboxEvent` without a substrate round-trip. The four
109
+ * unconditionally-dereferenced fields (`from`, `to`, `date`, `messageId`) are
110
+ * required; the rest are optional, matching the runtime type.
111
+ *
112
+ * Duplicated here as an arktype validator (rather than importing the TypeScript
113
+ * `MessageHeaders` type) so the IPC module validates the header block at the
114
+ * wire boundary, exactly as `OutboundMessagePayload` does for outbound mail.
115
+ */
116
+ export declare const MailboxNotifyHeaders: import("arktype/internal/variants/object.ts").ObjectType<{
117
+ from: string;
118
+ to: string[];
119
+ date: string;
120
+ messageId: string;
121
+ cc?: string[];
122
+ inReplyTo?: string;
123
+ references?: string[];
124
+ subject?: string;
125
+ listId?: string;
126
+ interchangeType?: "conversation.message" | "conversation.join" | "conversation.leave" | "offering.request" | "offering.response" | "offering.error" | "offering.discover" | "offering.catalog" | "payment.required" | "payment.receipt" | "payment.verified" | "approval.request" | "approval.granted" | "approval.denied" | "system.health" | "system.register" | "system.deregister" | "system.credential.refresh";
127
+ interchangeCorrelationId?: string;
128
+ interchangeTenantId?: string;
129
+ interchangeAgentId?: string;
130
+ interchangeSessionId?: string;
131
+ interchangeOfferingId?: string;
132
+ interchangeSchemaVersion?: string;
133
+ traceparent?: string;
134
+ tracestate?: string;
135
+ }, {}>;
136
+ export type MailboxNotifyHeaders = typeof MailboxNotifyHeaders.infer;
103
137
  /**
104
138
  * Discriminated union of every control-channel payload kind. The
105
139
  * `type` discriminator namespaces the control-plane vocabulary so a
@@ -113,6 +147,7 @@ export declare const ControlPayload: import("arktype/internal/variants/object.ts
113
147
  runId: string;
114
148
  messageId: string;
115
149
  receivedAt: number;
150
+ payload: unknown;
116
151
  };
117
152
  } | {
118
153
  type: "signal.deliver";
@@ -152,7 +187,7 @@ export declare const ControlPayload: import("arktype/internal/variants/object.ts
152
187
  id: string;
153
188
  provider: string;
154
189
  baseURL: string;
155
- apiKey: string;
190
+ credentialId: string;
156
191
  model: string;
157
192
  defaults?: {
158
193
  maxTokens?: number;
@@ -179,6 +214,7 @@ export declare const ControlPayload: import("arktype/internal/variants/object.ts
179
214
  secret: string;
180
215
  }[];
181
216
  };
217
+ revoke?: string[];
182
218
  };
183
219
  } | {
184
220
  type: "ready";
@@ -258,6 +294,7 @@ export declare const ControlPayload: import("arktype/internal/variants/object.ts
258
294
  dataBase64: string;
259
295
  }[];
260
296
  inReplyTo?: string;
297
+ references?: string[];
261
298
  correlationId?: string;
262
299
  sessionId?: string;
263
300
  tenantId?: string;
@@ -327,6 +364,60 @@ export declare const ControlPayload: import("arktype/internal/variants/object.ts
327
364
  data: {
328
365
  runIds: string[];
329
366
  };
367
+ } | {
368
+ type: "mailbox.notify";
369
+ data: {
370
+ runId: string;
371
+ mailbox: string;
372
+ uid: number;
373
+ headers: {
374
+ from: string;
375
+ to: string[];
376
+ date: string;
377
+ messageId: string;
378
+ cc?: string[];
379
+ inReplyTo?: string;
380
+ references?: string[];
381
+ subject?: string;
382
+ listId?: string;
383
+ interchangeType?: "conversation.message" | "conversation.join" | "conversation.leave" | "offering.request" | "offering.response" | "offering.error" | "offering.discover" | "offering.catalog" | "payment.required" | "payment.receipt" | "payment.verified" | "approval.request" | "approval.granted" | "approval.denied" | "system.health" | "system.register" | "system.deregister" | "system.credential.refresh";
384
+ interchangeCorrelationId?: string;
385
+ interchangeTenantId?: string;
386
+ interchangeAgentId?: string;
387
+ interchangeSessionId?: string;
388
+ interchangeOfferingId?: string;
389
+ interchangeSchemaVersion?: string;
390
+ traceparent?: string;
391
+ tracestate?: string;
392
+ };
393
+ };
394
+ } | {
395
+ type: "mailbox.mutate.request";
396
+ data: {
397
+ requestId: string;
398
+ runId: string;
399
+ mailbox: string;
400
+ op: "addFlags" | "removeFlags";
401
+ uid: number;
402
+ flags: string[];
403
+ } | {
404
+ requestId: string;
405
+ runId: string;
406
+ mailbox: string;
407
+ op: "expunge";
408
+ };
409
+ } | {
410
+ type: "mailbox.mutate.response";
411
+ data: {
412
+ requestId: string;
413
+ result: {
414
+ ok: true;
415
+ expungedUids?: number[];
416
+ } | {
417
+ ok: false;
418
+ reason: string;
419
+ };
420
+ };
330
421
  }, {}>;
331
422
  export type ControlPayload = typeof ControlPayload.infer;
332
423
  export interface NdjsonWriter {
@@ -127,10 +127,43 @@ export const OutboundMessagePayload = type({
127
127
  "summary?": "string",
128
128
  "attachments?": OutboundAttachmentPayload.array(),
129
129
  "inReplyTo?": "string",
130
+ "references?": "string[]",
130
131
  "correlationId?": "string",
131
132
  "sessionId?": "string",
132
133
  "tenantId?": "string",
133
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
+ });
134
167
  /**
135
168
  * Discriminated union of every control-channel payload kind. The
136
169
  * `type` discriminator namespaces the control-plane vocabulary so a
@@ -138,14 +171,22 @@ export const OutboundMessagePayload = type({
138
171
  * union and not by widening the envelope shape. Inference events
139
172
  * NEVER appear here; they ride the event channel.
140
173
  */
141
- export const ControlPayload = type({
174
+ export const ControlPayload = type.or({
142
175
  type: "'trigger.fire'",
143
176
  data: {
144
177
  runId: "string",
145
178
  messageId: "string",
146
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",
147
188
  },
148
- }, "|", {
189
+ }, {
149
190
  type: "'signal.deliver'",
150
191
  data: {
151
192
  runId: "string",
@@ -154,25 +195,23 @@ export const ControlPayload = type({
154
195
  // The resume decision in FINAL form -- the child commits it as the
155
196
  // SignalReceived payload verbatim. Each sender owns any
156
197
  // 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.
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.
160
202
  payload: "unknown",
161
203
  },
162
- })
163
- .or({
204
+ }, {
164
205
  type: "'drain'",
165
206
  data: {
166
207
  deadlineMs: "number",
167
208
  },
168
- })
169
- .or({
209
+ }, {
170
210
  type: "'shutdown'",
171
211
  data: {
172
212
  reason: "string",
173
213
  },
174
- })
175
- .or({
214
+ }, {
176
215
  type: "'grants-updated'",
177
216
  data: {
178
217
  /**
@@ -194,24 +233,28 @@ export const ControlPayload = type({
194
233
  */
195
234
  "stepHashes?": "Record<string, string>",
196
235
  },
197
- })
198
- .or({
236
+ }, {
199
237
  type: "'sources-updated'",
200
238
  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.
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.
209
252
  type: "'credentials-updated'",
210
253
  data: {
211
254
  delivery: CredentialDelivery,
255
+ "revoke?": "string[]",
212
256
  },
213
- })
214
- .or({
257
+ }, {
215
258
  type: "'ready'",
216
259
  data: {
217
260
  childPid: "number",
@@ -223,8 +266,7 @@ export const ControlPayload = type({
223
266
  */
224
267
  childPublicKey: "string",
225
268
  },
226
- })
227
- .or({
269
+ }, {
228
270
  // Child-initiated request to recycle the workflow-process. The
229
271
  // child emits this when its own self-check decides it needs to be
230
272
  // recycled (an internal consistency error it can't recover from,
@@ -238,8 +280,7 @@ export const ControlPayload = type({
238
280
  data: {
239
281
  reason: "string",
240
282
  },
241
- })
242
- .or({
283
+ }, {
243
284
  // Child-initiated `writeTreePreservingPrefix` request. The child
244
285
  // does not hold a substrate write authority for the workflow-run
245
286
  // repo (single-writer at the ref tip belongs to the supervisor);
@@ -262,8 +303,7 @@ export const ControlPayload = type({
262
303
  preservePrefix: "string > 0",
263
304
  message: "string > 0",
264
305
  },
265
- })
266
- .or({
306
+ }, {
267
307
  // Supervisor-initiated request for the child's merge bytes. Fired
268
308
  // from inside the supervisor's `writeTreePreservingPrefix` merge
269
309
  // callback while the per-repo lock is held; the child receives the
@@ -280,8 +320,7 @@ export const ControlPayload = type({
280
320
  contentBase64: "string",
281
321
  }).array(),
282
322
  },
283
- })
284
- .or({
323
+ }, {
285
324
  // Child's merge result. `requestId` correlates with the
286
325
  // `substrate.write.request` that started the write; the supervisor
287
326
  // resumes its merge callback with the supplied entries (or
@@ -300,8 +339,7 @@ export const ControlPayload = type({
300
339
  reason: "string > 0",
301
340
  }),
302
341
  },
303
- })
304
- .or({
342
+ }, {
305
343
  // Supervisor's terminal reply to a child's `substrate.write.request`.
306
344
  // The `requestId` echoes the child's allocated correlation id so
307
345
  // the child's pending-id map resolves the awaiter. A successful
@@ -321,8 +359,7 @@ export const ControlPayload = type({
321
359
  reason: "string > 0",
322
360
  }),
323
361
  },
324
- })
325
- .or({
362
+ }, {
326
363
  // Child-initiated outbound-mail request (OUTBOUND half of mailbox
327
364
  // ownership, §3a). The workflow-process child never holds the
328
365
  // agent's signing key and never calls `transport.send` itself. When
@@ -348,8 +385,7 @@ export const ControlPayload = type({
348
385
  "mailbox?": "string",
349
386
  message: OutboundMessagePayload,
350
387
  },
351
- })
352
- .or({
388
+ }, {
353
389
  // Supervisor's terminal reply to a child's `outbound.message`. The
354
390
  // `requestId` echoes the child's correlation id so the child's
355
391
  // pending mail-tool awaiter resolves. A successful send surfaces the
@@ -369,8 +405,7 @@ export const ControlPayload = type({
369
405
  reason: "string > 0",
370
406
  }),
371
407
  },
372
- })
373
- .or({
408
+ }, {
374
409
  // Child-initiated terminal-run notification. The workflow-process
375
410
  // child emits this when one of its runs reaches a terminal phase
376
411
  // (`RunCompleted`, `RunFailed`, `RunCancelled`) so the supervisor's
@@ -396,8 +431,7 @@ export const ControlPayload = type({
396
431
  message: "string",
397
432
  },
398
433
  },
399
- })
400
- .or({
434
+ }, {
401
435
  // Child-initiated control-plane suspension notification. The
402
436
  // workflow-process child emits this when a workflow agent step parks
403
437
  // on a reserved `signalName(correlationId)` channel (`env.onPark`),
@@ -421,8 +455,7 @@ export const ControlPayload = type({
421
455
  // process boundary. Optional: only an ask-rail suspension carries one.
422
456
  "snapshot?": BoundedApprovalSnapshot,
423
457
  },
424
- })
425
- .or({
458
+ }, {
426
459
  // Supervisor-initiated request: enumerate the child's currently-parked
427
460
  // approval correlations. The supervisor fires this after a
428
461
  // re-establishment (child respawn, or hub-link reconnect fanned out to
@@ -437,8 +470,7 @@ export const ControlPayload = type({
437
470
  data: {
438
471
  requestId: "string > 0",
439
472
  },
440
- })
441
- .or({
473
+ }, {
442
474
  // Child's reply to `parked-correlations.request`. Each entry mirrors
443
475
  // `park.notify`'s data -- the child-supplied half of a
444
476
  // `SuspensionRegistration` the supervisor stamps its deployment identity
@@ -460,8 +492,7 @@ export const ControlPayload = type({
460
492
  "snapshot?": BoundedApprovalSnapshot,
461
493
  }).array(),
462
494
  },
463
- })
464
- .or({
495
+ }, {
465
496
  // Child reports self-discovered runs after reconnect or recycle.
466
497
  // The supervisor seeds its cohort tracking from these runIds so
467
498
  // drain accumulators and dispatch routing account for runs the
@@ -470,6 +501,75 @@ export const ControlPayload = type({
470
501
  data: {
471
502
  runIds: type("string > 0").array(),
472
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
+ },
473
573
  });
474
574
  /**
475
575
  * Construct the supervisor-side control-channel sender. The
@@ -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";