@intx/workflow-host 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -14
- package/dist/adapters/mail-part-store.d.ts +46 -0
- package/dist/adapters/mail-part-store.js +251 -0
- package/dist/adapters/repo-store.d.ts +22 -1
- package/dist/adapters/repo-store.js +56 -65
- package/dist/adapters/spawn-child.d.ts +109 -44
- package/dist/adapters/spawn-child.js +77 -81
- package/dist/adapters/step-invoker.d.ts +52 -2
- package/dist/adapters/step-invoker.js +284 -37
- package/dist/adapters/substrate-mailbox-store.d.ts +80 -0
- package/dist/adapters/substrate-mailbox-store.js +404 -0
- package/dist/child/child-mailbox-reader.d.ts +10 -0
- package/dist/child/child-mailbox-reader.js +23 -0
- package/dist/child/credential-cell.d.ts +8 -0
- package/dist/child/credential-cell.js +66 -0
- package/dist/child/env-bootstrap.d.ts +20 -6
- package/dist/child/env-bootstrap.js +9 -1
- package/dist/child/from-process-env.d.ts +12 -0
- package/dist/child/from-process-env.js +6 -0
- package/dist/child/index.d.ts +6 -2
- package/dist/child/index.js +4 -1
- package/dist/child/mailbox-mutation-bridge.d.ts +61 -0
- package/dist/child/mailbox-mutation-bridge.js +101 -0
- package/dist/child/mailbox-watch-registry.d.ts +17 -0
- package/dist/child/mailbox-watch-registry.js +61 -0
- package/dist/child/outbound-mail-bridge.d.ts +3 -2
- package/dist/child/outbound-mail-bridge.js +20 -32
- package/dist/child/parked-correlations.d.ts +42 -0
- package/dist/child/parked-correlations.js +80 -0
- package/dist/child/pending-request.d.ts +89 -0
- package/dist/child/pending-request.js +80 -0
- package/dist/child/proxy-repo-store.d.ts +3 -2
- package/dist/child/proxy-repo-store.js +2 -0
- package/dist/child/run-child.d.ts +170 -14
- package/dist/child/run-child.js +569 -155
- package/dist/child/self-discovery.d.ts +10 -0
- package/dist/child/self-discovery.js +25 -1
- package/dist/child/substrate-write-bridge.d.ts +3 -2
- package/dist/child/substrate-write-bridge.js +21 -38
- package/dist/child/supervisor-backed-transport.d.ts +52 -6
- package/dist/child/supervisor-backed-transport.js +205 -62
- package/dist/child/verified-definition-loader.d.ts +33 -0
- package/dist/child/verified-definition-loader.js +43 -0
- package/dist/child/warm-agent-cache.d.ts +44 -4
- package/dist/child/warm-agent-cache.js +41 -10
- package/dist/index.d.ts +6 -4
- package/dist/index.js +6 -4
- package/dist/ipc/control-channel.d.ts +151 -2
- package/dist/ipc/control-channel.js +222 -29
- package/dist/ipc/event-channel.d.ts +32 -1
- package/dist/ipc/index.d.ts +1 -1
- package/dist/ipc/index.js +1 -1
- package/dist/mail-bus/hub-transport-adapter.d.ts +12 -7
- package/dist/mail-bus/hub-transport-adapter.js +9 -5
- package/dist/run-body-then-cleanup.d.ts +17 -0
- package/dist/run-body-then-cleanup.js +38 -0
- package/dist/seams/scheduler.d.ts +16 -6
- package/dist/seams/scheduler.js +87 -97
- package/dist/supervisor/cancel-signing.d.ts +2 -2
- package/dist/supervisor/cancel-signing.js +4 -8
- package/dist/supervisor/credentials.d.ts +28 -15
- package/dist/supervisor/credentials.js +7 -7
- package/dist/supervisor/dispatch-attribution.js +1 -1
- package/dist/supervisor/drain-timeout.d.ts +2 -2
- package/dist/supervisor/drain-timeout.js +1 -1
- package/dist/supervisor/index.d.ts +3 -3
- package/dist/supervisor/index.js +2 -2
- package/dist/supervisor/recycle.d.ts +10 -3
- package/dist/supervisor/recycle.js +18 -7
- package/dist/supervisor/run-event-compaction.d.ts +5 -5
- package/dist/supervisor/run-event-compaction.js +14 -19
- package/dist/supervisor/run-event-recovery.d.ts +34 -0
- package/dist/supervisor/run-event-recovery.js +45 -0
- package/dist/supervisor/spawn-env.d.ts +2 -2
- package/dist/supervisor/spawn-env.js +1 -1
- package/dist/supervisor/supervisor.d.ts +106 -26
- package/dist/supervisor/supervisor.js +1903 -414
- package/dist/supervisor/terminal-commit.d.ts +36 -0
- package/dist/supervisor/terminal-commit.js +130 -0
- package/dist/supervisor/types.d.ts +180 -23
- package/dist/testing/change-notifier.d.ts +12 -0
- package/dist/testing/change-notifier.js +63 -0
- package/dist/testing/index.d.ts +8 -0
- package/dist/testing/index.js +16 -0
- package/dist/testing/log-capture.d.ts +52 -0
- package/dist/testing/log-capture.js +124 -0
- package/dist/testing/mail-bus.d.ts +22 -0
- package/dist/testing/mail-bus.js +78 -0
- package/dist/testing/memory-streams.d.ts +43 -0
- package/dist/testing/memory-streams.js +211 -0
- package/dist/testing/spawn-observer.d.ts +12 -0
- package/dist/testing/spawn-observer.js +36 -0
- package/dist/testing/stub-repo-store.d.ts +10 -0
- package/dist/testing/stub-repo-store.js +39 -0
- package/dist/testing/supervisor-reaper.d.ts +24 -0
- package/dist/testing/supervisor-reaper.js +49 -0
- package/dist/testing/upstream-frames.d.ts +47 -0
- package/dist/testing/upstream-frames.js +94 -0
- package/dist/workflow-definition-loader.d.ts +187 -0
- package/dist/workflow-definition-loader.js +422 -0
- package/package.json +18 -11
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ControlPayload } from "../ipc/control-channel.js";
|
|
2
|
+
/**
|
|
3
|
+
* Decode every payload of `type_` from `lines`, in arrival order.
|
|
4
|
+
*
|
|
5
|
+
* Frames that fail envelope or payload validation are skipped, so a test
|
|
6
|
+
* asserting on one payload kind is not fooled by an unrelated frame sharing
|
|
7
|
+
* the stream.
|
|
8
|
+
*/
|
|
9
|
+
export declare function readPayloadsOfType<T extends string>(lines: readonly string[], type_: T): Extract<typeof ControlPayload.infer, {
|
|
10
|
+
type: T;
|
|
11
|
+
}>[];
|
|
12
|
+
/** The runIds carried on every `trigger.fire` frame in `lines`, in order. */
|
|
13
|
+
export declare function parseTriggerFireRunIds(lines: readonly string[]): string[];
|
|
14
|
+
/** The part of a stream double this module reads. */
|
|
15
|
+
export type UpstreamFrameSource = {
|
|
16
|
+
flushed(): readonly string[];
|
|
17
|
+
nextWrite(): Promise<void>;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Resolve with the first payload of `type_` that `match` accepts, whether it
|
|
21
|
+
* is already buffered or arrives later.
|
|
22
|
+
*
|
|
23
|
+
* The buffer is re-read only when a line actually arrives, and the wait
|
|
24
|
+
* carries no deadline: a frame that never comes is caught by the lane
|
|
25
|
+
* timeout, per "Synchronizing on State, Not Time" in CONVENTIONS.md.
|
|
26
|
+
*/
|
|
27
|
+
export declare function waitForUpstreamPayload<T extends string>(stream: UpstreamFrameSource, type_: T, match?: (payload: Extract<typeof ControlPayload.infer, {
|
|
28
|
+
type: T;
|
|
29
|
+
}>) => boolean): Promise<Extract<typeof ControlPayload.infer, {
|
|
30
|
+
type: T;
|
|
31
|
+
}>>;
|
|
32
|
+
/**
|
|
33
|
+
* Resolve once at least `count` payloads of `type_` have been written, with
|
|
34
|
+
* every matching payload in arrival order.
|
|
35
|
+
*
|
|
36
|
+
* The count form exists because most waits here are for the Nth frame rather
|
|
37
|
+
* than for a particular one; a caller wanting a specific frame should use
|
|
38
|
+
* `waitForUpstreamPayload` with a predicate.
|
|
39
|
+
*/
|
|
40
|
+
export declare function waitForUpstreamPayloads<T extends string>(stream: UpstreamFrameSource, type_: T, count?: number): Promise<Extract<typeof ControlPayload.infer, {
|
|
41
|
+
type: T;
|
|
42
|
+
}>[]>;
|
|
43
|
+
/**
|
|
44
|
+
* Resolve once at least `count` `trigger.fire` frames have been written, with
|
|
45
|
+
* every runId seen in order.
|
|
46
|
+
*/
|
|
47
|
+
export declare function waitForTriggerFireRunIds(stream: UpstreamFrameSource, count: number): Promise<string[]>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Reading and awaiting the control frames a supervisor writes upstream.
|
|
2
|
+
//
|
|
3
|
+
// A test that drives a supervisor asserts on the frames it emitted, and has
|
|
4
|
+
// to wait for the one it cares about first. That wait was written as a
|
|
5
|
+
// deadline plus a one-millisecond tick that re-decoded the whole buffer --
|
|
6
|
+
// two wall-clock numbers deciding a run whose subject is what a frame
|
|
7
|
+
// CONTAINS, never how quickly it appears. Waiting on the write removes the
|
|
8
|
+
// window instead of widening it.
|
|
9
|
+
import { type } from "arktype";
|
|
10
|
+
import { ControlPayload } from "../ipc/control-channel.js";
|
|
11
|
+
import { SignedEnvelope } from "../ipc/envelope.js";
|
|
12
|
+
/**
|
|
13
|
+
* Decode every payload of `type_` from `lines`, in arrival order.
|
|
14
|
+
*
|
|
15
|
+
* Frames that fail envelope or payload validation are skipped, so a test
|
|
16
|
+
* asserting on one payload kind is not fooled by an unrelated frame sharing
|
|
17
|
+
* the stream.
|
|
18
|
+
*/
|
|
19
|
+
export function readPayloadsOfType(lines, type_) {
|
|
20
|
+
const out = [];
|
|
21
|
+
for (const line of lines) {
|
|
22
|
+
let raw;
|
|
23
|
+
try {
|
|
24
|
+
raw = JSON.parse(line);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const signed = SignedEnvelope(raw);
|
|
30
|
+
if (signed instanceof type.errors)
|
|
31
|
+
continue;
|
|
32
|
+
const payload = ControlPayload(signed.envelope.payload);
|
|
33
|
+
if (payload instanceof type.errors)
|
|
34
|
+
continue;
|
|
35
|
+
if (payload.type !== type_)
|
|
36
|
+
continue;
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- the arktype narrow above pins the discriminator; the cast walks the union to the matching branch
|
|
38
|
+
out.push(payload);
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
/** The runIds carried on every `trigger.fire` frame in `lines`, in order. */
|
|
43
|
+
export function parseTriggerFireRunIds(lines) {
|
|
44
|
+
return readPayloadsOfType(lines, "trigger.fire").map((p) => p.data.runId);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolve with the first payload of `type_` that `match` accepts, whether it
|
|
48
|
+
* is already buffered or arrives later.
|
|
49
|
+
*
|
|
50
|
+
* The buffer is re-read only when a line actually arrives, and the wait
|
|
51
|
+
* carries no deadline: a frame that never comes is caught by the lane
|
|
52
|
+
* timeout, per "Synchronizing on State, Not Time" in CONVENTIONS.md.
|
|
53
|
+
*/
|
|
54
|
+
export async function waitForUpstreamPayload(stream, type_, match = () => true) {
|
|
55
|
+
for (;;) {
|
|
56
|
+
// Re-read on every pass, so a frame already in the buffer resolves this
|
|
57
|
+
// rather than leaving it waiting for the next write.
|
|
58
|
+
const arrived = stream.nextWrite();
|
|
59
|
+
const found = readPayloadsOfType(stream.flushed(), type_).find(match);
|
|
60
|
+
if (found !== undefined)
|
|
61
|
+
return found;
|
|
62
|
+
await arrived;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Resolve once at least `count` payloads of `type_` have been written, with
|
|
67
|
+
* every matching payload in arrival order.
|
|
68
|
+
*
|
|
69
|
+
* The count form exists because most waits here are for the Nth frame rather
|
|
70
|
+
* than for a particular one; a caller wanting a specific frame should use
|
|
71
|
+
* `waitForUpstreamPayload` with a predicate.
|
|
72
|
+
*/
|
|
73
|
+
export async function waitForUpstreamPayloads(stream, type_, count = 1) {
|
|
74
|
+
for (;;) {
|
|
75
|
+
const arrived = stream.nextWrite();
|
|
76
|
+
const found = readPayloadsOfType(stream.flushed(), type_);
|
|
77
|
+
if (found.length >= count)
|
|
78
|
+
return found;
|
|
79
|
+
await arrived;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Resolve once at least `count` `trigger.fire` frames have been written, with
|
|
84
|
+
* every runId seen in order.
|
|
85
|
+
*/
|
|
86
|
+
export async function waitForTriggerFireRunIds(stream, count) {
|
|
87
|
+
for (;;) {
|
|
88
|
+
const arrived = stream.nextWrite();
|
|
89
|
+
const ids = parseTriggerFireRunIds(stream.flushed());
|
|
90
|
+
if (ids.length >= count)
|
|
91
|
+
return ids;
|
|
92
|
+
await arrived;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { type AnnotatedPluginFactory, type DirectorRegistry, type ToolDeclaration } from "@intx/agent";
|
|
2
|
+
import type { WorkflowDefinition } from "@intx/workflow/definition";
|
|
3
|
+
import type { ActionHandler, LoopFnRegistry } from "@intx/workflow";
|
|
4
|
+
export interface LoadWorkflowDefinitionFromClosureArgs {
|
|
5
|
+
/**
|
|
6
|
+
* Directory of the materialized workflow package within the closure:
|
|
7
|
+
* the directory holding the package's `package.json`, with its
|
|
8
|
+
* `node_modules/` already laid out by the closure-materialization
|
|
9
|
+
* machinery so the entry module's bare-specifier imports resolve.
|
|
10
|
+
*/
|
|
11
|
+
readonly packageDir: string;
|
|
12
|
+
/**
|
|
13
|
+
* Optional token mixed into the import URL's query string to bust
|
|
14
|
+
* Node's ESM module cache. Node keys the ESM cache by resolved
|
|
15
|
+
* URL/path, not by content: a process that imports the same package
|
|
16
|
+
* directory twice with different bytes underneath (a rare re-apply in
|
|
17
|
+
* a reused child) would otherwise resolve to the first-imported module
|
|
18
|
+
* instance. Passing a per-materialization token (the closure's
|
|
19
|
+
* integrity SRI is the natural choice) makes each materialization a
|
|
20
|
+
* distinct ESM cache entry. Omit it when the process imports a given
|
|
21
|
+
* package directory at most once.
|
|
22
|
+
*/
|
|
23
|
+
readonly importCacheKey?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Test seam for dynamic import. Production omits this and the loader
|
|
26
|
+
* uses the native dynamic-import expression. The argument is the
|
|
27
|
+
* `file://` URL the loader resolves for the `interchange.workflow`
|
|
28
|
+
* entry.
|
|
29
|
+
*/
|
|
30
|
+
readonly importModule?: (importUrl: string) => Promise<unknown>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Import the `interchange.workflow` entry from a materialized workflow
|
|
34
|
+
* package closure, evaluate it, and return the validated
|
|
35
|
+
* `WorkflowDefinition` its `defineWorkflow(...)` call produced.
|
|
36
|
+
*
|
|
37
|
+
* @param args - the materialized package directory plus optional import
|
|
38
|
+
* seams
|
|
39
|
+
* @returns the validated `WorkflowDefinition`
|
|
40
|
+
* @throws if the package.json is missing/malformed, declares no
|
|
41
|
+
* `interchange.workflow` entry, the entry path escapes the package
|
|
42
|
+
* directory, the module cannot be imported, or its evaluation does not
|
|
43
|
+
* produce exactly one value that validates as a `WorkflowDefinition`
|
|
44
|
+
*/
|
|
45
|
+
export declare function loadWorkflowDefinitionFromClosure(args: LoadWorkflowDefinitionFromClosureArgs): Promise<WorkflowDefinition>;
|
|
46
|
+
export interface LoadWorkflowDirectorRegistryFromClosureArgs {
|
|
47
|
+
/**
|
|
48
|
+
* Directory of the materialized workflow package within the closure --
|
|
49
|
+
* the same directory `loadWorkflowDefinitionFromClosure` reads. Both the
|
|
50
|
+
* approval-time probe and the run-child call this over the SAME frozen
|
|
51
|
+
* closure, so the director set they compose cannot drift.
|
|
52
|
+
*/
|
|
53
|
+
readonly packageDir: string;
|
|
54
|
+
/** See `LoadWorkflowDefinitionFromClosureArgs.importCacheKey`. */
|
|
55
|
+
readonly importCacheKey?: string;
|
|
56
|
+
/** Test seam for dynamic import; see the definition loader's variant. */
|
|
57
|
+
readonly importModule?: (importUrl: string) => Promise<unknown>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Compose the `DirectorRegistry` for a workflow closure from the closure
|
|
61
|
+
* package's OWN `interchange.directors` module (if any), alongside the
|
|
62
|
+
* built-in default director. A package with no `interchange.directors`
|
|
63
|
+
* field composes to the built-ins-only registry -- absence is valid, a
|
|
64
|
+
* workflow need not ship a director. A present-but-empty directors module
|
|
65
|
+
* is malformed and throws, matching the tool-package loader.
|
|
66
|
+
*
|
|
67
|
+
* Only the workflow's OWN package directors are loaded here. Directors
|
|
68
|
+
* shipped by PINNED dependency packages are deliberately not resolved on
|
|
69
|
+
* the source-ref path yet: the airlocked probe does not materialize pinned
|
|
70
|
+
* packages, so loading them here would let the runtime resolve a director
|
|
71
|
+
* the probe never advertised for approval. A workflow referencing a
|
|
72
|
+
* pinned-package director fails closed (the capability walk reports it as
|
|
73
|
+
* unresolved).
|
|
74
|
+
*
|
|
75
|
+
* @throws if the directors entry path escapes the package, the module
|
|
76
|
+
* cannot be imported, or it exports no `AnnotatedDirectorFactory` value
|
|
77
|
+
*/
|
|
78
|
+
export declare function loadWorkflowDirectorRegistryFromClosure(args: LoadWorkflowDirectorRegistryFromClosureArgs): Promise<DirectorRegistry>;
|
|
79
|
+
export interface LoadWorkflowLoopFnsFromClosureArgs {
|
|
80
|
+
/**
|
|
81
|
+
* Directory of the materialized workflow package within the closure --
|
|
82
|
+
* the same directory `loadWorkflowDefinitionFromClosure` reads.
|
|
83
|
+
*/
|
|
84
|
+
readonly packageDir: string;
|
|
85
|
+
/** See `LoadWorkflowDefinitionFromClosureArgs.importCacheKey`. */
|
|
86
|
+
readonly importCacheKey?: string;
|
|
87
|
+
/** Test seam for dynamic import; see the definition loader's variant. */
|
|
88
|
+
readonly importModule?: (importUrl: string) => Promise<unknown>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Compose the `LoopFnRegistry` for a workflow closure from the closure
|
|
92
|
+
* package's OWN `interchange.loops` module. A `loop` primitive's `while` and
|
|
93
|
+
* `carry` refs resolve by EXPORT NAME against that module's exports.
|
|
94
|
+
*
|
|
95
|
+
* Unlike directors there is NO built-in default: a package with no
|
|
96
|
+
* `interchange.loops` field composes to an EMPTY registry that throws on any
|
|
97
|
+
* ref lookup. A workflow that declares a `loop` but ships no loops module thus
|
|
98
|
+
* fails closed when its refs are resolved (eagerly, at establish); a workflow
|
|
99
|
+
* with no `loop` primitive never resolves a ref, so an absent field is valid
|
|
100
|
+
* there. Loading OUTSIDE the definition-hash re-verify is safe: the approved
|
|
101
|
+
* hash pins each ref string, and the closure's SRI pins the module bytes.
|
|
102
|
+
*
|
|
103
|
+
* @throws (from the returned registry) if a requested ref names no export, or
|
|
104
|
+
* names an export that is not a function.
|
|
105
|
+
* @throws if the loops entry path escapes the package or cannot be imported.
|
|
106
|
+
*/
|
|
107
|
+
export declare function loadWorkflowLoopFnsFromClosure(args: LoadWorkflowLoopFnsFromClosureArgs): Promise<LoopFnRegistry>;
|
|
108
|
+
export interface LoadWorkflowActionHandlersFromClosureArgs {
|
|
109
|
+
/** Directory of the materialized workflow package within the closure. */
|
|
110
|
+
readonly packageDir: string;
|
|
111
|
+
/** See `LoadWorkflowDefinitionFromClosureArgs.importCacheKey`. */
|
|
112
|
+
readonly importCacheKey?: string;
|
|
113
|
+
/** Test seam for dynamic import; see the definition loader's variant. */
|
|
114
|
+
readonly importModule?: (importUrl: string) => Promise<unknown>;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Compose the action-handler resolver for a workflow closure from the closure
|
|
118
|
+
* package's OWN `interchange.actions` module. An `action` primitive's `handler`
|
|
119
|
+
* ref resolves by EXPORT NAME against that module's exports.
|
|
120
|
+
*
|
|
121
|
+
* Mirrors {@link loadWorkflowLoopFnsFromClosure}: there is NO built-in default,
|
|
122
|
+
* so a package with no `interchange.actions` field composes to a resolver that
|
|
123
|
+
* throws on any lookup. A workflow that declares an `action` but ships no
|
|
124
|
+
* actions module fails closed when its handler is resolved (eagerly, at
|
|
125
|
+
* establish); a workflow with no `action` primitive never resolves a handler.
|
|
126
|
+
* Loading OUTSIDE the definition-hash re-verify is safe: the approved hash pins
|
|
127
|
+
* each handler ref string, and the closure's SRI pins the module bytes.
|
|
128
|
+
*
|
|
129
|
+
* @throws (from the returned resolver) if a requested ref names no export, or an
|
|
130
|
+
* export that is not a function.
|
|
131
|
+
* @throws if the actions entry path escapes the package or cannot be imported.
|
|
132
|
+
*/
|
|
133
|
+
export declare function loadWorkflowActionHandlersFromClosure(args: LoadWorkflowActionHandlersFromClosureArgs): Promise<(ref: string) => ActionHandler>;
|
|
134
|
+
export interface LoadWorkflowPluginsFromClosureArgs {
|
|
135
|
+
/**
|
|
136
|
+
* Directory of the materialized workflow package within the closure --
|
|
137
|
+
* the same directory `loadWorkflowDefinitionFromClosure` reads. Each
|
|
138
|
+
* declared plugin package is resolved from this package's laid-out
|
|
139
|
+
* `node_modules/`, exactly as the workflow entry's own bare-specifier
|
|
140
|
+
* imports resolve.
|
|
141
|
+
*/
|
|
142
|
+
readonly packageDir: string;
|
|
143
|
+
/**
|
|
144
|
+
* Plugin-package names the workflow's agents declare via
|
|
145
|
+
* `AgentDefinition.plugins` (`["@intx/tools-lsp"]`). Each MUST be a
|
|
146
|
+
* direct dependency of the workflow package so it is laid out under the
|
|
147
|
+
* workflow package's `node_modules/`. Empty is valid (no plugins).
|
|
148
|
+
*/
|
|
149
|
+
readonly plugins: readonly string[];
|
|
150
|
+
/** See `LoadWorkflowDefinitionFromClosureArgs.importCacheKey`. */
|
|
151
|
+
readonly importCacheKey?: string;
|
|
152
|
+
/** Test seam for dynamic import; see the definition loader's variant. */
|
|
153
|
+
readonly importModule?: (importUrl: string) => Promise<unknown>;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Import each declared plugin package's `interchange.tools` module from the
|
|
157
|
+
* materialized workflow closure and collect the `AnnotatedPluginFactory`
|
|
158
|
+
* values it exports. This is the run-child counterpart to the tool-package
|
|
159
|
+
* loader's plugin channel: a source-ref workflow contributes no plugin factory
|
|
160
|
+
* through its agent definition (a plugin has no agent slot), so the child
|
|
161
|
+
* materializes the declared plugins straight from the already-laid-out closure
|
|
162
|
+
* -- no re-download, no manifest -- and feeds them into the existing per-step
|
|
163
|
+
* plugin chain. The closure bytes were SRI-verified when the deploy applied the
|
|
164
|
+
* frozen closure, and resolution walks the same `node_modules/` graph the
|
|
165
|
+
* workflow entry's imports use.
|
|
166
|
+
*
|
|
167
|
+
* @throws if a declared plugin package cannot be resolved, declares no
|
|
168
|
+
* `interchange.tools` entry, the entry escapes the package, cannot be
|
|
169
|
+
* imported, or exports no `AnnotatedPluginFactory` value
|
|
170
|
+
*/
|
|
171
|
+
export declare function loadWorkflowPluginFactoriesFromClosure(args: LoadWorkflowPluginsFromClosureArgs): Promise<AnnotatedPluginFactory[]>;
|
|
172
|
+
/**
|
|
173
|
+
* Read the static tool `definitions` each declared plugin package
|
|
174
|
+
* contributes, keyed by plugin-package name, WITHOUT retaining the plugin
|
|
175
|
+
* factory (so the caller never instantiates a plugin, which for LSP would
|
|
176
|
+
* start a subprocess). This is the probe/capability-walk counterpart to
|
|
177
|
+
* `loadWorkflowPluginFactoriesFromClosure`: it loads the SAME plugin module
|
|
178
|
+
* from the SAME frozen closure so the tool grant surface the walk approves
|
|
179
|
+
* matches the plugin the run-child materializes.
|
|
180
|
+
*
|
|
181
|
+
* A plugin package that exports plugin factories but declares no tool
|
|
182
|
+
* definitions (a middleware-only plugin) maps to an empty array -- valid,
|
|
183
|
+
* it contributes no tool grant.
|
|
184
|
+
*
|
|
185
|
+
* @throws under the same conditions as `loadWorkflowPluginFactoriesFromClosure`
|
|
186
|
+
*/
|
|
187
|
+
export declare function loadWorkflowPluginToolDefinitionsFromClosure(args: LoadWorkflowPluginsFromClosureArgs): Promise<Map<string, readonly ToolDeclaration[]>>;
|