@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.
- package/README.md +56 -10
- package/dist/adapters/repo-store.d.ts +22 -1
- package/dist/adapters/repo-store.js +53 -53
- package/dist/adapters/spawn-child.d.ts +71 -42
- package/dist/adapters/spawn-child.js +83 -77
- package/dist/adapters/step-invoker.js +84 -7
- package/dist/child/env-bootstrap.d.ts +20 -6
- package/dist/child/env-bootstrap.js +9 -1
- package/dist/child/index.d.ts +2 -1
- package/dist/child/parked-correlations.d.ts +42 -0
- package/dist/child/parked-correlations.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 +107 -13
- package/dist/child/run-child.js +290 -108
- package/dist/child/self-discovery.d.ts +10 -0
- package/dist/child/self-discovery.js +25 -1
- package/dist/child/verified-definition-loader.d.ts +33 -0
- package/dist/child/verified-definition-loader.js +43 -0
- package/dist/conversation-text.d.ts +23 -0
- package/dist/conversation-text.js +56 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/ipc/control-channel.d.ts +58 -0
- package/dist/ipc/control-channel.js +94 -1
- package/dist/ipc/event-channel.d.ts +32 -1
- package/dist/mail-bus/hub-transport-adapter.d.ts +12 -7
- package/dist/mail-bus/hub-transport-adapter.js +9 -5
- package/dist/seams/scheduler.d.ts +4 -6
- package/dist/seams/scheduler.js +74 -93
- package/dist/supervisor/cancel-signing.d.ts +2 -2
- package/dist/supervisor/cancel-signing.js +1 -1
- package/dist/supervisor/credentials.d.ts +11 -10
- 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 +5 -2
- package/dist/supervisor/recycle.js +18 -7
- package/dist/supervisor/run-event-compaction.d.ts +5 -5
- package/dist/supervisor/run-event-compaction.js +5 -5
- package/dist/supervisor/spawn-env.d.ts +2 -2
- package/dist/supervisor/spawn-env.js +1 -1
- package/dist/supervisor/supervisor.d.ts +82 -25
- package/dist/supervisor/supervisor.js +1313 -410
- package/dist/supervisor/terminal-commit.d.ts +36 -0
- package/dist/supervisor/terminal-commit.js +134 -0
- package/dist/supervisor/types.d.ts +150 -23
- package/dist/workflow-definition-loader.d.ts +131 -0
- package/dist/workflow-definition-loader.js +316 -0
- 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 {
|
|
5
|
-
export { createWorkflowSupervisor, assembleCredentialsSnapshot, commitCancelRequested, createDrainTimeoutAccumulator, createRecyclePolicy, defaultStepRepoId, hashGrants, triggerRecycle, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_KILL_TIMEOUT_MS, DEFAULT_POLICY_INTERVAL_MS,
|
|
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 {
|
|
5
|
-
export { createWorkflowSupervisor, assembleCredentialsSnapshot, commitCancelRequested, createDrainTimeoutAccumulator, createRecyclePolicy, defaultStepRepoId, hashGrants, triggerRecycle, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_KILL_TIMEOUT_MS, DEFAULT_POLICY_INTERVAL_MS,
|
|
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.
|
|
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
|
-
*
|
|
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
|
|
@@ -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 = {
|