@nifrajs/coding-agent 3.2.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/EXTENSIONS.md +25 -0
- package/PERFORMANCE.md +19 -0
- package/README.md +69 -0
- package/REGISTRY.md +15 -0
- package/SECURITY.md +32 -0
- package/dist/approvals.d.ts +70 -0
- package/dist/approvals.d.ts.map +1 -0
- package/dist/approvals.js +174 -0
- package/dist/approvals.js.map +1 -0
- package/dist/capabilities.d.ts +20 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +49 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/cli.d.ts +24 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +380 -0
- package/dist/cli.js.map +1 -0
- package/dist/context.d.ts +17 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +66 -0
- package/dist/context.js.map +1 -0
- package/dist/deployment-adapters.d.ts +40 -0
- package/dist/deployment-adapters.d.ts.map +1 -0
- package/dist/deployment-adapters.js +147 -0
- package/dist/deployment-adapters.js.map +1 -0
- package/dist/diff.d.ts +17 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +61 -0
- package/dist/diff.js.map +1 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/extensions.d.ts +104 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +284 -0
- package/dist/extensions.js.map +1 -0
- package/dist/handoffs.d.ts +97 -0
- package/dist/handoffs.d.ts.map +1 -0
- package/dist/handoffs.js +199 -0
- package/dist/handoffs.js.map +1 -0
- package/dist/healing.d.ts +56 -0
- package/dist/healing.d.ts.map +1 -0
- package/dist/healing.js +54 -0
- package/dist/healing.js.map +1 -0
- package/dist/host.d.ts +61 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +369 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/isolated-worker.d.ts +2 -0
- package/dist/isolated-worker.d.ts.map +1 -0
- package/dist/isolated-worker.js +155 -0
- package/dist/isolated-worker.js.map +1 -0
- package/dist/isolated.d.ts +52 -0
- package/dist/isolated.d.ts.map +1 -0
- package/dist/isolated.js +293 -0
- package/dist/isolated.js.map +1 -0
- package/dist/native.d.ts +92 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +339 -0
- package/dist/native.js.map +1 -0
- package/dist/orchestration/artifact-port.d.ts +30 -0
- package/dist/orchestration/artifact-port.d.ts.map +1 -0
- package/dist/orchestration/artifact-port.js +59 -0
- package/dist/orchestration/artifact-port.js.map +1 -0
- package/dist/orchestration/catalog.d.ts +71 -0
- package/dist/orchestration/catalog.d.ts.map +1 -0
- package/dist/orchestration/catalog.js +46 -0
- package/dist/orchestration/catalog.js.map +1 -0
- package/dist/orchestration/compile.d.ts +59 -0
- package/dist/orchestration/compile.d.ts.map +1 -0
- package/dist/orchestration/compile.js +268 -0
- package/dist/orchestration/compile.js.map +1 -0
- package/dist/orchestration/dispatch.d.ts +115 -0
- package/dist/orchestration/dispatch.d.ts.map +1 -0
- package/dist/orchestration/dispatch.js +220 -0
- package/dist/orchestration/dispatch.js.map +1 -0
- package/dist/orchestration/durable-jobs.d.ts +35 -0
- package/dist/orchestration/durable-jobs.d.ts.map +1 -0
- package/dist/orchestration/durable-jobs.js +273 -0
- package/dist/orchestration/durable-jobs.js.map +1 -0
- package/dist/orchestration/effect-key.d.ts +23 -0
- package/dist/orchestration/effect-key.d.ts.map +1 -0
- package/dist/orchestration/effect-key.js +24 -0
- package/dist/orchestration/effect-key.js.map +1 -0
- package/dist/orchestration/evidence-store.d.ts +75 -0
- package/dist/orchestration/evidence-store.d.ts.map +1 -0
- package/dist/orchestration/evidence-store.js +183 -0
- package/dist/orchestration/evidence-store.js.map +1 -0
- package/dist/orchestration/hash.d.ts +12 -0
- package/dist/orchestration/hash.d.ts.map +1 -0
- package/dist/orchestration/hash.js +43 -0
- package/dist/orchestration/hash.js.map +1 -0
- package/dist/orchestration/host.d.ts +121 -0
- package/dist/orchestration/host.d.ts.map +1 -0
- package/dist/orchestration/host.js +334 -0
- package/dist/orchestration/host.js.map +1 -0
- package/dist/orchestration/index.d.ts +20 -0
- package/dist/orchestration/index.d.ts.map +1 -0
- package/dist/orchestration/index.js +19 -0
- package/dist/orchestration/index.js.map +1 -0
- package/dist/orchestration/policy.d.ts +63 -0
- package/dist/orchestration/policy.d.ts.map +1 -0
- package/dist/orchestration/policy.js +80 -0
- package/dist/orchestration/policy.js.map +1 -0
- package/dist/orchestration/recovery.d.ts +87 -0
- package/dist/orchestration/recovery.d.ts.map +1 -0
- package/dist/orchestration/recovery.js +197 -0
- package/dist/orchestration/recovery.js.map +1 -0
- package/dist/orchestration/tracer.d.ts +31 -0
- package/dist/orchestration/tracer.d.ts.map +1 -0
- package/dist/orchestration/tracer.js +141 -0
- package/dist/orchestration/tracer.js.map +1 -0
- package/dist/plans.d.ts +35 -0
- package/dist/plans.d.ts.map +1 -0
- package/dist/plans.js +63 -0
- package/dist/plans.js.map +1 -0
- package/dist/presets.d.ts +10 -0
- package/dist/presets.d.ts.map +1 -0
- package/dist/presets.js +70 -0
- package/dist/presets.js.map +1 -0
- package/dist/process.d.ts +6 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +42 -0
- package/dist/process.js.map +1 -0
- package/dist/registry.d.ts +37 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +51 -0
- package/dist/registry.js.map +1 -0
- package/dist/replay.d.ts +24 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +154 -0
- package/dist/replay.js.map +1 -0
- package/dist/rpc.d.ts +51 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +412 -0
- package/dist/rpc.js.map +1 -0
- package/dist/session-migration.d.ts +59 -0
- package/dist/session-migration.d.ts.map +1 -0
- package/dist/session-migration.js +315 -0
- package/dist/session-migration.js.map +1 -0
- package/dist/sessions.d.ts +74 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +222 -0
- package/dist/sessions.js.map +1 -0
- package/dist/subagents.d.ts +51 -0
- package/dist/subagents.d.ts.map +1 -0
- package/dist/subagents.js +141 -0
- package/dist/subagents.js.map +1 -0
- package/dist/tools.d.ts +13 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +37 -0
- package/dist/tools.js.map +1 -0
- package/dist/ui.d.ts +42 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +93 -0
- package/dist/ui.js.map +1 -0
- package/dist/verification.d.ts +34 -0
- package/dist/verification.d.ts.map +1 -0
- package/dist/verification.js +91 -0
- package/dist/verification.js.map +1 -0
- package/dist/workflows.d.ts +98 -0
- package/dist/workflows.d.ts.map +1 -0
- package/dist/workflows.js +174 -0
- package/dist/workflows.js.map +1 -0
- package/package.json +84 -0
- package/src/approvals.ts +249 -0
- package/src/capabilities.ts +74 -0
- package/src/cli.ts +405 -0
- package/src/context.ts +81 -0
- package/src/deployment-adapters.ts +181 -0
- package/src/diff.ts +80 -0
- package/src/errors.ts +46 -0
- package/src/extensions.ts +402 -0
- package/src/handoffs.ts +299 -0
- package/src/healing.ts +93 -0
- package/src/host.ts +465 -0
- package/src/index.ts +165 -0
- package/src/isolated-worker.ts +177 -0
- package/src/isolated.ts +344 -0
- package/src/native.ts +478 -0
- package/src/orchestration/artifact-port.ts +75 -0
- package/src/orchestration/catalog.ts +102 -0
- package/src/orchestration/compile.ts +373 -0
- package/src/orchestration/dispatch.ts +383 -0
- package/src/orchestration/durable-jobs.ts +374 -0
- package/src/orchestration/effect-key.ts +35 -0
- package/src/orchestration/evidence-store.ts +233 -0
- package/src/orchestration/hash.ts +43 -0
- package/src/orchestration/host.ts +445 -0
- package/src/orchestration/index.ts +131 -0
- package/src/orchestration/policy.ts +116 -0
- package/src/orchestration/recovery.ts +271 -0
- package/src/orchestration/tracer.ts +177 -0
- package/src/plans.ts +105 -0
- package/src/presets.ts +90 -0
- package/src/process.ts +47 -0
- package/src/registry.ts +80 -0
- package/src/replay.ts +190 -0
- package/src/rpc.ts +598 -0
- package/src/session-migration.ts +401 -0
- package/src/sessions.ts +303 -0
- package/src/subagents.ts +210 -0
- package/src/tools.ts +52 -0
- package/src/ui.ts +137 -0
- package/src/verification.ts +133 -0
- package/src/workflows.ts +279 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export interface WorkflowContext {
|
|
2
|
+
readonly signal: AbortSignal;
|
|
3
|
+
readonly values: ReadonlyMap<string, unknown>;
|
|
4
|
+
set(name: string, value: unknown): void;
|
|
5
|
+
run(step: WorkflowStep): Promise<unknown>;
|
|
6
|
+
}
|
|
7
|
+
export type WorkflowStep = {
|
|
8
|
+
readonly type: "task";
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly run: (context: WorkflowContext) => unknown | PromiseLike<unknown>;
|
|
11
|
+
} | {
|
|
12
|
+
readonly type: "sequence";
|
|
13
|
+
readonly steps: readonly WorkflowStep[];
|
|
14
|
+
} | {
|
|
15
|
+
readonly type: "parallel";
|
|
16
|
+
readonly steps: readonly WorkflowStep[];
|
|
17
|
+
readonly maxConcurrency?: number;
|
|
18
|
+
} | {
|
|
19
|
+
readonly type: "verify";
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly run: (context: WorkflowContext) => unknown | PromiseLike<boolean | {
|
|
22
|
+
readonly ok: boolean;
|
|
23
|
+
readonly report?: unknown;
|
|
24
|
+
}>;
|
|
25
|
+
} | {
|
|
26
|
+
readonly type: "approve";
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly reason: string;
|
|
29
|
+
readonly run: (context: WorkflowContext) => unknown | PromiseLike<boolean>;
|
|
30
|
+
} | {
|
|
31
|
+
readonly type: "retry";
|
|
32
|
+
readonly step: WorkflowStep;
|
|
33
|
+
readonly attempts?: number;
|
|
34
|
+
readonly backoffMs?: number;
|
|
35
|
+
} | {
|
|
36
|
+
readonly type: "branch";
|
|
37
|
+
readonly id: string;
|
|
38
|
+
readonly when: (context: WorkflowContext) => boolean | PromiseLike<boolean>;
|
|
39
|
+
readonly then: WorkflowStep;
|
|
40
|
+
readonly otherwise?: WorkflowStep;
|
|
41
|
+
} | {
|
|
42
|
+
readonly type: "checkpoint";
|
|
43
|
+
readonly id: string;
|
|
44
|
+
readonly run: (context: WorkflowContext) => unknown | PromiseLike<unknown>;
|
|
45
|
+
} | {
|
|
46
|
+
readonly type: "handoff";
|
|
47
|
+
readonly id: string;
|
|
48
|
+
readonly run: (context: WorkflowContext) => unknown | PromiseLike<unknown>;
|
|
49
|
+
};
|
|
50
|
+
export interface WorkflowRunnerOptions {
|
|
51
|
+
readonly signal?: AbortSignal;
|
|
52
|
+
readonly maxSteps?: number;
|
|
53
|
+
readonly maxDepth?: number;
|
|
54
|
+
readonly onEvent?: (event: WorkflowEvent) => void | PromiseLike<void>;
|
|
55
|
+
readonly exposeErrorStacks?: boolean;
|
|
56
|
+
}
|
|
57
|
+
export type WorkflowEvent = {
|
|
58
|
+
readonly type: "step.started";
|
|
59
|
+
readonly id: string;
|
|
60
|
+
} | {
|
|
61
|
+
readonly type: "step.completed";
|
|
62
|
+
readonly id: string;
|
|
63
|
+
readonly output?: unknown;
|
|
64
|
+
} | {
|
|
65
|
+
readonly type: "step.failed";
|
|
66
|
+
readonly id: string;
|
|
67
|
+
readonly error: string;
|
|
68
|
+
readonly stack?: string;
|
|
69
|
+
} | {
|
|
70
|
+
readonly type: "approval.required";
|
|
71
|
+
readonly id: string;
|
|
72
|
+
readonly reason: string;
|
|
73
|
+
} | {
|
|
74
|
+
readonly type: "checkpoint.created";
|
|
75
|
+
readonly id: string;
|
|
76
|
+
};
|
|
77
|
+
export interface WorkflowResult {
|
|
78
|
+
readonly ok: boolean;
|
|
79
|
+
readonly values: ReadonlyMap<string, unknown>;
|
|
80
|
+
readonly completed: readonly string[];
|
|
81
|
+
readonly error?: string;
|
|
82
|
+
readonly stack?: string;
|
|
83
|
+
}
|
|
84
|
+
/** Bounded orchestration primitives. The kernel knows no provider, UI, or framework package. */
|
|
85
|
+
export declare class WorkflowRunner {
|
|
86
|
+
private readonly options;
|
|
87
|
+
private readonly values;
|
|
88
|
+
private readonly completed;
|
|
89
|
+
private steps;
|
|
90
|
+
constructor(options?: WorkflowRunnerOptions);
|
|
91
|
+
run(step: WorkflowStep): Promise<WorkflowResult>;
|
|
92
|
+
private context;
|
|
93
|
+
private execute;
|
|
94
|
+
private single;
|
|
95
|
+
private parallel;
|
|
96
|
+
private emit;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=workflows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflows.d.ts","sourceRoot":"","sources":["../src/workflows.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7C,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IACvC,GAAG,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC1C;AAED,MAAM,MAAM,YAAY,GACpB;IACE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;CAC3E,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;CAAE,GACtE;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CACjC,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,GAAG,EAAE,CACZ,OAAO,EAAE,eAAe,KACrB,OAAO,GAAG,WAAW,CAAC,OAAO,GAAG;QAAE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CAC1F,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;CAC3E,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;IAC3E,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,YAAY,CAAA;CAClC,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;CAC3E,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;CAC3E,CAAA;AAEL,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;IACrE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;CACrC;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GACnF;IACE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACpF;IAAE,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7C,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,gGAAgG;AAChG,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CACD;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,KAAK,CAAI;gBAEL,OAAO,GAAE,qBAA0B;IAYzC,GAAG,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC;IAwBtD,OAAO,CAAC,OAAO;YASD,OAAO;YA4DP,MAAM;YAuBN,QAAQ;YAsBR,IAAI;CAGnB"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { publicErrorDetails } from "./errors.js";
|
|
2
|
+
/** Bounded orchestration primitives. The kernel knows no provider, UI, or framework package. */
|
|
3
|
+
export class WorkflowRunner {
|
|
4
|
+
options;
|
|
5
|
+
values = new Map();
|
|
6
|
+
completed = [];
|
|
7
|
+
steps = 0;
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.options = {
|
|
10
|
+
...options,
|
|
11
|
+
maxSteps: options.maxSteps ?? 128,
|
|
12
|
+
maxDepth: options.maxDepth ?? 8,
|
|
13
|
+
};
|
|
14
|
+
if (!Number.isSafeInteger(this.options.maxSteps) || this.options.maxSteps < 1)
|
|
15
|
+
throw new RangeError("workflow: maxSteps must be positive");
|
|
16
|
+
if (!Number.isSafeInteger(this.options.maxDepth) || this.options.maxDepth < 1)
|
|
17
|
+
throw new RangeError("workflow: maxDepth must be positive");
|
|
18
|
+
}
|
|
19
|
+
async run(step) {
|
|
20
|
+
try {
|
|
21
|
+
await this.execute(step, 0);
|
|
22
|
+
return {
|
|
23
|
+
ok: true,
|
|
24
|
+
values: new Map(this.values),
|
|
25
|
+
completed: Object.freeze([...this.completed]),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
const details = publicErrorDetails(error, "workflow failed", this.options.exposeErrorStacks === true);
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
values: new Map(this.values),
|
|
33
|
+
completed: Object.freeze([...this.completed]),
|
|
34
|
+
error: details.message,
|
|
35
|
+
...(details.stack === undefined ? {} : { stack: details.stack }),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
context() {
|
|
40
|
+
return {
|
|
41
|
+
signal: this.options.signal ?? new AbortController().signal,
|
|
42
|
+
values: this.values,
|
|
43
|
+
set: (name, value) => this.values.set(name, value),
|
|
44
|
+
run: (step) => this.execute(step, 1),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async execute(step, depth) {
|
|
48
|
+
if (depth > this.options.maxDepth)
|
|
49
|
+
throw new Error("workflow max depth exceeded");
|
|
50
|
+
if (++this.steps > this.options.maxSteps)
|
|
51
|
+
throw new Error("workflow max steps exceeded");
|
|
52
|
+
if (this.options.signal?.aborted)
|
|
53
|
+
throw new Error("workflow cancelled");
|
|
54
|
+
switch (step.type) {
|
|
55
|
+
case "task":
|
|
56
|
+
return this.single(step.id, () => step.run(this.context()));
|
|
57
|
+
case "verify": {
|
|
58
|
+
const result = await this.single(step.id, () => step.run(this.context()));
|
|
59
|
+
const ok = typeof result === "boolean" ? result : isVerificationResult(result) && result.ok;
|
|
60
|
+
if (!ok)
|
|
61
|
+
throw new Error(`verification failed: ${step.id}`);
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
case "approve": {
|
|
65
|
+
await this.emit({ type: "approval.required", id: step.id, reason: step.reason });
|
|
66
|
+
const approved = await this.single(step.id, () => step.run(this.context()));
|
|
67
|
+
if (approved !== true)
|
|
68
|
+
throw new Error(`approval denied: ${step.id}`);
|
|
69
|
+
return approved;
|
|
70
|
+
}
|
|
71
|
+
case "checkpoint": {
|
|
72
|
+
const result = await this.single(step.id, () => step.run(this.context()));
|
|
73
|
+
await this.emit({ type: "checkpoint.created", id: step.id });
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
case "handoff":
|
|
77
|
+
return this.single(step.id, () => step.run(this.context()));
|
|
78
|
+
case "sequence": {
|
|
79
|
+
const results = [];
|
|
80
|
+
for (const child of step.steps)
|
|
81
|
+
results.push(await this.execute(child, depth + 1));
|
|
82
|
+
return results;
|
|
83
|
+
}
|
|
84
|
+
case "parallel":
|
|
85
|
+
return this.parallel(step.steps, step.maxConcurrency ?? step.steps.length, depth);
|
|
86
|
+
case "retry": {
|
|
87
|
+
const attempts = step.attempts ?? 3;
|
|
88
|
+
if (!Number.isSafeInteger(attempts) || attempts < 1 || attempts > 16)
|
|
89
|
+
throw new RangeError("workflow: attempts must be between 1 and 16");
|
|
90
|
+
let last;
|
|
91
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
92
|
+
try {
|
|
93
|
+
return await this.execute(step.step, depth + 1);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
last = error;
|
|
97
|
+
if (attempt + 1 < attempts && (step.backoffMs ?? 0) > 0)
|
|
98
|
+
await delay(Math.min(step.backoffMs * (attempt + 1), 10_000), this.options.signal);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
throw last instanceof Error ? last : new Error(String(last));
|
|
102
|
+
}
|
|
103
|
+
case "branch": {
|
|
104
|
+
const selected = await this.single(step.id, () => step.when(this.context()));
|
|
105
|
+
return selected === true
|
|
106
|
+
? this.execute(step.then, depth + 1)
|
|
107
|
+
: step.otherwise === undefined
|
|
108
|
+
? undefined
|
|
109
|
+
: this.execute(step.otherwise, depth + 1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async single(id, run) {
|
|
114
|
+
await this.emit({ type: "step.started", id });
|
|
115
|
+
try {
|
|
116
|
+
const output = await run();
|
|
117
|
+
this.completed.push(id);
|
|
118
|
+
await this.emit({ type: "step.completed", id, ...(output === undefined ? {} : { output }) });
|
|
119
|
+
return output;
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
const details = publicErrorDetails(error, "workflow step failed", this.options.exposeErrorStacks === true);
|
|
123
|
+
await this.emit({
|
|
124
|
+
type: "step.failed",
|
|
125
|
+
id,
|
|
126
|
+
error: details.message,
|
|
127
|
+
...(details.stack === undefined ? {} : { stack: details.stack }),
|
|
128
|
+
});
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async parallel(steps, maxConcurrency, depth) {
|
|
133
|
+
if (!Number.isSafeInteger(maxConcurrency) || maxConcurrency < 1)
|
|
134
|
+
throw new RangeError("workflow: maxConcurrency must be positive");
|
|
135
|
+
const outputs = new Array(steps.length);
|
|
136
|
+
let next = 0;
|
|
137
|
+
const worker = async () => {
|
|
138
|
+
for (;;) {
|
|
139
|
+
const index = next++;
|
|
140
|
+
if (index >= steps.length)
|
|
141
|
+
return;
|
|
142
|
+
outputs[index] = await this.execute(steps[index], depth + 1);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
await Promise.all(Array.from({ length: Math.min(maxConcurrency, steps.length) }, () => worker()));
|
|
146
|
+
return outputs;
|
|
147
|
+
}
|
|
148
|
+
async emit(event) {
|
|
149
|
+
await this.options.onEvent?.(event);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function delay(ms, signal) {
|
|
153
|
+
await new Promise((resolve, reject) => {
|
|
154
|
+
const timer = setTimeout(resolve, ms);
|
|
155
|
+
if (signal === undefined)
|
|
156
|
+
return;
|
|
157
|
+
if (signal.aborted) {
|
|
158
|
+
clearTimeout(timer);
|
|
159
|
+
reject(new Error("workflow cancelled"));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
signal.addEventListener("abort", () => {
|
|
163
|
+
clearTimeout(timer);
|
|
164
|
+
reject(new Error("workflow cancelled"));
|
|
165
|
+
}, { once: true });
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
function isVerificationResult(value) {
|
|
169
|
+
return (value !== null &&
|
|
170
|
+
typeof value === "object" &&
|
|
171
|
+
!Array.isArray(value) &&
|
|
172
|
+
typeof value.ok === "boolean");
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=workflows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflows.js","sourceRoot":"","sources":["../src/workflows.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nifrajs/coding-agent",
|
|
3
|
+
"version": "3.2.0",
|
|
4
|
+
"description": "Standalone, extensible Nifra coding-agent host and CLI.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"bun",
|
|
7
|
+
"typescript",
|
|
8
|
+
"nifra",
|
|
9
|
+
"agent",
|
|
10
|
+
"coding-agent",
|
|
11
|
+
"cli",
|
|
12
|
+
"workflows"
|
|
13
|
+
],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"module": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"bin": {
|
|
19
|
+
"nifra-agent": "./dist/cli.js"
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"bun": "./src/index.ts",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"default": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./extensions": {
|
|
28
|
+
"bun": "./src/extensions.ts",
|
|
29
|
+
"types": "./dist/extensions.d.ts",
|
|
30
|
+
"default": "./dist/extensions.js"
|
|
31
|
+
},
|
|
32
|
+
"./verification": {
|
|
33
|
+
"bun": "./src/verification.ts",
|
|
34
|
+
"types": "./dist/verification.d.ts",
|
|
35
|
+
"default": "./dist/verification.js"
|
|
36
|
+
},
|
|
37
|
+
"./rpc": {
|
|
38
|
+
"bun": "./src/rpc.ts",
|
|
39
|
+
"types": "./dist/rpc.d.ts",
|
|
40
|
+
"default": "./dist/rpc.js"
|
|
41
|
+
},
|
|
42
|
+
"./orchestration": {
|
|
43
|
+
"bun": "./src/orchestration/index.ts",
|
|
44
|
+
"types": "./dist/orchestration/index.d.ts",
|
|
45
|
+
"default": "./dist/orchestration/index.js"
|
|
46
|
+
},
|
|
47
|
+
"./registry": {
|
|
48
|
+
"bun": "./src/registry.ts",
|
|
49
|
+
"types": "./dist/registry.d.ts",
|
|
50
|
+
"default": "./dist/registry.js"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"dist",
|
|
55
|
+
"src",
|
|
56
|
+
"EXTENSIONS.md",
|
|
57
|
+
"PERFORMANCE.md",
|
|
58
|
+
"REGISTRY.md",
|
|
59
|
+
"SECURITY.md"
|
|
60
|
+
],
|
|
61
|
+
"sideEffects": false,
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsc -p tsconfig.build.json && bun run ../../scripts/fix-dts.ts dist"
|
|
64
|
+
},
|
|
65
|
+
"license": "MIT",
|
|
66
|
+
"publishConfig": {
|
|
67
|
+
"access": "public"
|
|
68
|
+
},
|
|
69
|
+
"dependencies": {
|
|
70
|
+
"@nifrajs/agent": "3.2.0",
|
|
71
|
+
"@nifrajs/agent-protocol": "3.2.0",
|
|
72
|
+
"@nifrajs/jobs": "3.2.0",
|
|
73
|
+
"@nifrajs/pi": "3.2.0"
|
|
74
|
+
},
|
|
75
|
+
"repository": {
|
|
76
|
+
"type": "git",
|
|
77
|
+
"url": "git+https://github.com/nifrajs/nifra.git",
|
|
78
|
+
"directory": "packages/coding-agent"
|
|
79
|
+
},
|
|
80
|
+
"bugs": {
|
|
81
|
+
"url": "https://github.com/nifrajs/nifra/issues"
|
|
82
|
+
},
|
|
83
|
+
"homepage": "https://github.com/nifrajs/nifra#readme"
|
|
84
|
+
}
|
package/src/approvals.ts
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AgentApprovalRequiredEvent,
|
|
3
|
+
type AgentApprovalResolvedEvent,
|
|
4
|
+
coordinateIsFresh,
|
|
5
|
+
coordinatesMatch,
|
|
6
|
+
type DecisionCoordinate,
|
|
7
|
+
nextApprovalState,
|
|
8
|
+
} from "@nifrajs/agent-protocol"
|
|
9
|
+
|
|
10
|
+
export interface ApprovalRequest {
|
|
11
|
+
readonly id: string
|
|
12
|
+
readonly sessionId: string
|
|
13
|
+
readonly turnId?: string
|
|
14
|
+
readonly action: string
|
|
15
|
+
readonly capability: string
|
|
16
|
+
readonly reason?: string
|
|
17
|
+
/**
|
|
18
|
+
* Optional decision-boundary coordinate. When present, {@link ApprovalManager.resolveMatched}
|
|
19
|
+
* admits a decision only if it names this exact run, node, capability, request id, and vector, and
|
|
20
|
+
* arrives before expiry. Coordinate-less approvals keep their original untyped {@link resolve} path.
|
|
21
|
+
*/
|
|
22
|
+
readonly coordinate?: DecisionCoordinate
|
|
23
|
+
readonly createdAt: number
|
|
24
|
+
readonly expiresAt: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Stable, content-free reasons a coordinate-matched resolution is refused. */
|
|
28
|
+
export type ApprovalMatchRejection =
|
|
29
|
+
| "unknown_boundary"
|
|
30
|
+
| "identity_mismatch"
|
|
31
|
+
| "stale_vector"
|
|
32
|
+
| "expired"
|
|
33
|
+
| "illegal_transition"
|
|
34
|
+
|
|
35
|
+
export type ApprovalMatchResult =
|
|
36
|
+
| { readonly ok: true; readonly decision: ApprovalDecision }
|
|
37
|
+
| { readonly ok: false; readonly code: ApprovalMatchRejection }
|
|
38
|
+
|
|
39
|
+
export interface ApprovalDecision {
|
|
40
|
+
readonly approvalId: string
|
|
41
|
+
readonly approved: boolean
|
|
42
|
+
readonly reason?: string
|
|
43
|
+
readonly at: number
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ApprovalManagerOptions {
|
|
47
|
+
readonly maxPending?: number
|
|
48
|
+
readonly timeoutMs?: number
|
|
49
|
+
readonly onRequired?: (request: ApprovalRequest) => void | PromiseLike<void>
|
|
50
|
+
readonly onResolved?: (decision: ApprovalDecision) => void | PromiseLike<void>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface PendingApproval {
|
|
54
|
+
readonly request: ApprovalRequest
|
|
55
|
+
readonly resolve: (approved: boolean) => void
|
|
56
|
+
readonly timer: ReturnType<typeof setTimeout>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Small approval broker shared by RPC, Workbench, and workflow extensions.
|
|
61
|
+
*
|
|
62
|
+
* Approval state is deliberately bounded and expires closed (denied) by default. It is a policy
|
|
63
|
+
* seam, not a security boundary: filesystem/process isolation still belongs to the host or OS.
|
|
64
|
+
*/
|
|
65
|
+
export class ApprovalManager {
|
|
66
|
+
private readonly options: Required<Pick<ApprovalManagerOptions, "maxPending" | "timeoutMs">> &
|
|
67
|
+
ApprovalManagerOptions
|
|
68
|
+
private readonly pendingApprovals = new Map<string, PendingApproval>()
|
|
69
|
+
|
|
70
|
+
constructor(options: ApprovalManagerOptions = {}) {
|
|
71
|
+
this.options = {
|
|
72
|
+
...options,
|
|
73
|
+
maxPending: options.maxPending ?? 32,
|
|
74
|
+
timeoutMs: options.timeoutMs ?? 5 * 60_000,
|
|
75
|
+
}
|
|
76
|
+
if (!Number.isSafeInteger(this.options.maxPending) || this.options.maxPending < 1)
|
|
77
|
+
throw new RangeError("approvals: maxPending must be positive")
|
|
78
|
+
if (
|
|
79
|
+
!Number.isSafeInteger(this.options.timeoutMs) ||
|
|
80
|
+
this.options.timeoutMs < 1 ||
|
|
81
|
+
this.options.timeoutMs > 24 * 60 * 60_000
|
|
82
|
+
)
|
|
83
|
+
throw new RangeError("approvals: timeoutMs must be between 1ms and 24h")
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get pending(): readonly ApprovalRequest[] {
|
|
87
|
+
return Object.freeze([...this.pendingApprovals.values()].map(({ request }) => request))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Convert a streamed backend approval event into a resolvable pending request. */
|
|
91
|
+
async observe(event: AgentApprovalRequiredEvent): Promise<ApprovalRequest | undefined> {
|
|
92
|
+
if (this.pendingApprovals.has(event.approvalId))
|
|
93
|
+
return this.pendingApprovals.get(event.approvalId)?.request
|
|
94
|
+
return this.create({
|
|
95
|
+
id: event.approvalId,
|
|
96
|
+
sessionId: event.sessionId,
|
|
97
|
+
turnId: event.turnId,
|
|
98
|
+
action: event.action,
|
|
99
|
+
capability: event.capability,
|
|
100
|
+
...(event.reason === undefined ? {} : { reason: event.reason }),
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Publish a pending approval without waiting for its decision (useful for transports). */
|
|
105
|
+
async offer(
|
|
106
|
+
input: Omit<ApprovalRequest, "createdAt" | "expiresAt">,
|
|
107
|
+
): Promise<ApprovalRequest | undefined> {
|
|
108
|
+
return this.create(input)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Create a host-owned approval that can be awaited by a workflow or extension. */
|
|
112
|
+
async request(input: Omit<ApprovalRequest, "createdAt" | "expiresAt">): Promise<boolean> {
|
|
113
|
+
const request = await this.offer(input)
|
|
114
|
+
if (request === undefined) return false
|
|
115
|
+
const pending = this.pendingApprovals.get(request.id)
|
|
116
|
+
if (pending === undefined) return false
|
|
117
|
+
return new Promise<boolean>((resolve) => {
|
|
118
|
+
// Replace the resolver only for this host-owned request; observed backend requests are
|
|
119
|
+
// intentionally notification-only and are resolved through `resolve`.
|
|
120
|
+
const current = this.pendingApprovals.get(request.id)
|
|
121
|
+
if (current === undefined) return resolve(false)
|
|
122
|
+
this.pendingApprovals.set(request.id, { ...current, resolve })
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Resolve an approval that was opened with a {@link DecisionCoordinate}, admitting the decision only
|
|
128
|
+
* when it matches the live boundary and is still fresh. Every failure is content-free and fails
|
|
129
|
+
* closed: an unknown id, a coordinate-less approval, a mismatched or superseded coordinate, or an
|
|
130
|
+
* expired boundary resumes no work. This adds a typed path alongside the untyped {@link resolve}; it
|
|
131
|
+
* never loosens it.
|
|
132
|
+
*/
|
|
133
|
+
resolveMatched(
|
|
134
|
+
coordinate: DecisionCoordinate,
|
|
135
|
+
approved: boolean,
|
|
136
|
+
now: number = Date.now(),
|
|
137
|
+
): ApprovalMatchResult {
|
|
138
|
+
const pending = this.pendingApprovals.get(coordinate.requestId)
|
|
139
|
+
if (pending === undefined) return { ok: false, code: "unknown_boundary" }
|
|
140
|
+
const bound = pending.request.coordinate
|
|
141
|
+
if (bound === undefined) return { ok: false, code: "identity_mismatch" }
|
|
142
|
+
if (!coordinatesMatch(bound, coordinate))
|
|
143
|
+
return {
|
|
144
|
+
ok: false,
|
|
145
|
+
code: bound.vector !== coordinate.vector ? "stale_vector" : "identity_mismatch",
|
|
146
|
+
}
|
|
147
|
+
if (!coordinateIsFresh(bound, now)) {
|
|
148
|
+
this.resolve(coordinate.requestId, false, "approval expired")
|
|
149
|
+
return { ok: false, code: "expired" }
|
|
150
|
+
}
|
|
151
|
+
if (nextApprovalState("pending", approved ? "approve" : "deny") === undefined)
|
|
152
|
+
return { ok: false, code: "illegal_transition" }
|
|
153
|
+
const decision = this.resolve(coordinate.requestId, approved)
|
|
154
|
+
if (decision === undefined) return { ok: false, code: "unknown_boundary" }
|
|
155
|
+
return { ok: true, decision }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
resolve(approvalId: string, approved: boolean, reason?: string): ApprovalDecision | undefined {
|
|
159
|
+
const pending = this.pendingApprovals.get(approvalId)
|
|
160
|
+
if (pending === undefined) return undefined
|
|
161
|
+
clearTimeout(pending.timer)
|
|
162
|
+
this.pendingApprovals.delete(approvalId)
|
|
163
|
+
pending.resolve(approved === true)
|
|
164
|
+
const decision: ApprovalDecision = {
|
|
165
|
+
approvalId,
|
|
166
|
+
approved: approved === true,
|
|
167
|
+
...(reason === undefined ? {} : { reason: reason.slice(0, 512) }),
|
|
168
|
+
at: Date.now(),
|
|
169
|
+
}
|
|
170
|
+
void this.options.onResolved?.(decision)
|
|
171
|
+
return decision
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
close(): void {
|
|
175
|
+
for (const approvalId of this.pendingApprovals.keys())
|
|
176
|
+
this.resolve(approvalId, false, "approval manager closed")
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private async create(
|
|
180
|
+
input: Omit<ApprovalRequest, "createdAt" | "expiresAt">,
|
|
181
|
+
): Promise<ApprovalRequest | undefined> {
|
|
182
|
+
if (this.pendingApprovals.size >= this.options.maxPending) return undefined
|
|
183
|
+
if (!/^[A-Za-z0-9._:-]{1,128}$/.test(input.id))
|
|
184
|
+
throw new TypeError("approvals: approval id is invalid")
|
|
185
|
+
if (!/^[A-Za-z0-9._:-]{1,128}$/.test(input.sessionId))
|
|
186
|
+
throw new TypeError("approvals: session id is invalid")
|
|
187
|
+
if (!input.action || input.action.length > 512)
|
|
188
|
+
throw new TypeError("approvals: action is empty or too long")
|
|
189
|
+
if (!input.capability || input.capability.length > 128)
|
|
190
|
+
throw new TypeError("approvals: capability is empty or too long")
|
|
191
|
+
const createdAt = Date.now()
|
|
192
|
+
const request: ApprovalRequest = Object.freeze({
|
|
193
|
+
...input,
|
|
194
|
+
...(input.turnId === undefined ? {} : { turnId: input.turnId }),
|
|
195
|
+
...(input.reason === undefined ? {} : { reason: input.reason.slice(0, 512) }),
|
|
196
|
+
...(input.coordinate === undefined ? {} : { coordinate: input.coordinate }),
|
|
197
|
+
createdAt,
|
|
198
|
+
expiresAt: createdAt + this.options.timeoutMs,
|
|
199
|
+
})
|
|
200
|
+
let resolveApproval = (_approved: boolean): void => {}
|
|
201
|
+
const timer = setTimeout(() => {
|
|
202
|
+
this.resolve(request.id, false, "approval timed out")
|
|
203
|
+
}, this.options.timeoutMs)
|
|
204
|
+
;(timer as unknown as { unref?: () => void }).unref?.()
|
|
205
|
+
const pending: PendingApproval = {
|
|
206
|
+
request,
|
|
207
|
+
resolve: (approved) => resolveApproval(approved),
|
|
208
|
+
timer,
|
|
209
|
+
}
|
|
210
|
+
this.pendingApprovals.set(request.id, pending)
|
|
211
|
+
await this.options.onRequired?.(request)
|
|
212
|
+
// A backend-observed request is resolved through `resolve`; a host request swaps this closure
|
|
213
|
+
// from the promise below before returning to the workflow.
|
|
214
|
+
resolveApproval = (_approved: boolean): void => {}
|
|
215
|
+
return request
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function approvalRequestFromEvent(event: AgentApprovalRequiredEvent): ApprovalRequest {
|
|
220
|
+
return Object.freeze({
|
|
221
|
+
id: event.approvalId,
|
|
222
|
+
sessionId: event.sessionId,
|
|
223
|
+
turnId: event.turnId,
|
|
224
|
+
action: event.action,
|
|
225
|
+
capability: event.capability,
|
|
226
|
+
...(event.reason === undefined ? {} : { reason: event.reason }),
|
|
227
|
+
createdAt: event.at,
|
|
228
|
+
expiresAt: event.at + 5 * 60_000,
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function approvalResolvedEvent(
|
|
233
|
+
sessionId: string,
|
|
234
|
+
seq: number,
|
|
235
|
+
decision: ApprovalDecision,
|
|
236
|
+
turnId?: string,
|
|
237
|
+
): AgentApprovalResolvedEvent {
|
|
238
|
+
return Object.freeze({
|
|
239
|
+
version: 1 as const,
|
|
240
|
+
sessionId,
|
|
241
|
+
seq,
|
|
242
|
+
at: decision.at,
|
|
243
|
+
type: "approval.resolved" as const,
|
|
244
|
+
...(turnId === undefined ? {} : { turnId }),
|
|
245
|
+
approvalId: decision.approvalId,
|
|
246
|
+
approved: decision.approved,
|
|
247
|
+
...(decision.reason === undefined ? {} : { reason: decision.reason }),
|
|
248
|
+
})
|
|
249
|
+
}
|