@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.
- package/LICENSE +21 -0
- package/README.md +415 -2
- package/dist/catalog/parse.d.ts +3 -0
- package/dist/catalog/parse.js +13 -0
- package/dist/catalog/schemas.d.ts +8 -0
- package/dist/catalog/schemas.js +78 -0
- package/dist/catalog/types.d.ts +53 -0
- package/dist/catalog/types.js +0 -0
- package/dist/coordinator/admission.d.ts +7 -0
- package/dist/coordinator/admission.js +189 -0
- package/dist/coordinator/cancel.d.ts +7 -0
- package/dist/coordinator/cancel.js +111 -0
- package/dist/coordinator/create.d.ts +3 -0
- package/dist/coordinator/create.js +87 -0
- package/dist/coordinator/parse.d.ts +11 -0
- package/dist/coordinator/parse.js +60 -0
- package/dist/coordinator/read.d.ts +4 -0
- package/dist/coordinator/read.js +267 -0
- package/dist/coordinator/recovery.d.ts +4 -0
- package/dist/coordinator/recovery.js +24 -0
- package/dist/coordinator/shutdown.d.ts +7 -0
- package/dist/coordinator/shutdown.js +66 -0
- package/dist/coordinator/types.d.ts +154 -0
- package/dist/coordinator/types.js +0 -0
- package/dist/delivery/calls.d.ts +4 -0
- package/dist/delivery/calls.js +34 -0
- package/dist/delivery/journal.d.ts +7 -0
- package/dist/delivery/journal.js +57 -0
- package/dist/delivery/parse.d.ts +4 -0
- package/dist/delivery/parse.js +31 -0
- package/dist/delivery/recovery.d.ts +3 -0
- package/dist/delivery/recovery.js +18 -0
- package/dist/delivery/scheduler.d.ts +3 -0
- package/dist/delivery/scheduler.js +123 -0
- package/dist/delivery/setup.d.ts +7 -0
- package/dist/delivery/setup.js +37 -0
- package/dist/delivery/types.d.ts +36 -0
- package/dist/delivery/types.js +0 -0
- package/dist/execution/authority.d.ts +16 -0
- package/dist/execution/authority.js +86 -0
- package/dist/execution/cleanup.d.ts +3 -0
- package/dist/execution/cleanup.js +75 -0
- package/dist/execution/control.d.ts +6 -0
- package/dist/execution/control.js +231 -0
- package/dist/execution/finish.d.ts +6 -0
- package/dist/execution/finish.js +49 -0
- package/dist/execution/journal.d.ts +19 -0
- package/dist/execution/journal.js +28 -0
- package/dist/execution/prepare.d.ts +3 -0
- package/dist/execution/prepare.js +153 -0
- package/dist/execution/scheduler.d.ts +4 -0
- package/dist/execution/scheduler.js +136 -0
- package/dist/execution/state.d.ts +107 -0
- package/dist/execution/state.js +118 -0
- package/dist/execution/stream.d.ts +9 -0
- package/dist/execution/stream.js +108 -0
- package/dist/host/types.d.ts +95 -0
- package/dist/host/types.js +0 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +58 -0
- package/dist/mcp/bridge.d.ts +4 -0
- package/dist/mcp/bridge.js +222 -0
- package/dist/mcp/http.d.ts +11 -0
- package/dist/mcp/http.js +73 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.js +4 -0
- package/dist/mcp/limits.d.ts +10 -0
- package/dist/mcp/limits.js +30 -0
- package/dist/mcp/protocol.d.ts +50 -0
- package/dist/mcp/protocol.js +81 -0
- package/dist/mcp/types.d.ts +22 -0
- package/dist/mcp/types.js +0 -0
- package/dist/policy/matching.d.ts +4 -0
- package/dist/policy/matching.js +18 -0
- package/dist/policy/parse.d.ts +3 -0
- package/dist/policy/parse.js +13 -0
- package/dist/policy/resolve.d.ts +7 -0
- package/dist/policy/resolve.js +67 -0
- package/dist/policy/schemas.d.ts +5 -0
- package/dist/policy/schemas.js +59 -0
- package/dist/policy/types.d.ts +60 -0
- package/dist/policy/types.js +0 -0
- package/dist/shared/bounds.d.ts +6 -0
- package/dist/shared/bounds.js +88 -0
- package/dist/shared/errors.d.ts +9 -0
- package/dist/shared/errors.js +27 -0
- package/dist/shared/identity.d.ts +6 -0
- package/dist/shared/identity.js +32 -0
- package/dist/shared/immutable.d.ts +2 -0
- package/dist/shared/immutable.js +10 -0
- package/dist/shared/limits.d.ts +32 -0
- package/dist/shared/limits.js +69 -0
- package/dist/shared/operational-errors.d.ts +7 -0
- package/dist/shared/operational-errors.js +14 -0
- package/dist/shared/schemas.d.ts +11 -0
- package/dist/shared/schemas.js +23 -0
- package/dist/shared/text.d.ts +2 -0
- package/dist/shared/text.js +15 -0
- package/dist/store/memory.d.ts +7 -0
- package/dist/store/memory.js +215 -0
- package/dist/store/pages.d.ts +16 -0
- package/dist/store/pages.js +46 -0
- package/dist/store/parse.d.ts +5 -0
- package/dist/store/parse.js +137 -0
- package/dist/store/types.d.ts +219 -0
- package/dist/store/types.js +0 -0
- package/dist/store-adapter/admission.d.ts +6 -0
- package/dist/store-adapter/admission.js +90 -0
- package/dist/store-adapter/context.d.ts +20 -0
- package/dist/store-adapter/context.js +107 -0
- package/dist/store-adapter/delivery.d.ts +15 -0
- package/dist/store-adapter/delivery.js +81 -0
- package/dist/store-adapter/execution.d.ts +12 -0
- package/dist/store-adapter/execution.js +151 -0
- package/dist/store-adapter/index.d.ts +7 -0
- package/dist/store-adapter/index.js +27 -0
- package/dist/store-adapter/lifecycle.d.ts +15 -0
- package/dist/store-adapter/lifecycle.js +80 -0
- package/dist/store-adapter/plan.d.ts +7 -0
- package/dist/store-adapter/plan.js +168 -0
- package/dist/store-adapter/types.d.ts +45 -0
- package/dist/store-adapter/types.js +0 -0
- package/dist/tasks/failure.d.ts +4 -0
- package/dist/tasks/failure.js +24 -0
- package/dist/tasks/parse.d.ts +18 -0
- package/dist/tasks/parse.js +66 -0
- package/dist/tasks/schemas.d.ts +47 -0
- package/dist/tasks/schemas.js +215 -0
- package/dist/tasks/types.d.ts +192 -0
- package/dist/tasks/types.js +0 -0
- package/dist/tools/create.d.ts +4 -0
- package/dist/tools/create.js +69 -0
- package/dist/tools/errors.d.ts +4 -0
- package/dist/tools/errors.js +35 -0
- package/dist/tools/guidance.d.ts +4 -0
- package/dist/tools/guidance.js +28 -0
- package/dist/tools/handlers.d.ts +4 -0
- package/dist/tools/handlers.js +84 -0
- package/dist/tools/projection.d.ts +13 -0
- package/dist/tools/projection.js +72 -0
- package/dist/tools/schemas.d.ts +6 -0
- package/dist/tools/schemas.js +93 -0
- package/dist/tools/types.d.ts +28 -0
- package/dist/tools/types.js +0 -0
- package/package.json +64 -3
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import { copyBoundedDelegationData } from "../shared/bounds.js";
|
|
3
|
+
import { delegationSchemaError } from "../shared/errors.js";
|
|
4
|
+
import { delegationDataBytes } from "../shared/identity.js";
|
|
5
|
+
import { freezeDelegationData } from "../shared/immutable.js";
|
|
6
|
+
import { AtSchema, OpaqueIdSchema } from "../tasks/schemas.js";
|
|
7
|
+
const accepted = z.strictObject({ status: z.literal("accepted"), inboxId: OpaqueIdSchema, acceptedAt: AtSchema });
|
|
8
|
+
const rejected = z.strictObject({ status: z.literal("rejected"), reason: z.enum(["revoked", "parent_retired", "expired", "dismissed"]) });
|
|
9
|
+
const acceptance = z.discriminatedUnion("status", [
|
|
10
|
+
accepted,
|
|
11
|
+
rejected,
|
|
12
|
+
z.strictObject({ status: z.literal("blocked"), reason: z.enum(["busy", "authority_unavailable", "capacity"]), retryAt: AtSchema }),
|
|
13
|
+
z.strictObject({ status: z.literal("uncertain") })
|
|
14
|
+
]).refine((value) => delegationDataBytes(value) <= 2048, "Inbox result exceeds its bounded receipt.");
|
|
15
|
+
const inspection = z.discriminatedUnion("status", [
|
|
16
|
+
accepted,
|
|
17
|
+
rejected,
|
|
18
|
+
z.strictObject({ status: z.literal("absent") })
|
|
19
|
+
]).refine((value) => delegationDataBytes(value) <= 2048, "Inbox inspection exceeds its bounded receipt.");
|
|
20
|
+
function parse(schema, input) {
|
|
21
|
+
const captured = copyBoundedDelegationData(input);
|
|
22
|
+
const result = schema.safeParse(captured);
|
|
23
|
+
if (!result.success) return delegationSchemaError(result.error, captured);
|
|
24
|
+
return freezeDelegationData(result.data);
|
|
25
|
+
}
|
|
26
|
+
const parseDelegationInboxAcceptance = (input) => parse(acceptance, input);
|
|
27
|
+
const parseDelegationInboxInspection = (input) => parse(inspection, input);
|
|
28
|
+
export {
|
|
29
|
+
parseDelegationInboxAcceptance,
|
|
30
|
+
parseDelegationInboxInspection
|
|
31
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { now } from "../execution/state.js";
|
|
2
|
+
import { readNotification, receiptNotification } from "./journal.js";
|
|
3
|
+
async function recoverNotificationClaims(state) {
|
|
4
|
+
let cursor;
|
|
5
|
+
do {
|
|
6
|
+
const page = await state.options.store.scan({ kind: "eligible_notifications", at: now(), limit: state.options.limits.maxPageRecords, ...cursor ? { cursor } : {} });
|
|
7
|
+
if (page.kind !== "eligible_notifications") return;
|
|
8
|
+
for (const scanned of page.page.items) {
|
|
9
|
+
const notification = await readNotification(state, scanned.envelope.notificationId);
|
|
10
|
+
if (notification.state !== "claimed" || notification.claim?.ownerEpoch === state.owner.epoch) continue;
|
|
11
|
+
await receiptNotification(state, notification, { status: "uncertain", retryAt: new Date(Date.now() + 1e3).toISOString() });
|
|
12
|
+
}
|
|
13
|
+
cursor = page.page.cursor;
|
|
14
|
+
} while (cursor);
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
recoverNotificationClaims
|
|
18
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { now } from "../execution/state.js";
|
|
2
|
+
import { DelegationOperationError } from "../shared/operational-errors.js";
|
|
3
|
+
import { callInbox } from "./calls.js";
|
|
4
|
+
import { claimNotification, readNotification, receiptNotification } from "./journal.js";
|
|
5
|
+
import { parseDelegationInboxAcceptance, parseDelegationInboxInspection } from "./parse.js";
|
|
6
|
+
function retryTime(attempt) {
|
|
7
|
+
return new Date(Date.now() + Math.min(3e4, 1e3 * 2 ** Math.min(5, Math.max(0, attempt - 1)))).toISOString();
|
|
8
|
+
}
|
|
9
|
+
function eligibleAt(notification) {
|
|
10
|
+
const retry = notification.retryAt ? Date.parse(notification.retryAt) : 0;
|
|
11
|
+
return notification.reason === "uncertain" ? retry : Math.min(Date.parse(notification.envelope.expiresAt), retry);
|
|
12
|
+
}
|
|
13
|
+
function deliveryResult(value, attempt) {
|
|
14
|
+
if (value.status === "accepted") return value;
|
|
15
|
+
if (value.status === "uncertain") return { status: "uncertain", retryAt: retryTime(attempt) };
|
|
16
|
+
if (value.status === "blocked") return { ...value, retryAt: new Date(Math.max(Date.now() + 1e3, Math.min(Date.now() + 3e4, Date.parse(value.retryAt)))).toISOString() };
|
|
17
|
+
return { status: value.reason === "expired" || value.reason === "dismissed" ? value.reason : "rejected", reason: value.reason };
|
|
18
|
+
}
|
|
19
|
+
function installDeliveryScheduler(state) {
|
|
20
|
+
const port = state.options.parentDelivery;
|
|
21
|
+
let stopped = false;
|
|
22
|
+
let pump;
|
|
23
|
+
let pending = false;
|
|
24
|
+
let timer;
|
|
25
|
+
const isStopped = () => stopped || state.failed || state.released;
|
|
26
|
+
async function deliver(notification) {
|
|
27
|
+
if (notification.state === "claimed") {
|
|
28
|
+
return receiptNotification(state, notification, { status: "uncertain", retryAt: retryTime(notification.attempts) });
|
|
29
|
+
}
|
|
30
|
+
const claimed = await claimNotification(state, notification);
|
|
31
|
+
if (claimed.state !== "claimed") return claimed;
|
|
32
|
+
let result = { status: "uncertain", retryAt: retryTime(claimed.attempts) };
|
|
33
|
+
try {
|
|
34
|
+
let inspection = { status: "absent" };
|
|
35
|
+
if (claimed.attempts > 1) {
|
|
36
|
+
const raw = await callInbox(state, claimed, "inspect", isStopped, (signal, deadlineAt) => port.inspect({
|
|
37
|
+
storeId: claimed.envelope.storeId,
|
|
38
|
+
notificationId: claimed.envelope.notificationId,
|
|
39
|
+
parent: claimed.envelope.parent,
|
|
40
|
+
signal,
|
|
41
|
+
deadlineAt
|
|
42
|
+
}));
|
|
43
|
+
if (raw === void 0) return receiptNotification(state, claimed, { status: "uncertain", retryAt: retryTime(claimed.attempts) });
|
|
44
|
+
inspection = parseDelegationInboxInspection(raw);
|
|
45
|
+
}
|
|
46
|
+
if (inspection.status !== "absent") result = deliveryResult(inspection, claimed.attempts);
|
|
47
|
+
else if (Date.parse(claimed.envelope.expiresAt) <= Date.now()) result = { status: "expired", reason: "expired" };
|
|
48
|
+
else if (!isStopped() && state.deliveryCalls.size === 0) {
|
|
49
|
+
const raw = await callInbox(state, claimed, "accept", isStopped, (signal, deadlineAt) => port.accept({ envelope: claimed.envelope, signal, deadlineAt }));
|
|
50
|
+
result = raw === void 0 ? { status: "uncertain", retryAt: retryTime(claimed.attempts) } : deliveryResult(parseDelegationInboxAcceptance(raw), claimed.attempts);
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
result = { status: "uncertain", retryAt: retryTime(claimed.attempts) };
|
|
54
|
+
}
|
|
55
|
+
return receiptNotification(state, claimed, result);
|
|
56
|
+
}
|
|
57
|
+
async function run() {
|
|
58
|
+
if (timer) {
|
|
59
|
+
clearTimeout(timer);
|
|
60
|
+
timer = void 0;
|
|
61
|
+
}
|
|
62
|
+
let earliest = Infinity;
|
|
63
|
+
let cursor;
|
|
64
|
+
do {
|
|
65
|
+
const page = await state.options.store.scan({ kind: "eligible_notifications", at: now(), limit: state.options.limits.maxPageRecords, ...cursor ? { cursor } : {} });
|
|
66
|
+
if (page.kind !== "eligible_notifications") return;
|
|
67
|
+
for (const scanned of page.page.items) {
|
|
68
|
+
if (isStopped() || state.deliveryCalls.size > 0) return;
|
|
69
|
+
let notification = await readNotification(state, scanned.envelope.notificationId);
|
|
70
|
+
if (notification.envelope.policy === "collect" || !["pending", "claimed"].includes(notification.state)) continue;
|
|
71
|
+
if (notification.state === "claimed" && notification.claim?.ownerEpoch === state.owner.epoch) continue;
|
|
72
|
+
const eligible = eligibleAt(notification);
|
|
73
|
+
if (notification.state === "pending" && eligible > Date.now()) {
|
|
74
|
+
earliest = Math.min(earliest, eligible);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
notification = await deliver(notification);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (!(error instanceof DelegationOperationError) || error.code !== "stale_revision") throw error;
|
|
81
|
+
notification = await readNotification(state, scanned.envelope.notificationId);
|
|
82
|
+
}
|
|
83
|
+
if (notification.state === "pending") earliest = Math.min(earliest, eligibleAt(notification));
|
|
84
|
+
}
|
|
85
|
+
cursor = page.page.cursor;
|
|
86
|
+
} while (cursor && !isStopped());
|
|
87
|
+
if (Number.isFinite(earliest) && !isStopped() && state.deliveryCalls.size === 0) {
|
|
88
|
+
timer = setTimeout(() => {
|
|
89
|
+
timer = void 0;
|
|
90
|
+
schedule();
|
|
91
|
+
}, Math.max(1, earliest - Date.now()));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function schedule() {
|
|
95
|
+
if (!port || isStopped()) return;
|
|
96
|
+
if (pump) {
|
|
97
|
+
pending = true;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const work = Promise.resolve().then(run).catch(() => state.fail()).finally(() => {
|
|
101
|
+
pump = void 0;
|
|
102
|
+
state.changed();
|
|
103
|
+
if (pending) {
|
|
104
|
+
pending = false;
|
|
105
|
+
schedule();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
pump = work;
|
|
109
|
+
state.track(work);
|
|
110
|
+
}
|
|
111
|
+
state.wakeDelivery = schedule;
|
|
112
|
+
state.stopDelivery = (abort) => {
|
|
113
|
+
stopped = true;
|
|
114
|
+
if (timer) {
|
|
115
|
+
clearTimeout(timer);
|
|
116
|
+
timer = void 0;
|
|
117
|
+
}
|
|
118
|
+
if (abort) for (const call of state.deliveryCalls.values()) call.controller.abort(new DOMException("Parent inbox delivery stopped.", "AbortError"));
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export {
|
|
122
|
+
installDeliveryScheduler
|
|
123
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CreateDelegationCoordinatorOptions } from "../coordinator/types.js";
|
|
2
|
+
import type { CoordinatorState } from "../execution/state.js";
|
|
3
|
+
import type { DelegationCompletionPolicy } from "../tasks/types.js";
|
|
4
|
+
export declare function validateDeliverySetup(options: CreateDelegationCoordinatorOptions): void;
|
|
5
|
+
export declare function assertCompletionPolicy(state: CoordinatorState, policy: DelegationCompletionPolicy): void;
|
|
6
|
+
export declare function assertRetainedDeliverySetup(state: CoordinatorState): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { now } from "../execution/state.js";
|
|
2
|
+
import { failDelegation } from "../shared/operational-errors.js";
|
|
3
|
+
function validateDeliverySetup(options) {
|
|
4
|
+
const port = options.parentDelivery;
|
|
5
|
+
if (port === void 0) return;
|
|
6
|
+
if (!port || typeof port !== "object" || !["volatile", "durable"].includes(port.persistence) || typeof port.accept !== "function" || typeof port.inspect !== "function") {
|
|
7
|
+
failDelegation("unsupported_operation", "Completion delivery requires a declared bounded inbox port.");
|
|
8
|
+
}
|
|
9
|
+
if (options.store.persistence === "durable" && port.persistence !== "durable") {
|
|
10
|
+
failDelegation("unsupported_operation", "A durable task store requires durable parent inbox acceptance.");
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function assertCompletionPolicy(state, policy) {
|
|
14
|
+
if (policy !== "collect" && !state.options.parentDelivery) {
|
|
15
|
+
failDelegation("unsupported_operation", "This completion policy requires an explicit compatible parent inbox port.");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function assertRetainedDeliverySetup(state) {
|
|
19
|
+
if (state.options.parentDelivery) return;
|
|
20
|
+
for (const kind of ["queued", "recoverable_runs", "eligible_notifications"]) {
|
|
21
|
+
let cursor;
|
|
22
|
+
do {
|
|
23
|
+
const page = await state.options.store.scan({ kind, at: now(), limit: state.options.limits.maxPageRecords, ...cursor ? { cursor } : {} });
|
|
24
|
+
if (page.kind === "eligible_notifications") {
|
|
25
|
+
for (const notification of page.page.items) assertCompletionPolicy(state, notification.envelope.policy);
|
|
26
|
+
} else {
|
|
27
|
+
for (const run of page.page.items) assertCompletionPolicy(state, (await state.task(run.taskId)).task.completionPolicy);
|
|
28
|
+
}
|
|
29
|
+
cursor = page.page.cursor;
|
|
30
|
+
} while (cursor);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
assertCompletionPolicy,
|
|
35
|
+
assertRetainedDeliverySetup,
|
|
36
|
+
validateDeliverySetup
|
|
37
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { DelegationCompletionEnvelope, DelegationParentScope, DelegationPersistence } from "../tasks/types.js";
|
|
2
|
+
export type DelegationInboxAcceptance = Readonly<{
|
|
3
|
+
status: "accepted";
|
|
4
|
+
inboxId: string;
|
|
5
|
+
acceptedAt: string;
|
|
6
|
+
}> | Readonly<{
|
|
7
|
+
status: "blocked";
|
|
8
|
+
reason: "busy" | "authority_unavailable" | "capacity";
|
|
9
|
+
retryAt: string;
|
|
10
|
+
}> | Readonly<{
|
|
11
|
+
status: "rejected";
|
|
12
|
+
reason: "revoked" | "parent_retired" | "expired" | "dismissed";
|
|
13
|
+
}> | Readonly<{
|
|
14
|
+
status: "uncertain";
|
|
15
|
+
}>;
|
|
16
|
+
export type DelegationInboxInspection = Readonly<{
|
|
17
|
+
status: "absent";
|
|
18
|
+
}> | Extract<DelegationInboxAcceptance, {
|
|
19
|
+
status: "accepted" | "rejected";
|
|
20
|
+
}>;
|
|
21
|
+
export interface DelegationParentDeliveryPort {
|
|
22
|
+
readonly persistence: DelegationPersistence;
|
|
23
|
+
accept(input: Readonly<{
|
|
24
|
+
envelope: DelegationCompletionEnvelope;
|
|
25
|
+
signal: AbortSignal;
|
|
26
|
+
deadlineAt: string;
|
|
27
|
+
}>): Promise<DelegationInboxAcceptance>;
|
|
28
|
+
inspect(input: Readonly<{
|
|
29
|
+
storeId: string;
|
|
30
|
+
notificationId: string;
|
|
31
|
+
parent: DelegationParentScope;
|
|
32
|
+
signal: AbortSignal;
|
|
33
|
+
deadlineAt: string;
|
|
34
|
+
}>): Promise<DelegationInboxInspection>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=types.d.ts.map
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DelegationTarget, DelegationTargetReference } from "../catalog/types.js";
|
|
2
|
+
import type { DelegationPolicyCeiling } from "../tasks/types.js";
|
|
3
|
+
import type { CoordinatorState, LiveRun, ParentBinding } from "./state.js";
|
|
4
|
+
export declare function sameTarget(left: DelegationTargetReference, right: DelegationTargetReference): boolean;
|
|
5
|
+
export declare function readCeiling(state: CoordinatorState, ceilingId: string): Promise<DelegationPolicyCeiling>;
|
|
6
|
+
export declare function bindingCeiling(state: CoordinatorState, binding: ParentBinding): Promise<DelegationPolicyCeiling | undefined>;
|
|
7
|
+
export declare function effectiveChoices(state: CoordinatorState, binding: ParentBinding): Promise<{
|
|
8
|
+
resolved: import("../index.js").ResolvedDelegationPolicy;
|
|
9
|
+
targets: DelegationTarget[];
|
|
10
|
+
ceiling: DelegationPolicyCeiling;
|
|
11
|
+
originPolicyRevision: number;
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function admittedTarget(choices: Awaited<ReturnType<typeof effectiveChoices>>, reference: DelegationTargetReference, revision: number): DelegationTarget;
|
|
15
|
+
export declare function assertRunDispatch(state: CoordinatorState, live: LiveRun, policyRevision: number, originPolicyRevision: number): void;
|
|
16
|
+
//# sourceMappingURL=authority.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { resolveDelegationPolicy } from "../policy/resolve.js";
|
|
2
|
+
import { delegationCanonicalJson } from "../shared/identity.js";
|
|
3
|
+
import { failDelegation } from "../shared/operational-errors.js";
|
|
4
|
+
import { createDelegationPolicyCeiling, parseDelegationParentScope } from "../tasks/parse.js";
|
|
5
|
+
function sameTarget(left, right) {
|
|
6
|
+
return delegationCanonicalJson(left) === delegationCanonicalJson(right);
|
|
7
|
+
}
|
|
8
|
+
function current(state, scope) {
|
|
9
|
+
const snapshot = state.options.host.currentPolicy(scope);
|
|
10
|
+
const actual = parseDelegationParentScope(snapshot.scope);
|
|
11
|
+
if (actual.parentId !== scope.parentId || actual.scopeId !== scope.scopeId || actual.revision !== scope.revision) {
|
|
12
|
+
failDelegation("stale_revision", "The parent scope grant is no longer current.");
|
|
13
|
+
}
|
|
14
|
+
return resolveDelegationPolicy({ catalog: snapshot.catalog, policy: snapshot.policy });
|
|
15
|
+
}
|
|
16
|
+
async function readCeiling(state, ceilingId) {
|
|
17
|
+
const targets = [];
|
|
18
|
+
let origin;
|
|
19
|
+
let cursor;
|
|
20
|
+
do {
|
|
21
|
+
const page = await state.options.store.read({ kind: "ceiling", ceilingId, ...cursor === void 0 ? {} : { cursor }, limit: state.options.limits.maxPageRecords });
|
|
22
|
+
if (page.kind !== "ceiling" || !page.origin) return failDelegation("owner_lost", "The originating policy ceiling is unavailable.");
|
|
23
|
+
origin = page.origin;
|
|
24
|
+
targets.push(...page.page.items);
|
|
25
|
+
if (targets.length > 1e3) failDelegation("capacity_exceeded", "The originating policy ceiling exceeds its bound.");
|
|
26
|
+
cursor = page.page.cursor;
|
|
27
|
+
} while (cursor !== void 0);
|
|
28
|
+
if (!origin) return failDelegation("owner_lost", "The originating policy ceiling is unavailable.");
|
|
29
|
+
const ceiling = createDelegationPolicyCeiling({ origin, targets });
|
|
30
|
+
if (ceiling.ceilingId !== ceilingId) failDelegation("owner_lost", "The stored policy ceiling is inconsistent.");
|
|
31
|
+
return ceiling;
|
|
32
|
+
}
|
|
33
|
+
async function bindingCeiling(state, binding) {
|
|
34
|
+
if (binding.ceiling) return binding.ceiling;
|
|
35
|
+
if (!binding.ancestor) return void 0;
|
|
36
|
+
const [{ task }, run] = await Promise.all([state.task(binding.ancestor.taskId), state.run(binding.ancestor.taskId, binding.ancestor.runId)]);
|
|
37
|
+
if (task.parent.scopeId !== binding.scope.scopeId || run.ownerEpoch !== state.owner.epoch || !["preparing", "running", "waiting"].includes(run.phase) || !state.live.has(run.runId)) {
|
|
38
|
+
failDelegation("owner_lost", "The descendant binding does not belong to a live owned ancestor run.");
|
|
39
|
+
}
|
|
40
|
+
return readCeiling(state, task.policyCeilingRef);
|
|
41
|
+
}
|
|
42
|
+
async function effectiveChoices(state, binding) {
|
|
43
|
+
const inherited = await bindingCeiling(state, binding);
|
|
44
|
+
const resolved = current(state, binding.scope);
|
|
45
|
+
const origin = inherited === void 0 ? resolved : current(state, inherited.origin);
|
|
46
|
+
const inheritedKeys = inherited === void 0 ? void 0 : new Set(inherited.targets.map(delegationCanonicalJson));
|
|
47
|
+
const originKeys = new Set(origin.allowedTargets.map((target) => delegationCanonicalJson(target.reference)));
|
|
48
|
+
const targets = resolved.allowedTargets.filter((target) => originKeys.has(delegationCanonicalJson(target.reference)) && (inheritedKeys === void 0 || inheritedKeys.has(delegationCanonicalJson(target.reference))));
|
|
49
|
+
const ceiling = inherited ?? createDelegationPolicyCeiling({ origin: binding.scope, targets: targets.map((target) => target.reference) });
|
|
50
|
+
return { resolved, targets, ceiling, originPolicyRevision: origin.policyRevision, enabled: resolved.enabled && origin.enabled };
|
|
51
|
+
}
|
|
52
|
+
function admittedTarget(choices, reference, revision) {
|
|
53
|
+
if (choices.resolved.policyRevision !== revision) failDelegation("stale_revision", "The selected managed policy revision is stale.");
|
|
54
|
+
if (!choices.resolved.enabled) failDelegation("delegation_disabled", "Managed delegation is disabled.");
|
|
55
|
+
const decision = choices.resolved.decisions.find((entry) => entry.reference.targetId === reference.targetId && entry.reference.catalogId === reference.catalogId);
|
|
56
|
+
if (!decision || !sameTarget(decision.reference, reference)) failDelegation("stale_target", "The exact selected target or source revision is stale.");
|
|
57
|
+
const target = choices.targets.find((entry) => sameTarget(entry.reference, reference));
|
|
58
|
+
if (!target) {
|
|
59
|
+
if (decision.reasons.some((reason) => reason.code === "target_unavailable")) failDelegation("target_unavailable", "The selected target is unavailable.");
|
|
60
|
+
failDelegation("policy_denied", "Current policy or the originating ceiling denies this target.");
|
|
61
|
+
}
|
|
62
|
+
return target;
|
|
63
|
+
}
|
|
64
|
+
function assertRunDispatch(state, live, policyRevision, originPolicyRevision) {
|
|
65
|
+
if (state.closing && !state.draining || state.failed || state.released) failDelegation("owner_lost", "The coordinator is closed to new execution input.");
|
|
66
|
+
if (live.lifetime.signal.aborted) failDelegation("recovery_required", "The run no longer accepts execution input.");
|
|
67
|
+
const resolved = current(state, live.scope);
|
|
68
|
+
const origin = current(state, live.ceiling.origin);
|
|
69
|
+
if (resolved.policyRevision !== policyRevision || origin.policyRevision !== originPolicyRevision) {
|
|
70
|
+
failDelegation("stale_revision", "Managed policy changed before provider execution.");
|
|
71
|
+
}
|
|
72
|
+
if (!resolved.enabled || !origin.enabled) failDelegation("delegation_disabled", "Managed delegation is disabled.");
|
|
73
|
+
const exact = (target) => sameTarget(target.reference, live.target.reference);
|
|
74
|
+
if (!live.ceiling.targets.some((reference) => sameTarget(reference, live.target.reference)) || !resolved.allowedTargets.some(exact) || !origin.allowedTargets.some(exact)) {
|
|
75
|
+
failDelegation("policy_denied", "Current policy or the originating ceiling no longer permits the exact target.");
|
|
76
|
+
}
|
|
77
|
+
live.execution?.assertCurrent();
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
admittedTarget,
|
|
81
|
+
assertRunDispatch,
|
|
82
|
+
bindingCeiling,
|
|
83
|
+
effectiveChoices,
|
|
84
|
+
readCeiling,
|
|
85
|
+
sameTarget
|
|
86
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { dispatchIntent, operationIdentity, operationReceipt } from "./journal.js";
|
|
2
|
+
async function record(state, action) {
|
|
3
|
+
try {
|
|
4
|
+
await action();
|
|
5
|
+
} catch {
|
|
6
|
+
state.fail();
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
async function runtimeCleanup(state, live) {
|
|
10
|
+
const close = operationIdentity(live, "session_close");
|
|
11
|
+
const binding = operationIdentity(live, "binding_cleanup");
|
|
12
|
+
if (live.rejectedOpening && live.runtimeCleanupAttempts === 0) {
|
|
13
|
+
live.runtimeCleanupAttempts = 1;
|
|
14
|
+
await record(state, () => dispatchIntent(state, live, "session_close", close, { attempt: 1 }));
|
|
15
|
+
await record(state, () => dispatchIntent(state, live, "binding_cleanup", binding, { attempt: 1 }));
|
|
16
|
+
await record(state, () => operationReceipt(state, live, "uncertain", close));
|
|
17
|
+
await record(state, () => operationReceipt(state, live, "uncertain", binding));
|
|
18
|
+
}
|
|
19
|
+
while (!live.runtimeClosed && live.runtimeCleanupAttempts < 2) {
|
|
20
|
+
live.runtimeCleanupAttempts += 1;
|
|
21
|
+
const attempt = live.runtimeCleanupAttempts;
|
|
22
|
+
await record(state, () => dispatchIntent(state, live, "session_close", close, { attempt }));
|
|
23
|
+
await record(state, () => dispatchIntent(state, live, "binding_cleanup", binding, { attempt }));
|
|
24
|
+
try {
|
|
25
|
+
if (live.rejectedOpening) await live.rejectedOpening.retryCleanup();
|
|
26
|
+
else if (live.session) await live.session.close({ reason: state.closing ? "shutdown" : "idle" });
|
|
27
|
+
live.runtimeClosed = true;
|
|
28
|
+
} catch {
|
|
29
|
+
await record(state, () => operationReceipt(state, live, "uncertain", close));
|
|
30
|
+
await record(state, () => operationReceipt(state, live, "uncertain", binding));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return live.runtimeClosed;
|
|
34
|
+
}
|
|
35
|
+
function cleanupRun(state, live) {
|
|
36
|
+
if (live.cleanup) return live.cleanup;
|
|
37
|
+
live.cleanup = (async () => {
|
|
38
|
+
live.removeRevocation?.();
|
|
39
|
+
if (live.deadline) clearTimeout(live.deadline);
|
|
40
|
+
if (live.request) {
|
|
41
|
+
state.requests.delete(live.request.handle);
|
|
42
|
+
live.request = void 0;
|
|
43
|
+
}
|
|
44
|
+
const reason = new DOMException("The managed run is closing.", "AbortError");
|
|
45
|
+
live.controlAbort.abort(reason);
|
|
46
|
+
const runtimeClosing = runtimeCleanup(state, live);
|
|
47
|
+
live.lifetime.abort(reason);
|
|
48
|
+
while (live.controls.size > 0) await Promise.allSettled(live.controls);
|
|
49
|
+
if (!await runtimeClosing) return false;
|
|
50
|
+
while (!live.resourcesReleased && live.resourceReleaseAttempts < 2) {
|
|
51
|
+
live.resourceReleaseAttempts += 1;
|
|
52
|
+
try {
|
|
53
|
+
await live.resources?.release();
|
|
54
|
+
live.resourcesReleased = true;
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!live.resourcesReleased) return false;
|
|
59
|
+
await record(state, () => operationReceipt(state, live, "completed", operationIdentity(live, "session_close")));
|
|
60
|
+
await record(state, () => operationReceipt(state, live, "completed", operationIdentity(live, "binding_cleanup")));
|
|
61
|
+
while (!live.executionReleased && live.executionReleaseAttempts < 2) {
|
|
62
|
+
live.executionReleaseAttempts += 1;
|
|
63
|
+
try {
|
|
64
|
+
await live.execution?.release();
|
|
65
|
+
live.executionReleased = true;
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return live.executionReleased;
|
|
70
|
+
})();
|
|
71
|
+
return live.cleanup;
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
cleanupRun
|
|
75
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DelegationRequestReceipt, DelegationResolveHostRequestInput, DelegationSendReceipt, DelegationSendTaskInput } from "../coordinator/types.js";
|
|
2
|
+
import { type CoordinatorState, type LiveRun } from "./state.js";
|
|
3
|
+
export declare function cancelLive(state: CoordinatorState, live: LiveRun, reason: string): Promise<unknown>;
|
|
4
|
+
export declare function steerRun(state: CoordinatorState, live: LiveRun, input: DelegationSendTaskInput, digest: string, originPolicyRevision: number): Promise<DelegationSendReceipt>;
|
|
5
|
+
export declare function resolveLiveRequest(state: CoordinatorState, input: DelegationResolveHostRequestInput): Promise<DelegationRequestReceipt>;
|
|
6
|
+
//# sourceMappingURL=control.d.ts.map
|