@agen-ai/agent-orchestration 0.0.0-stage → 0.4.1

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 (145) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +415 -2
  3. package/dist/catalog/parse.d.ts +3 -0
  4. package/dist/catalog/parse.js +13 -0
  5. package/dist/catalog/schemas.d.ts +8 -0
  6. package/dist/catalog/schemas.js +78 -0
  7. package/dist/catalog/types.d.ts +53 -0
  8. package/dist/catalog/types.js +0 -0
  9. package/dist/coordinator/admission.d.ts +7 -0
  10. package/dist/coordinator/admission.js +189 -0
  11. package/dist/coordinator/cancel.d.ts +7 -0
  12. package/dist/coordinator/cancel.js +111 -0
  13. package/dist/coordinator/create.d.ts +3 -0
  14. package/dist/coordinator/create.js +87 -0
  15. package/dist/coordinator/parse.d.ts +11 -0
  16. package/dist/coordinator/parse.js +60 -0
  17. package/dist/coordinator/read.d.ts +4 -0
  18. package/dist/coordinator/read.js +267 -0
  19. package/dist/coordinator/recovery.d.ts +4 -0
  20. package/dist/coordinator/recovery.js +24 -0
  21. package/dist/coordinator/shutdown.d.ts +7 -0
  22. package/dist/coordinator/shutdown.js +66 -0
  23. package/dist/coordinator/types.d.ts +154 -0
  24. package/dist/coordinator/types.js +0 -0
  25. package/dist/delivery/calls.d.ts +4 -0
  26. package/dist/delivery/calls.js +34 -0
  27. package/dist/delivery/journal.d.ts +7 -0
  28. package/dist/delivery/journal.js +57 -0
  29. package/dist/delivery/parse.d.ts +4 -0
  30. package/dist/delivery/parse.js +31 -0
  31. package/dist/delivery/recovery.d.ts +3 -0
  32. package/dist/delivery/recovery.js +18 -0
  33. package/dist/delivery/scheduler.d.ts +3 -0
  34. package/dist/delivery/scheduler.js +123 -0
  35. package/dist/delivery/setup.d.ts +7 -0
  36. package/dist/delivery/setup.js +37 -0
  37. package/dist/delivery/types.d.ts +36 -0
  38. package/dist/delivery/types.js +0 -0
  39. package/dist/execution/authority.d.ts +16 -0
  40. package/dist/execution/authority.js +86 -0
  41. package/dist/execution/cleanup.d.ts +3 -0
  42. package/dist/execution/cleanup.js +75 -0
  43. package/dist/execution/control.d.ts +6 -0
  44. package/dist/execution/control.js +231 -0
  45. package/dist/execution/finish.d.ts +6 -0
  46. package/dist/execution/finish.js +49 -0
  47. package/dist/execution/journal.d.ts +19 -0
  48. package/dist/execution/journal.js +28 -0
  49. package/dist/execution/prepare.d.ts +3 -0
  50. package/dist/execution/prepare.js +153 -0
  51. package/dist/execution/scheduler.d.ts +4 -0
  52. package/dist/execution/scheduler.js +136 -0
  53. package/dist/execution/state.d.ts +107 -0
  54. package/dist/execution/state.js +118 -0
  55. package/dist/execution/stream.d.ts +9 -0
  56. package/dist/execution/stream.js +108 -0
  57. package/dist/host/types.d.ts +95 -0
  58. package/dist/host/types.js +0 -0
  59. package/dist/index.d.ts +20 -0
  60. package/dist/index.js +58 -0
  61. package/dist/mcp/bridge.d.ts +4 -0
  62. package/dist/mcp/bridge.js +222 -0
  63. package/dist/mcp/http.d.ts +11 -0
  64. package/dist/mcp/http.js +73 -0
  65. package/dist/mcp/index.d.ts +3 -0
  66. package/dist/mcp/index.js +4 -0
  67. package/dist/mcp/limits.d.ts +10 -0
  68. package/dist/mcp/limits.js +30 -0
  69. package/dist/mcp/protocol.d.ts +50 -0
  70. package/dist/mcp/protocol.js +81 -0
  71. package/dist/mcp/types.d.ts +22 -0
  72. package/dist/mcp/types.js +0 -0
  73. package/dist/policy/matching.d.ts +4 -0
  74. package/dist/policy/matching.js +18 -0
  75. package/dist/policy/parse.d.ts +3 -0
  76. package/dist/policy/parse.js +13 -0
  77. package/dist/policy/resolve.d.ts +7 -0
  78. package/dist/policy/resolve.js +67 -0
  79. package/dist/policy/schemas.d.ts +5 -0
  80. package/dist/policy/schemas.js +59 -0
  81. package/dist/policy/types.d.ts +60 -0
  82. package/dist/policy/types.js +0 -0
  83. package/dist/shared/bounds.d.ts +6 -0
  84. package/dist/shared/bounds.js +88 -0
  85. package/dist/shared/errors.d.ts +9 -0
  86. package/dist/shared/errors.js +27 -0
  87. package/dist/shared/identity.d.ts +6 -0
  88. package/dist/shared/identity.js +32 -0
  89. package/dist/shared/immutable.d.ts +2 -0
  90. package/dist/shared/immutable.js +10 -0
  91. package/dist/shared/limits.d.ts +32 -0
  92. package/dist/shared/limits.js +69 -0
  93. package/dist/shared/operational-errors.d.ts +7 -0
  94. package/dist/shared/operational-errors.js +14 -0
  95. package/dist/shared/schemas.d.ts +11 -0
  96. package/dist/shared/schemas.js +23 -0
  97. package/dist/shared/text.d.ts +2 -0
  98. package/dist/shared/text.js +15 -0
  99. package/dist/store/memory.d.ts +7 -0
  100. package/dist/store/memory.js +215 -0
  101. package/dist/store/pages.d.ts +16 -0
  102. package/dist/store/pages.js +46 -0
  103. package/dist/store/parse.d.ts +5 -0
  104. package/dist/store/parse.js +137 -0
  105. package/dist/store/types.d.ts +219 -0
  106. package/dist/store/types.js +0 -0
  107. package/dist/store-adapter/admission.d.ts +6 -0
  108. package/dist/store-adapter/admission.js +90 -0
  109. package/dist/store-adapter/context.d.ts +20 -0
  110. package/dist/store-adapter/context.js +107 -0
  111. package/dist/store-adapter/delivery.d.ts +15 -0
  112. package/dist/store-adapter/delivery.js +81 -0
  113. package/dist/store-adapter/execution.d.ts +12 -0
  114. package/dist/store-adapter/execution.js +151 -0
  115. package/dist/store-adapter/index.d.ts +7 -0
  116. package/dist/store-adapter/index.js +27 -0
  117. package/dist/store-adapter/lifecycle.d.ts +15 -0
  118. package/dist/store-adapter/lifecycle.js +80 -0
  119. package/dist/store-adapter/plan.d.ts +7 -0
  120. package/dist/store-adapter/plan.js +168 -0
  121. package/dist/store-adapter/types.d.ts +45 -0
  122. package/dist/store-adapter/types.js +0 -0
  123. package/dist/tasks/failure.d.ts +4 -0
  124. package/dist/tasks/failure.js +24 -0
  125. package/dist/tasks/parse.d.ts +18 -0
  126. package/dist/tasks/parse.js +66 -0
  127. package/dist/tasks/schemas.d.ts +47 -0
  128. package/dist/tasks/schemas.js +215 -0
  129. package/dist/tasks/types.d.ts +192 -0
  130. package/dist/tasks/types.js +0 -0
  131. package/dist/tools/create.d.ts +4 -0
  132. package/dist/tools/create.js +69 -0
  133. package/dist/tools/errors.d.ts +4 -0
  134. package/dist/tools/errors.js +35 -0
  135. package/dist/tools/guidance.d.ts +4 -0
  136. package/dist/tools/guidance.js +28 -0
  137. package/dist/tools/handlers.d.ts +4 -0
  138. package/dist/tools/handlers.js +84 -0
  139. package/dist/tools/projection.d.ts +13 -0
  140. package/dist/tools/projection.js +72 -0
  141. package/dist/tools/schemas.d.ts +6 -0
  142. package/dist/tools/schemas.js +93 -0
  143. package/dist/tools/types.d.ts +28 -0
  144. package/dist/tools/types.js +0 -0
  145. package/package.json +64 -3
@@ -0,0 +1,231 @@
1
+ import { AgentProviderDelegatedOperationError } from "@agen-ai/agent-runtime";
2
+ import { parseAgentRequestResolutionFor } from "@agen-ai/agent-protocol";
3
+ import { parseOperationId, parseRequestResolution } from "../coordinator/parse.js";
4
+ import { delegationInputDigest } from "../shared/identity.js";
5
+ import { failDelegation } from "../shared/operational-errors.js";
6
+ import { assertRunDispatch, sameTarget } from "./authority.js";
7
+ import { finishRun } from "./finish.js";
8
+ import { dispatchIntent, operationIdentity, operationReceipt } from "./journal.js";
9
+ import { now } from "./state.js";
10
+ import { markRequest } from "./stream.js";
11
+ function serializeControl(live, invoke) {
12
+ const control = live.controlTail.then(invoke, invoke);
13
+ live.controlTail = control.then(() => void 0, () => void 0);
14
+ return control;
15
+ }
16
+ async function boundedControl(state, live, invoke, cancellable = true) {
17
+ const controller = new AbortController();
18
+ let timer;
19
+ const signal = cancellable ? AbortSignal.any([controller.signal, live.controlAbort.signal]) : controller.signal;
20
+ const work = state.track(Promise.resolve().then(() => invoke(signal)), live);
21
+ const timeout = new Promise((_resolve, reject) => {
22
+ timer = setTimeout(() => {
23
+ const error = new DOMException("The managed control deadline expired.", "AbortError");
24
+ controller.abort(error);
25
+ reject(error);
26
+ }, state.options.limits.controlTimeoutMs);
27
+ });
28
+ try {
29
+ return await Promise.race([work, timeout]);
30
+ } finally {
31
+ if (timer) clearTimeout(timer);
32
+ }
33
+ }
34
+ function cancelLive(state, live, reason) {
35
+ if (live.cancellation) return live.cancellation;
36
+ live.controlAbort.abort(new DOMException("The managed run's controls were canceled.", "AbortError"));
37
+ live.cancellation = serializeControl(live, async () => {
38
+ const run = await state.run(live.taskId, live.runId);
39
+ if (run.phase === "settled") return;
40
+ if (!run.cancelRequested) {
41
+ const identity2 = operationIdentity(live, "cancel");
42
+ await state.mutate(
43
+ live.taskId,
44
+ live.runId,
45
+ identity2.operationId,
46
+ identity2.inputDigest,
47
+ (fence) => ({ ...fence, kind: "cancel", at: now(), reason })
48
+ );
49
+ }
50
+ if (!live.session || run.phase === "preparing") {
51
+ live.lifetime.abort(new DOMException("Managed execution was canceled during preparation.", "AbortError"));
52
+ return;
53
+ }
54
+ const identity = operationIdentity(live, "interrupt");
55
+ await dispatchIntent(state, live, "interrupt", identity);
56
+ if (live.session.interruption.kind !== "supported") {
57
+ live.uncertain = "cancellation_uncertain";
58
+ await operationReceipt(state, live, "uncertain", identity);
59
+ live.lifetime.abort();
60
+ } else {
61
+ const interruption = live.session.interruption;
62
+ try {
63
+ const result = await boundedControl(state, live, (signal) => interruption.interruptTurn({
64
+ turnId: live.turnId,
65
+ reason: reason === "timeout" ? "timeout" : reason === "shutdown" ? "shutdown" : "user_requested",
66
+ requestedAt: now(),
67
+ signal
68
+ }), false);
69
+ await operationReceipt(state, live, ["accepted", "completed"].includes(result.status) ? "interruption_requested" : "uncertain", identity);
70
+ if (!["accepted", "completed"].includes(result.status)) live.uncertain = "cancellation_uncertain";
71
+ } catch {
72
+ live.uncertain = "cancellation_uncertain";
73
+ await operationReceipt(state, live, "uncertain", identity);
74
+ }
75
+ }
76
+ if (!live.stream) {
77
+ if (!live.terminal) {
78
+ live.uncertain = "cancellation_uncertain";
79
+ void finishRun(state, live, { kind: "recovery_required", reason: "cancellation_uncertain" }).catch(() => void 0);
80
+ }
81
+ } else if (!live.finishing) {
82
+ if (live.deadline) clearTimeout(live.deadline);
83
+ live.deadline = setTimeout(() => {
84
+ live.uncertain = "cancellation_uncertain";
85
+ live.lifetime.abort(new DOMException("Interruption did not settle the managed output producer.", "AbortError"));
86
+ }, state.options.limits.controlTimeoutMs);
87
+ }
88
+ });
89
+ return state.track(live.cancellation, live);
90
+ }
91
+ async function deliverSteering(state, live, input, digest, originPolicyRevision) {
92
+ const run = await state.run(live.taskId, live.runId);
93
+ const { task } = await state.task(live.taskId);
94
+ if (live.uncertain) failDelegation("recovery_required", "An earlier control operation has an uncertain outcome.");
95
+ if (live.cancellation || run.cancelRequested) failDelegation("unsupported_operation", "The managed turn no longer accepts steering after cancellation.");
96
+ if (!live.session || live.session.steering.kind !== "supported" || run.phase !== "running" || !live.stream || live.finishing) {
97
+ failDelegation("unsupported_operation", "This managed child does not have a steerable active turn.");
98
+ }
99
+ if (!run.appliedTarget || !sameTarget(run.appliedTarget, input.target)) failDelegation("stale_target", "Steering must preserve the exact applied target.");
100
+ const identity = { operationId: input.operationId, inputDigest: digest };
101
+ await dispatchIntent(state, live, "steer", identity);
102
+ const steering = live.session.steering;
103
+ let status;
104
+ try {
105
+ const result = await boundedControl(state, live, (signal) => {
106
+ if (live.uncertain || live.cancellation) failDelegation("recovery_required", "The managed turn no longer accepts new execution input.");
107
+ state.options.host.assertControl({ scope: live.scope, task });
108
+ assertRunDispatch(state, live, input.expectedPolicyRevision, originPolicyRevision);
109
+ return steering.steerTurn({ turnId: live.turnId, ...input.input, signal });
110
+ });
111
+ status = result.status;
112
+ } catch (error) {
113
+ status = error instanceof AgentProviderDelegatedOperationError || error instanceof DOMException && error.name === "AbortError" ? "delivery_uncertain" : "rejected";
114
+ }
115
+ if (status === "delivery_uncertain") live.uncertain = "steering_uncertain";
116
+ await operationReceipt(state, live, status === "delivery_uncertain" ? "uncertain" : status, identity);
117
+ return { status, taskId: live.taskId, runId: live.runId, operationId: input.operationId, duplicate: false };
118
+ }
119
+ function steerRun(state, live, input, digest, originPolicyRevision) {
120
+ const prior = live.pendingOperations.get(input.operationId);
121
+ if (prior) {
122
+ if (prior.digest !== digest) failDelegation("operation_conflict", "The operation identity has different in-flight input.");
123
+ return prior.promise.then((receipt) => ({ ...receipt, duplicate: true }));
124
+ }
125
+ const work = state.track(serializeControl(live, () => deliverSteering(state, live, input, digest, originPolicyRevision)), live);
126
+ live.pendingOperations.set(input.operationId, { digest, promise: work });
127
+ void work.finally(() => live.pendingOperations.delete(input.operationId)).catch(() => void 0);
128
+ return work;
129
+ }
130
+ function requestReceipt(live, operationId, disposition, requestState, duplicate = false) {
131
+ const identity = { taskId: live.taskId, runId: live.runId, operationId, duplicate };
132
+ if (disposition === "delivered") return { ...identity, status: "delivered" };
133
+ if (disposition === "rejected" && requestState) return { ...identity, status: "rejected", requestState };
134
+ return { ...identity, status: "delivery_uncertain" };
135
+ }
136
+ async function continueRequest(state, input) {
137
+ const operationId = parseOperationId(input.operationId);
138
+ const resolution = parseRequestResolution(input.resolution);
139
+ const pending = state.requests.get(input.handle);
140
+ if (!pending) failDelegation("request_unavailable", "The live host request handle is unavailable.");
141
+ const { live, request } = pending;
142
+ parseAgentRequestResolutionFor(request, resolution);
143
+ const identity = { operationId, inputDigest: delegationInputDigest({ kind: "request_resolution", taskId: live.taskId, runId: live.runId, requestId: request.requestId, resolution }) };
144
+ return serializeControl(live, async () => {
145
+ const [{ task }, run] = await Promise.all([state.task(live.taskId), state.run(live.taskId, live.runId)]);
146
+ state.options.host.assertResultAccess({ scope: live.scope, task });
147
+ state.options.host.assertRequestAccess({ handle: input.handle, task, run, request });
148
+ live.execution?.assertCurrent();
149
+ const existing = await state.options.store.read({ kind: "operation", parent: live.scope, operationId });
150
+ if (existing.kind === "operation" && existing.operation) {
151
+ if (existing.operation.kind !== "request_resolution" || existing.operation.inputDigest !== identity.inputDigest || existing.operation.taskId !== live.taskId || existing.operation.runId !== live.runId) {
152
+ failDelegation("operation_conflict", "The operation identity already has different input or ownership.");
153
+ }
154
+ return requestReceipt(live, operationId, existing.operation.disposition, existing.operation.requestState, true);
155
+ }
156
+ if (live.request !== pending || !live.stream || live.finishing || !live.turn || !live.requestHistory || live.uncertain || live.cancellation || run.phase !== "waiting" || live.execution?.signal.aborted || live.lifetime.signal.aborted) {
157
+ failDelegation("request_unavailable", "The exact waiting request no longer has a live executor.");
158
+ }
159
+ const history = live.requestHistory;
160
+ await markRequest(state, live, "resolution_intended", { ...history, state: "resolution_intended" }, identity);
161
+ live.request = void 0;
162
+ let disposition;
163
+ let requestState;
164
+ let localFailure;
165
+ let entered = false;
166
+ try {
167
+ const turn = live.turn;
168
+ const result = await boundedControl(state, live, (signal) => turn.resolveRequest({
169
+ resolution,
170
+ signal: AbortSignal.any([signal, live.lifetime.signal]),
171
+ onProviderExecutionStarted() {
172
+ state.options.host.assertRequestAccess({ handle: input.handle, task, run, request });
173
+ live.execution?.assertCurrent();
174
+ entered = true;
175
+ }
176
+ }));
177
+ disposition = result.status === "delivery_uncertain" ? "uncertain" : result.status;
178
+ if (result.status === "rejected") requestState = result.requestState;
179
+ } catch (error) {
180
+ if (error instanceof AgentProviderDelegatedOperationError || entered && error instanceof DOMException && error.name === "AbortError") {
181
+ disposition = "uncertain";
182
+ } else {
183
+ disposition = "rejected";
184
+ requestState = live.lifetime.signal.aborted || live.cancellation ? "unavailable" : "pending";
185
+ localFailure = { error };
186
+ }
187
+ }
188
+ if (disposition === "uncertain") live.uncertain = "request_executor_lost";
189
+ try {
190
+ await state.mutate(
191
+ live.taskId,
192
+ live.runId,
193
+ operationId,
194
+ identity.inputDigest,
195
+ (fence) => ({ ...fence, kind: "operation_receipt", disposition, at: now(), ...requestState ? { requestState } : {} })
196
+ );
197
+ } catch (error) {
198
+ live.uncertain = "storage_failed";
199
+ void finishRun(state, live, { kind: "recovery_required", reason: "storage_failed" }).catch(() => void 0);
200
+ throw error;
201
+ }
202
+ if (requestState === "pending" && !live.lifetime.signal.aborted && !live.cancellation) live.request = pending;
203
+ if (live.uncertain) void finishRun(state, live, { kind: "recovery_required", reason: live.uncertain }).catch(() => void 0);
204
+ if (localFailure) throw localFailure.error;
205
+ return requestReceipt(live, operationId, disposition, requestState);
206
+ });
207
+ }
208
+ function resolveLiveRequest(state, input) {
209
+ const pending = state.requests.get(input.handle);
210
+ if (!pending) return state.track(continueRequest(state, input));
211
+ const operationId = parseOperationId(input.operationId);
212
+ const resolution = parseRequestResolution(input.resolution);
213
+ parseAgentRequestResolutionFor(pending.request, resolution);
214
+ const digest = delegationInputDigest({ kind: "request_resolution", taskId: pending.live.taskId, runId: pending.live.runId, requestId: pending.request.requestId, resolution });
215
+ const prior = pending.live.pendingOperations.get(operationId);
216
+ if (prior && prior.digest !== digest) failDelegation("operation_conflict", "The operation identity has different in-flight input.");
217
+ if (prior) return state.track(prior.promise.then((receipt) => ({ ...receipt, duplicate: true })), pending.live);
218
+ const work = state.track(continueRequest(state, input), pending.live);
219
+ pending.live.pendingOperations.set(operationId, { digest, promise: work });
220
+ pending.live.requestResolution = work;
221
+ void work.finally(() => {
222
+ pending.live.pendingOperations.delete(operationId);
223
+ if (pending.live.requestResolution === work) pending.live.requestResolution = void 0;
224
+ }).catch(() => void 0);
225
+ return work;
226
+ }
227
+ export {
228
+ cancelLive,
229
+ resolveLiveRequest,
230
+ steerRun
231
+ };
@@ -0,0 +1,6 @@
1
+ import type { DelegationRunOutcome } from "../tasks/types.js";
2
+ import { type CoordinatorState, type LiveRun } from "./state.js";
3
+ export declare function settleRun(state: CoordinatorState, live: Pick<LiveRun, "taskId" | "runId">, outcome: DelegationRunOutcome): Promise<void>;
4
+ export declare function settleQueuedRun(state: CoordinatorState, live: Pick<LiveRun, "taskId" | "runId">, outcome: DelegationRunOutcome): Promise<void>;
5
+ export declare function finishRun(state: CoordinatorState, live: LiveRun, outcome: DelegationRunOutcome): Promise<void>;
6
+ //# sourceMappingURL=finish.d.ts.map
@@ -0,0 +1,49 @@
1
+ import { delegationInputDigest, delegationSubordinateId } from "../shared/identity.js";
2
+ import { cleanupRun } from "./cleanup.js";
3
+ import { now } from "./state.js";
4
+ async function settle(state, live, outcome, queuedOnly) {
5
+ const identity = { operationId: delegationSubordinateId(live.runId, "settle"), inputDigest: delegationInputDigest({ runId: live.runId, outcome }) };
6
+ await state.mutate(
7
+ live.taskId,
8
+ live.runId,
9
+ identity.operationId,
10
+ identity.inputDigest,
11
+ (fence, _task, run) => queuedOnly && run.phase !== "queued" ? void 0 : { ...fence, kind: "settle", outcome, at: now() }
12
+ );
13
+ }
14
+ function settleRun(state, live, outcome) {
15
+ return settle(state, live, outcome, false);
16
+ }
17
+ function settleQueuedRun(state, live, outcome) {
18
+ return settle(state, live, outcome, true);
19
+ }
20
+ function finishRun(state, live, outcome) {
21
+ if (live.finishing) return live.finishing;
22
+ live.finishing = (async () => {
23
+ const closed = await cleanupRun(state, live);
24
+ const truth = outcome.kind === "succeeded" ? {
25
+ kind: "succeeded",
26
+ result: { text: live.summary, truncated: live.truncated, references: [...live.references] }
27
+ } : outcome;
28
+ const sticky = live.uncertain && !(live.uncertain === "cancellation_uncertain" && (truth.kind === "succeeded" || truth.kind === "canceled"));
29
+ const final = sticky ? { kind: "recovery_required", reason: live.uncertain } : !closed ? { kind: "recovery_required", reason: "cancellation_uncertain" } : truth;
30
+ try {
31
+ await settleRun(state, live, final);
32
+ if (closed) {
33
+ if ((final.kind === "succeeded" || final.kind === "failed" || final.kind === "canceled") && live.session && live.resources?.continuation?.kind === "resume") {
34
+ state.continuations.set(live.taskId, { runId: live.runId, binding: live.session.binding, target: live.target });
35
+ } else state.continuations.delete(live.taskId);
36
+ state.live.delete(live.runId);
37
+ }
38
+ } finally {
39
+ state.changed();
40
+ live.schedule?.();
41
+ }
42
+ })();
43
+ return state.track(live.finishing);
44
+ }
45
+ export {
46
+ finishRun,
47
+ settleQueuedRun,
48
+ settleRun
49
+ };
@@ -0,0 +1,19 @@
1
+ import type { DelegationTargetReference } from "../catalog/types.js";
2
+ import type { DelegationOperationDisposition, DelegationOperationKind } from "../store/types.js";
3
+ import { type CoordinatorState, type LiveRun } from "./state.js";
4
+ export declare function operationIdentity(live: LiveRun, kind: string): {
5
+ operationId: string;
6
+ inputDigest: string;
7
+ };
8
+ export declare function dispatchIntent(state: CoordinatorState, live: LiveRun, kind: Exclude<DelegationOperationKind, "admit" | "cancel">, identity?: {
9
+ operationId: string;
10
+ inputDigest: string;
11
+ }, extra?: Readonly<{
12
+ appliedTarget?: DelegationTargetReference;
13
+ attempt?: 1 | 2;
14
+ }>): Promise<import("../index.js").DelegationCommitReceipt | undefined>;
15
+ export declare function operationReceipt(state: CoordinatorState, live: LiveRun, disposition: Exclude<DelegationOperationDisposition, "intent">, identity: Readonly<{
16
+ operationId: string;
17
+ inputDigest: string;
18
+ }>): Promise<import("../index.js").DelegationCommitReceipt | undefined>;
19
+ //# sourceMappingURL=journal.d.ts.map
@@ -0,0 +1,28 @@
1
+ import { delegationInputDigest, delegationSubordinateId } from "../shared/identity.js";
2
+ import { now } from "./state.js";
3
+ function operationIdentity(live, kind) {
4
+ return { operationId: delegationSubordinateId(live.runId, kind), inputDigest: delegationInputDigest({ runId: live.runId, kind }) };
5
+ }
6
+ async function dispatchIntent(state, live, kind, identity = operationIdentity(live, kind), extra = {}) {
7
+ return state.mutate(
8
+ live.taskId,
9
+ live.runId,
10
+ identity.operationId,
11
+ identity.inputDigest,
12
+ (fence, _task, run) => kind === "prepare" && run.phase !== "queued" ? void 0 : { ...fence, kind: "dispatch_intent", operationKind: kind, at: now(), ...extra }
13
+ );
14
+ }
15
+ async function operationReceipt(state, live, disposition, identity) {
16
+ return state.mutate(
17
+ live.taskId,
18
+ live.runId,
19
+ identity.operationId,
20
+ identity.inputDigest,
21
+ (fence) => ({ ...fence, kind: "operation_receipt", disposition, at: now() })
22
+ );
23
+ }
24
+ export {
25
+ dispatchIntent,
26
+ operationIdentity,
27
+ operationReceipt
28
+ };
@@ -0,0 +1,3 @@
1
+ import type { CoordinatorState, LiveRun } from "./state.js";
2
+ export declare function prepareRun(state: CoordinatorState, live: LiveRun, revoke: () => void): Promise<void>;
3
+ //# sourceMappingURL=prepare.d.ts.map
@@ -0,0 +1,153 @@
1
+ import { parseAgentConfigurationSelectionFor } from "@agen-ai/agent-protocol";
2
+ import { isAgentProviderSessionCleanupErrorFor } from "@agen-ai/agent-runtime";
3
+ import { delegationCanonicalJson } from "../shared/identity.js";
4
+ import { failDelegation } from "../shared/operational-errors.js";
5
+ import { assertRunDispatch, sameTarget } from "./authority.js";
6
+ import { finishRun } from "./finish.js";
7
+ import { dispatchIntent, operationIdentity, operationReceipt } from "./journal.js";
8
+ import { consumeTurn } from "./stream.js";
9
+ async function prepareRun(state, live, revoke) {
10
+ const run = await state.run(live.taskId, live.runId);
11
+ const { task } = await state.task(live.taskId);
12
+ const prepare = operationIdentity(live, "prepare");
13
+ const gate = () => assertRunDispatch(state, live, run.admittedPolicyRevision, run.admittedOriginPolicyRevision);
14
+ let preparationCompleted = false;
15
+ let openingInput;
16
+ try {
17
+ if (run.cancelRequested) failDelegation("recovery_required", "The run was canceled before preparation acquired execution authority.");
18
+ gate();
19
+ live.execution = await state.options.host.acquireExecution({
20
+ scope: live.scope,
21
+ task,
22
+ run,
23
+ target: live.target,
24
+ signal: live.lifetime.signal
25
+ });
26
+ const revoked = () => {
27
+ revoke();
28
+ };
29
+ live.execution.signal.addEventListener("abort", revoked, { once: true });
30
+ live.removeRevocation = () => live.execution?.signal.removeEventListener("abort", revoked);
31
+ if (live.execution.signal.aborted) revoke();
32
+ gate();
33
+ const continuation = run.previousRunId ? state.continuations.get(live.taskId) : void 0;
34
+ if (run.previousRunId && (!continuation || continuation.runId !== run.previousRunId)) {
35
+ failDelegation("unsupported_operation", "The exact previous conversation is unavailable for continuation.");
36
+ }
37
+ live.resources = await state.options.host.acquireChildResources({
38
+ scope: live.scope,
39
+ task,
40
+ run,
41
+ target: live.target,
42
+ ...continuation ? { continuation } : {},
43
+ signal: live.lifetime.signal
44
+ });
45
+ gate();
46
+ if (continuation && (live.resources.continuation?.kind !== "resume" || !sameTarget(continuation.target.reference, live.target.reference) && !live.resources.continuation.allowTargetChange || continuation.target.instanceId !== live.target.instanceId || continuation.target.providerKey !== live.target.providerKey)) {
47
+ failDelegation("unsupported_operation", "The host has not authorized this exact conversation continuation.");
48
+ }
49
+ const instance = state.options.host.registry.requireInstance(live.target.instanceId);
50
+ if (instance.capabilities.providerKey !== live.target.providerKey) failDelegation("stale_target", "The registered provider does not match the admitted target.");
51
+ const readiness = await state.options.host.registry.checkReadiness(live.target.instanceId, { signal: live.lifetime.signal });
52
+ if (readiness.status !== "ready") failDelegation("target_unavailable", "The selected provider is not ready.");
53
+ gate();
54
+ if (state.options.host.registry.requireInstance(live.target.instanceId) !== instance) failDelegation("target_unavailable", "The prepared provider instance is no longer registered.");
55
+ const context = {
56
+ sessionId: live.sessionId,
57
+ workingDirectory: live.resources.workingDirectory.path,
58
+ configuration: live.target.application.configuration,
59
+ signal: live.lifetime.signal,
60
+ ...live.resources.hostBinding ? { hostBinding: live.resources.hostBinding } : {}
61
+ };
62
+ if (continuation) {
63
+ if (instance.adapter.resumption.kind !== "supported") failDelegation("unsupported_operation", "The registered provider cannot resume this conversation.");
64
+ const input = { ...context, binding: continuation.binding };
65
+ openingInput = input;
66
+ live.session = await instance.adapter.resumption.resumeSession(input);
67
+ } else {
68
+ const input = { ...context, onBindingCreated: () => void 0 };
69
+ openingInput = input;
70
+ live.session = await instance.adapter.createSession(input);
71
+ }
72
+ gate();
73
+ const configuration = live.session.configuration;
74
+ let observedConfiguration;
75
+ if (configuration.kind === "selectable") observedConfiguration = await configuration.listConfiguration({ signal: live.lifetime.signal });
76
+ for (const [index, selection] of live.target.application.beforeFirstTurn.entries()) {
77
+ if (configuration.kind !== "selectable" || !observedConfiguration) failDelegation("unsupported_operation", "The admitted configuration is not selectable on this session.");
78
+ parseAgentConfigurationSelectionFor(observedConfiguration, selection);
79
+ const field = observedConfiguration.fields.find((entry) => entry.key === selection.key);
80
+ if (!field?.mutable || field.scope !== "session" || field.applicationTiming !== "immediate") {
81
+ failDelegation("unsupported_operation", "Managed child configuration must be immediate and local to its owned session.");
82
+ }
83
+ const identity = operationIdentity(live, `configure:${index}`);
84
+ await dispatchIntent(state, live, "configure", identity);
85
+ gate();
86
+ let invoked = false;
87
+ try {
88
+ const result = await configuration.applyConfigurationSelection({
89
+ selection,
90
+ signal: live.lifetime.signal,
91
+ onProviderExecutionStarted() {
92
+ gate();
93
+ invoked = true;
94
+ }
95
+ });
96
+ await operationReceipt(state, live, result.status === "completed" ? "completed" : "uncertain", identity);
97
+ if (result.status !== "completed") failDelegation("recovery_required", "Configuration application did not complete definitively.");
98
+ } catch (error) {
99
+ if (invoked) live.uncertain = "dispatch_uncertain";
100
+ else await operationReceipt(state, live, "rejected", identity);
101
+ throw error;
102
+ }
103
+ gate();
104
+ observedConfiguration = await configuration.listConfiguration({ signal: live.lifetime.signal });
105
+ }
106
+ gate();
107
+ const applied = await state.options.host.verifyAppliedConfiguration({
108
+ task,
109
+ run,
110
+ target: live.target,
111
+ session: live.session,
112
+ ...observedConfiguration ? { observedConfiguration } : {},
113
+ signal: live.lifetime.signal
114
+ });
115
+ if (!sameTarget(applied.target, live.target.reference) || delegationCanonicalJson(applied.configuration) !== delegationCanonicalJson(live.target.application.configuration)) {
116
+ failDelegation("stale_target", "The host did not attest the exact admitted configuration and source.");
117
+ }
118
+ gate();
119
+ await operationReceipt(state, live, "completed", prepare);
120
+ preparationCompleted = true;
121
+ await dispatchIntent(state, live, "turn", operationIdentity(live, "turn"), { appliedTarget: live.target.reference });
122
+ gate();
123
+ live.turn = live.session.startTurn({
124
+ turnId: live.turnId,
125
+ interactionMode: "default",
126
+ ...run.input,
127
+ deadlineAt: run.deadlineAt,
128
+ signal: live.lifetime.signal,
129
+ onProviderExecutionStarted() {
130
+ gate();
131
+ live.observedStart = true;
132
+ }
133
+ });
134
+ const stream = consumeTurn(state, live, live.turn.outputs);
135
+ live.stream = stream;
136
+ await stream;
137
+ } catch (error) {
138
+ if (openingInput && isAgentProviderSessionCleanupErrorFor(error, openingInput)) {
139
+ live.rejectedOpening = error;
140
+ }
141
+ if (!preparationCompleted) {
142
+ try {
143
+ await operationReceipt(state, live, live.rejectedOpening || live.uncertain ? "uncertain" : "rejected", prepare);
144
+ } catch {
145
+ live.uncertain = "storage_failed";
146
+ }
147
+ }
148
+ await finishRun(state, live, live.observedStart || live.rejectedOpening || live.uncertain ? { kind: "recovery_required", reason: live.uncertain ?? "dispatch_uncertain" } : { kind: "failed", error: { category: "unknown", code: "preparation_rejected", message: "The managed child could not start under its admitted authority.", recovery: { kind: "unknown" } } });
149
+ }
150
+ }
151
+ export {
152
+ prepareRun
153
+ };
@@ -0,0 +1,4 @@
1
+ import { type CoordinatorState } from "./state.js";
2
+ export declare function schedule(state: CoordinatorState): void;
3
+ export declare function assertAccepting(state: CoordinatorState): void;
4
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1,136 @@
1
+ import { parseAgentSessionId, parseAgentTurnId } from "@agen-ai/agent-protocol";
2
+ import { resolveDelegationPolicy } from "../policy/resolve.js";
3
+ import { assertCompletionPolicy } from "../delivery/setup.js";
4
+ import { createDelegationId } from "../shared/identity.js";
5
+ import { DelegationOperationError, failDelegation } from "../shared/operational-errors.js";
6
+ import { assertRunDispatch, readCeiling, sameTarget } from "./authority.js";
7
+ import { cancelLive } from "./control.js";
8
+ import { settleQueuedRun } from "./finish.js";
9
+ import { dispatchIntent } from "./journal.js";
10
+ import { prepareRun } from "./prepare.js";
11
+ import { now } from "./state.js";
12
+ async function pump(state) {
13
+ if (state.released) return;
14
+ if (state.queueTimer) {
15
+ clearTimeout(state.queueTimer);
16
+ state.queueTimer = void 0;
17
+ }
18
+ let earliest = Infinity;
19
+ let cursor;
20
+ do {
21
+ const page = await state.options.store.scan({
22
+ kind: "queued",
23
+ at: now(),
24
+ limit: state.options.limits.maxPageRecords,
25
+ ...cursor ? { cursor } : {}
26
+ });
27
+ if (page.kind !== "queued") return;
28
+ for (const run of page.page.items) {
29
+ if (state.closing && !state.draining || state.failed || state.released) return;
30
+ const expiry = Math.min(Date.parse(run.queuedStartDeadlineAt), Date.parse(run.deadlineAt));
31
+ if (expiry <= Date.now()) {
32
+ await settleQueuedRun(state, run, { kind: "failed", error: { category: "unknown", code: "queued_deadline_expired", message: "The managed task expired before its execution slot became available.", recovery: { kind: "unknown" } } });
33
+ continue;
34
+ }
35
+ if (run.previousRunId) {
36
+ const predecessor = await state.run(run.taskId, run.previousRunId);
37
+ if (predecessor.phase === "settled" && (predecessor.outcome?.kind === "recovery_required" || run.continuationMode === "next_turn" && (predecessor.cancelRequested || predecessor.outcome?.kind === "canceled"))) {
38
+ await settleQueuedRun(state, run, { kind: "failed", error: { category: "unknown", code: "continuation_rejected", message: "The previous run cannot authorize this queued automatic continuation.", recovery: { kind: "unknown" } } });
39
+ continue;
40
+ }
41
+ if (predecessor.phase !== "settled") {
42
+ earliest = Math.min(earliest, expiry);
43
+ continue;
44
+ }
45
+ }
46
+ if (state.live.size >= state.options.limits.maxActiveRuns || [...state.live.values()].some((live2) => live2.taskId === run.taskId)) {
47
+ earliest = Math.min(earliest, expiry);
48
+ continue;
49
+ }
50
+ const { task } = await state.task(run.taskId);
51
+ assertCompletionPolicy(state, task.completionPolicy);
52
+ const ceiling = await readCeiling(state, task.policyCeilingRef);
53
+ const snapshot = state.options.host.currentPolicy(task.parent);
54
+ const resolved = resolveDelegationPolicy({ catalog: snapshot.catalog, policy: snapshot.policy });
55
+ const target = resolved.allowedTargets.find((candidate) => sameTarget(candidate.reference, run.admittedTarget));
56
+ if (!target || Date.parse(run.queuedStartDeadlineAt) <= Date.now() || Date.parse(run.deadlineAt) <= Date.now()) {
57
+ await settleQueuedRun(state, run, { kind: "failed", error: { category: "unknown", code: "admission_expired", message: "Current authority or the queued start deadline no longer permits this task.", recovery: { kind: "unknown" } } });
58
+ continue;
59
+ }
60
+ const live = {
61
+ taskId: run.taskId,
62
+ runId: run.runId,
63
+ scope: task.parent,
64
+ target,
65
+ ceiling,
66
+ lifetime: new AbortController(),
67
+ controlAbort: new AbortController(),
68
+ sessionId: parseAgentSessionId(createDelegationId()),
69
+ turnId: parseAgentTurnId(createDelegationId()),
70
+ controls: /* @__PURE__ */ new Set(),
71
+ pendingOperations: /* @__PURE__ */ new Map(),
72
+ observedStart: false,
73
+ recordedStart: false,
74
+ controlTail: Promise.resolve(),
75
+ summary: "",
76
+ truncated: false,
77
+ references: [],
78
+ runtimeCleanupAttempts: 0,
79
+ resourceReleaseAttempts: 0,
80
+ executionReleaseAttempts: 0,
81
+ runtimeClosed: false,
82
+ resourcesReleased: false,
83
+ executionReleased: false,
84
+ schedule: () => schedule(state)
85
+ };
86
+ try {
87
+ assertRunDispatch(state, live, run.admittedPolicyRevision, run.admittedOriginPolicyRevision);
88
+ if (!await dispatchIntent(state, live, "prepare")) continue;
89
+ } catch (error) {
90
+ await settleQueuedRun(state, run, { kind: "failed", error: { category: "unknown", code: error instanceof DelegationOperationError ? error.code : "admission_rejected", message: "The queued admission is no longer current.", recovery: { kind: "unknown" } } });
91
+ continue;
92
+ }
93
+ state.live.set(live.runId, live);
94
+ const revoke = () => {
95
+ void cancelLive(state, live, "authority_revoked").catch(() => void 0);
96
+ };
97
+ live.deadline = setTimeout(() => {
98
+ void cancelLive(state, live, "timeout").catch(() => void 0);
99
+ }, Math.max(1, Date.parse(run.deadlineAt) - Date.now()));
100
+ live.ready = prepareRun(state, live, revoke);
101
+ state.track(live.ready);
102
+ void live.ready.catch(() => state.fail());
103
+ }
104
+ cursor = page.page.cursor;
105
+ } while (cursor && (!state.closing || state.draining) && !state.released);
106
+ if (Number.isFinite(earliest) && (!state.closing || state.draining) && !state.failed && !state.released) {
107
+ state.queueTimer = setTimeout(() => {
108
+ state.queueTimer = void 0;
109
+ schedule(state);
110
+ }, Math.max(1, earliest - Date.now()));
111
+ }
112
+ }
113
+ function schedule(state) {
114
+ if (state.closing && !state.draining || state.failed || state.released) return;
115
+ if (state.pump) {
116
+ state.schedulePending = true;
117
+ return;
118
+ }
119
+ const work = Promise.resolve().then(() => pump(state)).catch(() => state.fail()).finally(() => {
120
+ state.pump = void 0;
121
+ state.changed();
122
+ if (state.schedulePending) {
123
+ state.schedulePending = false;
124
+ schedule(state);
125
+ }
126
+ });
127
+ state.pump = work;
128
+ state.track(work);
129
+ }
130
+ function assertAccepting(state) {
131
+ if (state.closing || state.failed || state.released) failDelegation("owner_lost", "The coordinator no longer admits new managed work.");
132
+ }
133
+ export {
134
+ assertAccepting,
135
+ schedule
136
+ };