@warlock.js/ai 4.5.0 → 4.6.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/CHANGELOG.md +14 -0
- package/cjs/index.cjs +20 -1
- package/cjs/{src-DFibP2FQ.cjs → src-Bmajk4Qg.cjs} +1 -1
- package/cjs/{src-C02yzsLs.cjs → src-OZyDYHxm.cjs} +2789 -691
- package/cjs/src-OZyDYHxm.cjs.map +1 -0
- package/esm/agent/agent-config.type.d.mts +29 -0
- package/esm/agent/agent-config.type.d.mts.map +1 -1
- package/esm/agent/agent.d.mts.map +1 -1
- package/esm/agent/agent.mjs +126 -7
- package/esm/agent/agent.mjs.map +1 -1
- package/esm/agent/signature.mjs +57 -0
- package/esm/agent/signature.mjs.map +1 -0
- package/esm/agent/snapshot.mjs +101 -0
- package/esm/agent/snapshot.mjs.map +1 -0
- package/esm/ai-openai/src/image.mjs +5 -0
- package/esm/ai-openai/src/index.mjs +3 -0
- package/esm/ai-openai/src/sdk.mjs +3 -0
- package/esm/ai-openai/src/speech.mjs +5 -0
- package/esm/ai-openai/src/transcription.mjs +6 -0
- package/esm/ai-openai/src/utils/index.mjs +1 -0
- package/esm/ai-openai/src/utils/to-openai-messages.mjs +3 -0
- package/esm/ai.d.mts +45 -0
- package/esm/ai.d.mts.map +1 -1
- package/esm/ai.mjs +37 -1
- package/esm/ai.mjs.map +1 -1
- package/esm/contracts/agent/agent-options.type.d.mts +22 -2
- package/esm/contracts/agent/agent-options.type.d.mts.map +1 -1
- package/esm/contracts/agent/agent-snapshot.type.d.mts +90 -0
- package/esm/contracts/agent/agent-snapshot.type.d.mts.map +1 -0
- package/esm/contracts/agent/agent.contract.d.mts +29 -1
- package/esm/contracts/agent/agent.contract.d.mts.map +1 -1
- package/esm/contracts/agent/index.d.mts +2 -1
- package/esm/contracts/image-model.contract.d.mts +156 -0
- package/esm/contracts/image-model.contract.d.mts.map +1 -0
- package/esm/contracts/index.d.mts +8 -3
- package/esm/contracts/planner/index.d.mts +3 -2
- package/esm/contracts/planner/planner-config.type.d.mts +30 -0
- package/esm/contracts/planner/planner-config.type.d.mts.map +1 -1
- package/esm/contracts/planner/planner-execute-options.type.d.mts +13 -1
- package/esm/contracts/planner/planner-execute-options.type.d.mts.map +1 -1
- package/esm/contracts/planner/planner-snapshot.type.d.mts +77 -0
- package/esm/contracts/planner/planner-snapshot.type.d.mts.map +1 -0
- package/esm/contracts/planner/planner.contract.d.mts +21 -1
- package/esm/contracts/planner/planner.contract.d.mts.map +1 -1
- package/esm/contracts/result/base-report.type.d.mts +1 -1
- package/esm/contracts/result/base-report.type.d.mts.map +1 -1
- package/esm/contracts/result/base-report.type.mjs.map +1 -1
- package/esm/contracts/sdk-adapter.contract.d.mts +37 -0
- package/esm/contracts/sdk-adapter.contract.d.mts.map +1 -1
- package/esm/contracts/speech-model.contract.d.mts +97 -0
- package/esm/contracts/speech-model.contract.d.mts.map +1 -0
- package/esm/contracts/transcription-model.contract.d.mts +101 -0
- package/esm/contracts/transcription-model.contract.d.mts.map +1 -0
- package/esm/errors/agent-drift-error.d.mts +32 -0
- package/esm/errors/agent-drift-error.d.mts.map +1 -0
- package/esm/errors/agent-drift-error.mjs +31 -0
- package/esm/errors/agent-drift-error.mjs.map +1 -0
- package/esm/errors/error-code.type.d.mts +1 -1
- package/esm/errors/index.d.mts +2 -0
- package/esm/errors/index.mjs +2 -0
- package/esm/errors/planner-drift-error.d.mts +34 -0
- package/esm/errors/planner-drift-error.d.mts.map +1 -0
- package/esm/errors/planner-drift-error.mjs +33 -0
- package/esm/errors/planner-drift-error.mjs.map +1 -0
- package/esm/image/image-cost.d.mts +32 -0
- package/esm/image/image-cost.d.mts.map +1 -0
- package/esm/image/image-cost.mjs +55 -0
- package/esm/image/image-cost.mjs.map +1 -0
- package/esm/image/image.d.mts +92 -0
- package/esm/image/image.d.mts.map +1 -0
- package/esm/image/image.mjs +113 -0
- package/esm/image/image.mjs.map +1 -0
- package/esm/image/index.mjs +4 -0
- package/esm/index.d.mts +26 -4
- package/esm/index.mjs +20 -1
- package/esm/mock/index.d.mts +3 -0
- package/esm/mock/index.mjs +3 -0
- package/esm/mock/mock-config.type.d.mts +22 -0
- package/esm/mock/mock-config.type.d.mts.map +1 -1
- package/esm/mock/mock-image-model.d.mts +41 -0
- package/esm/mock/mock-image-model.d.mts.map +1 -0
- package/esm/mock/mock-image-model.mjs +52 -0
- package/esm/mock/mock-image-model.mjs.map +1 -0
- package/esm/mock/mock-sdk.d.mts +7 -1
- package/esm/mock/mock-sdk.d.mts.map +1 -1
- package/esm/mock/mock-sdk.mjs +27 -0
- package/esm/mock/mock-sdk.mjs.map +1 -1
- package/esm/mock/mock-speech-model.d.mts +31 -0
- package/esm/mock/mock-speech-model.d.mts.map +1 -0
- package/esm/mock/mock-speech-model.mjs +39 -0
- package/esm/mock/mock-speech-model.mjs.map +1 -0
- package/esm/mock/mock-transcription-model.d.mts +32 -0
- package/esm/mock/mock-transcription-model.d.mts.map +1 -0
- package/esm/mock/mock-transcription-model.mjs +36 -0
- package/esm/mock/mock-transcription-model.mjs.map +1 -0
- package/esm/planner/planner-run.d.mts +8 -0
- package/esm/planner/planner-run.d.mts.map +1 -1
- package/esm/planner/planner-run.mjs +161 -6
- package/esm/planner/planner-run.mjs.map +1 -1
- package/esm/planner/planner.d.mts.map +1 -1
- package/esm/planner/planner.mjs +25 -1
- package/esm/planner/planner.mjs.map +1 -1
- package/esm/planner/snapshot.mjs +95 -0
- package/esm/planner/snapshot.mjs.map +1 -0
- package/esm/rag/index.d.mts +7 -0
- package/esm/rag/index.mjs +7 -0
- package/esm/rag/loaders/errors.d.mts +19 -0
- package/esm/rag/loaders/errors.d.mts.map +1 -0
- package/esm/rag/loaders/errors.mjs +25 -0
- package/esm/rag/loaders/errors.mjs.map +1 -0
- package/esm/rag/loaders/index.mjs +7 -0
- package/esm/rag/loaders/load-html.d.mts +26 -0
- package/esm/rag/loaders/load-html.d.mts.map +1 -0
- package/esm/rag/loaders/load-html.mjs +138 -0
- package/esm/rag/loaders/load-html.mjs.map +1 -0
- package/esm/rag/loaders/load-pdf.d.mts +38 -0
- package/esm/rag/loaders/load-pdf.d.mts.map +1 -0
- package/esm/rag/loaders/load-pdf.mjs +150 -0
- package/esm/rag/loaders/load-pdf.mjs.map +1 -0
- package/esm/rag/loaders/load-text.d.mts +47 -0
- package/esm/rag/loaders/load-text.d.mts.map +1 -0
- package/esm/rag/loaders/load-text.mjs +60 -0
- package/esm/rag/loaders/load-text.mjs.map +1 -0
- package/esm/rag/loaders/load-web.d.mts +42 -0
- package/esm/rag/loaders/load-web.d.mts.map +1 -0
- package/esm/rag/loaders/load-web.mjs +89 -0
- package/esm/rag/loaders/load-web.mjs.map +1 -0
- package/esm/rag/loaders/loader.type.d.mts +89 -0
- package/esm/rag/loaders/loader.type.d.mts.map +1 -0
- package/esm/rag/store/pg-vector-store.d.mts +139 -0
- package/esm/rag/store/pg-vector-store.d.mts.map +1 -0
- package/esm/rag/store/pg-vector-store.mjs +328 -0
- package/esm/rag/store/pg-vector-store.mjs.map +1 -0
- package/esm/speech/index.mjs +3 -0
- package/esm/speech/speech.d.mts +65 -0
- package/esm/speech/speech.d.mts.map +1 -0
- package/esm/speech/speech.mjs +123 -0
- package/esm/speech/speech.mjs.map +1 -0
- package/esm/supervisor/entries.mjs +2 -2
- package/esm/supervisor/entries.mjs.map +1 -1
- package/esm/transcribe/audio-input.d.mts +47 -0
- package/esm/transcribe/audio-input.d.mts.map +1 -0
- package/esm/transcribe/audio-input.mjs +84 -0
- package/esm/transcribe/audio-input.mjs.map +1 -0
- package/esm/transcribe/index.mjs +4 -0
- package/esm/transcribe/transcribe.d.mts +64 -0
- package/esm/transcribe/transcribe.d.mts.map +1 -0
- package/esm/transcribe/transcribe.mjs +128 -0
- package/esm/transcribe/transcribe.mjs.map +1 -0
- package/llms-full.txt +753 -0
- package/llms.txt +5 -0
- package/package.json +3 -3
- package/skills/README.md +4 -0
- package/skills/durable-agent-runs/SKILL.md +135 -0
- package/skills/generate-images/SKILL.md +138 -0
- package/skills/generate-speech/SKILL.md +139 -0
- package/skills/rag-loaders-and-stores/SKILL.md +164 -0
- package/skills/transcribe-audio/SKILL.md +157 -0
- package/cjs/src-C02yzsLs.cjs.map +0 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Usage } from "../contracts/result/usage.type.mjs";
|
|
2
|
+
import { AudioInput, TranscribeOptions, TranscriptionModelContract, TranscriptionModelPricing, TranscriptionResponse, TranscriptionSegment } from "../contracts/transcription-model.contract.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai/src/mock/mock-transcription-model.d.ts
|
|
5
|
+
/** One scripted response for a {@link MockTranscriptionModel}. */
|
|
6
|
+
type MockTranscriptionResponse = {
|
|
7
|
+
text?: string;
|
|
8
|
+
segments?: TranscriptionSegment[];
|
|
9
|
+
durationSeconds?: number;
|
|
10
|
+
usage?: Usage;
|
|
11
|
+
error?: Error;
|
|
12
|
+
delay?: number;
|
|
13
|
+
};
|
|
14
|
+
/** One recorded `transcribe()` invocation, for test assertions. */
|
|
15
|
+
type MockTranscriptionCall = {
|
|
16
|
+
audio: AudioInput;
|
|
17
|
+
options: TranscribeOptions | undefined;
|
|
18
|
+
};
|
|
19
|
+
/** Deterministic {@link TranscriptionModelContract} double for tests — no HTTP. */
|
|
20
|
+
declare class MockTranscriptionModel implements TranscriptionModelContract {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
private readonly responses;
|
|
23
|
+
readonly pricing?: TranscriptionModelPricing | undefined;
|
|
24
|
+
readonly provider = "mock";
|
|
25
|
+
readonly calls: MockTranscriptionCall[];
|
|
26
|
+
private callIndex;
|
|
27
|
+
constructor(name: string, responses: MockTranscriptionResponse[], pricing?: TranscriptionModelPricing | undefined);
|
|
28
|
+
transcribe(audio: AudioInput, options?: TranscribeOptions): Promise<TranscriptionResponse>;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { MockTranscriptionCall, MockTranscriptionModel, MockTranscriptionResponse };
|
|
32
|
+
//# sourceMappingURL=mock-transcription-model.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-transcription-model.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai/src/mock/mock-transcription-model.ts"],"mappings":";;;;;KAWY,yBAAA;EACV,IAAA;EACA,QAAA,GAAW,oBAAA;EACX,eAAA;EACA,KAAA,GAAQ,KAAA;EACR,KAAA,GAAQ,KAAA;EACR,KAAA;AAAA;;KAIU,qBAAA;EAA0B,KAAA,EAAO,UAAA;EAAY,OAAA,EAAS,iBAAiB;AAAA;;cAGtE,sBAAA,YAAkC,0BAAA;EAAA,SAO3B,IAAA;EAAA,iBACC,SAAA;EAAA,SACD,OAAA,GAAU,yBAAA;EAAA,SARZ,QAAA;EAAA,SACA,KAAA,EAAO,qBAAA;EAAA,QAEf,SAAA;cAGU,IAAA,UACC,SAAA,EAAW,yBAAA,IACZ,OAAA,GAAU,yBAAA;EAGf,UAAA,CACX,KAAA,EAAO,UAAA,EACP,OAAA,GAAU,iBAAA,GACT,OAAA,CAAQ,qBAAA;AAAA"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region ../@warlock.js/ai/src/mock/mock-transcription-model.ts
|
|
2
|
+
/** Deterministic {@link TranscriptionModelContract} double for tests — no HTTP. */
|
|
3
|
+
var MockTranscriptionModel = class {
|
|
4
|
+
constructor(name, responses, pricing) {
|
|
5
|
+
this.name = name;
|
|
6
|
+
this.responses = responses;
|
|
7
|
+
this.pricing = pricing;
|
|
8
|
+
this.provider = "mock";
|
|
9
|
+
this.calls = [];
|
|
10
|
+
this.callIndex = 0;
|
|
11
|
+
}
|
|
12
|
+
async transcribe(audio, options) {
|
|
13
|
+
this.calls.push({
|
|
14
|
+
audio,
|
|
15
|
+
options
|
|
16
|
+
});
|
|
17
|
+
const response = this.responses[Math.min(this.callIndex, this.responses.length - 1)] ?? {};
|
|
18
|
+
this.callIndex += 1;
|
|
19
|
+
if (response.delay) await new Promise((resolve) => setTimeout(resolve, response.delay));
|
|
20
|
+
if (response.error) throw response.error;
|
|
21
|
+
return {
|
|
22
|
+
text: response.text ?? "mock transcript",
|
|
23
|
+
...response.segments ? { segments: response.segments } : {},
|
|
24
|
+
...response.durationSeconds !== void 0 ? { durationSeconds: response.durationSeconds } : {},
|
|
25
|
+
usage: response.usage ?? {
|
|
26
|
+
input: 0,
|
|
27
|
+
output: 0,
|
|
28
|
+
total: 0
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
export { MockTranscriptionModel };
|
|
36
|
+
//# sourceMappingURL=mock-transcription-model.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-transcription-model.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai/src/mock/mock-transcription-model.ts"],"sourcesContent":["import type { Usage } from \"../contracts/result/usage.type\";\nimport type {\n AudioInput,\n TranscribeOptions,\n TranscriptionModelContract,\n TranscriptionModelPricing,\n TranscriptionResponse,\n TranscriptionSegment,\n} from \"../contracts/transcription-model.contract\";\n\n/** One scripted response for a {@link MockTranscriptionModel}. */\nexport type MockTranscriptionResponse = {\n text?: string;\n segments?: TranscriptionSegment[];\n durationSeconds?: number;\n usage?: Usage;\n error?: Error;\n delay?: number;\n};\n\n/** One recorded `transcribe()` invocation, for test assertions. */\nexport type MockTranscriptionCall = { audio: AudioInput; options: TranscribeOptions | undefined };\n\n/** Deterministic {@link TranscriptionModelContract} double for tests — no HTTP. */\nexport class MockTranscriptionModel implements TranscriptionModelContract {\n public readonly provider = \"mock\";\n public readonly calls: MockTranscriptionCall[] = [];\n\n private callIndex = 0;\n\n public constructor(\n public readonly name: string,\n private readonly responses: MockTranscriptionResponse[],\n public readonly pricing?: TranscriptionModelPricing,\n ) {}\n\n public async transcribe(\n audio: AudioInput,\n options?: TranscribeOptions,\n ): Promise<TranscriptionResponse> {\n this.calls.push({ audio, options });\n\n const response = this.responses[Math.min(this.callIndex, this.responses.length - 1)] ?? {};\n this.callIndex += 1;\n\n if (response.delay) {\n await new Promise((resolve) => setTimeout(resolve, response.delay));\n }\n if (response.error) {\n throw response.error;\n }\n\n return {\n text: response.text ?? \"mock transcript\",\n ...(response.segments ? { segments: response.segments } : {}),\n ...(response.durationSeconds !== undefined\n ? { durationSeconds: response.durationSeconds }\n : {}),\n usage: response.usage ?? { input: 0, output: 0, total: 0 },\n };\n }\n}\n"],"mappings":";;AAwBA,IAAa,yBAAb,MAA0E;CAMxE,AAAO,YACL,AAAgB,MAChB,AAAiB,WACjB,AAAgB,SAChB;EAHgB;EACC;EACD;kBARS;eACsB,CAAC;mBAE9B;CAMjB;CAEH,MAAa,WACX,OACA,SACgC;EAChC,KAAK,MAAM,KAAK;GAAE;GAAO;EAAQ,CAAC;EAElC,MAAM,WAAW,KAAK,UAAU,KAAK,IAAI,KAAK,WAAW,KAAK,UAAU,SAAS,CAAC,MAAM,CAAC;EACzF,KAAK,aAAa;EAElB,IAAI,SAAS,OACX,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,SAAS,KAAK,CAAC;EAEpE,IAAI,SAAS,OACX,MAAM,SAAS;EAGjB,OAAO;GACL,MAAM,SAAS,QAAQ;GACvB,GAAI,SAAS,WAAW,EAAE,UAAU,SAAS,SAAS,IAAI,CAAC;GAC3D,GAAI,SAAS,oBAAoB,SAC7B,EAAE,iBAAiB,SAAS,gBAAgB,IAC5C,CAAC;GACL,OAAO,SAAS,SAAS;IAAE,OAAO;IAAG,QAAQ;IAAG,OAAO;GAAE;EAC3D;CACF;AACF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AgentContract } from "../contracts/agent/agent.contract.mjs";
|
|
2
2
|
import { PlannerCapability } from "../contracts/planner/planner-capability.type.mjs";
|
|
3
|
+
import { PlannerSnapshot } from "../contracts/planner/planner-snapshot.type.mjs";
|
|
3
4
|
import { PlannerConfig } from "../contracts/planner/planner-config.type.mjs";
|
|
4
5
|
import { PlannerExecuteOptions } from "../contracts/planner/planner-execute-options.type.mjs";
|
|
5
6
|
|
|
@@ -17,6 +18,13 @@ type PlannerRunArgs<TOutput> = {
|
|
|
17
18
|
planningAgent: AgentContract<unknown>;
|
|
18
19
|
goal: string;
|
|
19
20
|
options?: PlannerExecuteOptions<TOutput>;
|
|
21
|
+
/**
|
|
22
|
+
* Durable resume seed. When present the run re-hydrates the frozen plan
|
|
23
|
+
* + executed-node ledger + usage + child reports + replan budget from a
|
|
24
|
+
* prior crash, skips plan generation, and continues scheduling only the
|
|
25
|
+
* unfinished frontier. Absent ⇒ a normal cold run.
|
|
26
|
+
*/
|
|
27
|
+
resumeFrom?: PlannerSnapshot;
|
|
20
28
|
};
|
|
21
29
|
//#endregion
|
|
22
30
|
export { PlannerRunArgs };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-run.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai/src/planner/planner-run.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"planner-run.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai/src/planner/planner-run.ts"],"mappings":";;;;;;;;AA8CA;;;;KAAY,cAAA;EACV,MAAA,EAAQ,aAAA,CAAc,OAAA;EACtB,YAAA,EAAc,GAAA,SAAY,iBAAA;EAC1B,QAAA;EACA,SAAA;EACA,aAAA,EAAe,aAAA;EACf,IAAA;EACA,OAAA,GAAU,qBAAA,CAAsB,OAAA;EAOJ;;;;;;EAA5B,UAAA,GAAa,eAAA;AAAA"}
|
|
@@ -11,6 +11,8 @@ import { stampReportLineage } from "../utils/stamp-report-lineage.mjs";
|
|
|
11
11
|
import { notifyObservers } from "../observe/resolve-observers.mjs";
|
|
12
12
|
import { buildDag, readyNodes, sinkNodes } from "./dag-scheduler.mjs";
|
|
13
13
|
import { planSchema } from "./plan-schema.mjs";
|
|
14
|
+
import { deletePlannerSnapshot, persistPlannerSnapshot } from "./snapshot.mjs";
|
|
15
|
+
import { log } from "@warlock.js/logger";
|
|
14
16
|
|
|
15
17
|
//#region ../@warlock.js/ai/src/planner/planner-run.ts
|
|
16
18
|
/**
|
|
@@ -45,7 +47,16 @@ var PlannerRun = class {
|
|
|
45
47
|
this.executedSteps = [];
|
|
46
48
|
this.awaitingApproval = false;
|
|
47
49
|
this.replanCount = 0;
|
|
48
|
-
this.
|
|
50
|
+
this.dagResumeConsumed = false;
|
|
51
|
+
this.runId = args.resumeFrom?.runId ?? args.options?.runId ?? generateRunId("planner");
|
|
52
|
+
if (args.resumeFrom) {
|
|
53
|
+
this.plan = args.resumeFrom.plan;
|
|
54
|
+
this.executedSteps.push(...args.resumeFrom.executedSteps);
|
|
55
|
+
this.children.push(...args.resumeFrom.children);
|
|
56
|
+
this.mergeUsage(this.usage, args.resumeFrom.usage);
|
|
57
|
+
this.replanCount = args.resumeFrom.replanCount;
|
|
58
|
+
this.startedAt = args.resumeFrom.startedAt;
|
|
59
|
+
}
|
|
49
60
|
}
|
|
50
61
|
/**
|
|
51
62
|
* Run the planner end-to-end. Never throws on runtime failure —
|
|
@@ -65,17 +76,30 @@ var PlannerRun = class {
|
|
|
65
76
|
* unified tree is emitted exactly once.
|
|
66
77
|
*/
|
|
67
78
|
async runPlan() {
|
|
79
|
+
if (this.args.resumeFrom && this.args.resumeFrom.status === "completed") {
|
|
80
|
+
this.rebuildResumedTerminal("completed");
|
|
81
|
+
return this.buildResult();
|
|
82
|
+
}
|
|
68
83
|
try {
|
|
69
84
|
if (this.isAborted()) {
|
|
70
85
|
this.markCancelled();
|
|
86
|
+
await this.checkpoint(this.resolveSnapshotStatus());
|
|
87
|
+
return this.buildResult();
|
|
88
|
+
}
|
|
89
|
+
const plan = this.args.resumeFrom ? this.plan : this.args.options?.approvedPlan ?? await this.generatePlan();
|
|
90
|
+
if (this.error || !plan) {
|
|
91
|
+
await this.checkpoint(this.resolveSnapshotStatus());
|
|
71
92
|
return this.buildResult();
|
|
72
93
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
94
|
+
if (!this.args.resumeFrom) {
|
|
95
|
+
this.assertPlanValid(plan);
|
|
96
|
+
if (this.error) {
|
|
97
|
+
await this.checkpoint(this.resolveSnapshotStatus());
|
|
98
|
+
return this.buildResult();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
77
101
|
this.plan = plan;
|
|
78
|
-
if (this.args.options?.mode === "plan-only" && !this.args.options?.approvedPlan) {
|
|
102
|
+
if (!this.args.resumeFrom && this.args.options?.mode === "plan-only" && !this.args.options?.approvedPlan) {
|
|
79
103
|
this.awaitingApproval = true;
|
|
80
104
|
return this.buildResult();
|
|
81
105
|
}
|
|
@@ -84,6 +108,14 @@ var PlannerRun = class {
|
|
|
84
108
|
} catch (caught) {
|
|
85
109
|
this.error = this.toAIError(caught);
|
|
86
110
|
}
|
|
111
|
+
await this.checkpoint(this.resolveSnapshotStatus());
|
|
112
|
+
if (!this.error && this.args.config.durable?.deleteOnComplete) {
|
|
113
|
+
const outcome = await deletePlannerSnapshot({
|
|
114
|
+
durable: this.args.config.durable,
|
|
115
|
+
runId: this.runId
|
|
116
|
+
});
|
|
117
|
+
if (!outcome.ok) this.logDurableFailure("snapshot.delete.failed", outcome.error);
|
|
118
|
+
}
|
|
87
119
|
return this.buildResult();
|
|
88
120
|
}
|
|
89
121
|
/**
|
|
@@ -167,6 +199,7 @@ var PlannerRun = class {
|
|
|
167
199
|
const previousOutputs = [];
|
|
168
200
|
let steps = plan.steps;
|
|
169
201
|
let index = 0;
|
|
202
|
+
if (this.args.resumeFrom) index = this.rehydrateSequentialState(steps, previousOutputs);
|
|
170
203
|
while (index < steps.length) {
|
|
171
204
|
const step = steps[index];
|
|
172
205
|
if (index >= this.args.maxSteps) {
|
|
@@ -222,6 +255,10 @@ var PlannerRun = class {
|
|
|
222
255
|
const outputs = /* @__PURE__ */ new Map();
|
|
223
256
|
const rawOutputs = /* @__PURE__ */ new Map();
|
|
224
257
|
let executedCount = 0;
|
|
258
|
+
if (this.args.resumeFrom && !this.dagResumeConsumed) {
|
|
259
|
+
this.dagResumeConsumed = true;
|
|
260
|
+
executedCount = this.rehydrateDagState(dag, completed, done, outputs, rawOutputs);
|
|
261
|
+
}
|
|
225
262
|
while (done.size < dag.nodes.length) {
|
|
226
263
|
if (this.isAborted()) {
|
|
227
264
|
this.markCancelled();
|
|
@@ -315,6 +352,7 @@ var PlannerRun = class {
|
|
|
315
352
|
usage: result.usage,
|
|
316
353
|
childReport
|
|
317
354
|
});
|
|
355
|
+
await this.checkpoint("running");
|
|
318
356
|
if (failed) {
|
|
319
357
|
this.error = result.error;
|
|
320
358
|
return false;
|
|
@@ -584,6 +622,123 @@ var PlannerRun = class {
|
|
|
584
622
|
const mergedCost = accumulateCost(target.cost, child.cost);
|
|
585
623
|
if (mergedCost !== void 0) target.cost = mergedCost;
|
|
586
624
|
}
|
|
625
|
+
/**
|
|
626
|
+
* Re-derive the sequential cursor + prior-output context from the
|
|
627
|
+
* persisted ledger on resume. Threads every already-`completed` node's
|
|
628
|
+
* output into `previousOutputs`, returns the first index NOT completed
|
|
629
|
+
* as the resume cursor, and prunes stale non-completed ledger entries
|
|
630
|
+
* (the failed node + any skipped tail) at-or-after that cursor so the
|
|
631
|
+
* re-run repopulates them without duplicating.
|
|
632
|
+
*/
|
|
633
|
+
rehydrateSequentialState(steps, previousOutputs) {
|
|
634
|
+
let cursor = 0;
|
|
635
|
+
for (let index = 0; index < steps.length; index++) {
|
|
636
|
+
const snapshot = this.snapshotFor(index);
|
|
637
|
+
if (snapshot?.status === "completed") {
|
|
638
|
+
const step = steps[index];
|
|
639
|
+
previousOutputs.push(this.stringifyOutput(step.capability, snapshot.output));
|
|
640
|
+
cursor = index + 1;
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
this.pruneLedgerFrom(cursor);
|
|
646
|
+
return cursor;
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Re-derive the DAG scheduler's working sets from the persisted ledger
|
|
650
|
+
* on resume. Completed nodes go into `completed` + `done` with their
|
|
651
|
+
* string + raw outputs restored (so dependents read the right context);
|
|
652
|
+
* stale non-completed entries are pruned so the re-run repopulates them.
|
|
653
|
+
* Returns the count of nodes already dispatched (for the `maxSteps`
|
|
654
|
+
* truncation budget).
|
|
655
|
+
*/
|
|
656
|
+
rehydrateDagState(dag, completed, done, outputs, rawOutputs) {
|
|
657
|
+
const completedIndices = /* @__PURE__ */ new Set();
|
|
658
|
+
for (const node of dag.nodes) {
|
|
659
|
+
const snapshot = this.snapshotFor(node.index);
|
|
660
|
+
if (snapshot?.status !== "completed") continue;
|
|
661
|
+
completed.add(node.id);
|
|
662
|
+
done.add(node.id);
|
|
663
|
+
completedIndices.add(node.index);
|
|
664
|
+
rawOutputs.set(node.id, snapshot.output);
|
|
665
|
+
outputs.set(node.id, this.stringifyOutput(node.step.capability, snapshot.output));
|
|
666
|
+
}
|
|
667
|
+
const retained = this.executedSteps.filter((snapshot) => completedIndices.has(snapshot.index));
|
|
668
|
+
this.executedSteps.length = 0;
|
|
669
|
+
this.executedSteps.push(...retained);
|
|
670
|
+
return completedIndices.size;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Drop every ledger entry whose index is at or after `from`. Used by
|
|
674
|
+
* the sequential resume re-seed to clear the crashed run's failed /
|
|
675
|
+
* skipped frontier before the re-run repopulates it.
|
|
676
|
+
*/
|
|
677
|
+
pruneLedgerFrom(from) {
|
|
678
|
+
const retained = this.executedSteps.filter((snapshot) => snapshot.index < from);
|
|
679
|
+
this.executedSteps.length = 0;
|
|
680
|
+
this.executedSteps.push(...retained);
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Map the run's terminal outcome to the persisted snapshot status.
|
|
684
|
+
* `awaiting-approval` (plan-only) never persists a durable snapshot
|
|
685
|
+
* (resume is always an execution), so it folds to `running` here —
|
|
686
|
+
* but the durable + plan-only combination is disallowed at the call
|
|
687
|
+
* site, so this path is effectively unreachable.
|
|
688
|
+
*/
|
|
689
|
+
resolveSnapshotStatus() {
|
|
690
|
+
if (this.cancelledAt !== void 0) return "cancelled";
|
|
691
|
+
if (this.error) return "failed";
|
|
692
|
+
if (this.awaitingApproval) return "running";
|
|
693
|
+
return "completed";
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Build and persist a {@link PlannerSnapshot} from the current
|
|
697
|
+
* accumulators. The per-node and terminal checkpoints both route
|
|
698
|
+
* through here. No-op when `durable` is absent. A failed persist is
|
|
699
|
+
* logged and swallowed (never aborts the run), matching the supervisor
|
|
700
|
+
* / workflow checkpoint policy.
|
|
701
|
+
*/
|
|
702
|
+
async checkpoint(status) {
|
|
703
|
+
if (!this.args.config.durable || !this.plan) return;
|
|
704
|
+
const outcome = await persistPlannerSnapshot({
|
|
705
|
+
durable: this.args.config.durable,
|
|
706
|
+
runId: this.runId,
|
|
707
|
+
plannerName: this.args.config.name,
|
|
708
|
+
signature: this.args.signature,
|
|
709
|
+
version: this.args.config.version,
|
|
710
|
+
goal: this.args.goal,
|
|
711
|
+
plan: this.plan,
|
|
712
|
+
executedSteps: this.executedSteps,
|
|
713
|
+
usage: this.usage,
|
|
714
|
+
children: this.children,
|
|
715
|
+
replanCount: this.replanCount,
|
|
716
|
+
status,
|
|
717
|
+
startedAt: this.startedAt
|
|
718
|
+
});
|
|
719
|
+
if (!outcome.ok) this.logDurableFailure("snapshot.persist.failed", outcome.error);
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Re-derive the terminal state when a resume short-circuits a snapshot
|
|
723
|
+
* whose run already COMPLETED. The persisted ledger is the
|
|
724
|
+
* authoritative outcome — `this.data` is restored from the last
|
|
725
|
+
* completed node so the rebuilt result carries the final output.
|
|
726
|
+
*
|
|
727
|
+
* Only reached for a `completed` snapshot — `failed` / `cancelled`
|
|
728
|
+
* snapshots re-enter execution to retry the unfinished frontier instead.
|
|
729
|
+
*/
|
|
730
|
+
rebuildResumedTerminal(_status) {
|
|
731
|
+
const lastCompleted = [...this.executedSteps].reverse().find((snapshot) => snapshot.status === "completed");
|
|
732
|
+
if (lastCompleted) this.data = lastCompleted.output;
|
|
733
|
+
}
|
|
734
|
+
/** Structured-log a durable persist/delete failure. */
|
|
735
|
+
logDurableFailure(action, error) {
|
|
736
|
+
log.warn("ai.planner", action, "durable snapshot operation failed", {
|
|
737
|
+
runId: this.runId,
|
|
738
|
+
planner: this.args.config.name,
|
|
739
|
+
error: error instanceof Error ? error.message : String(error)
|
|
740
|
+
});
|
|
741
|
+
}
|
|
587
742
|
/** Whether the caller's abort signal has fired. */
|
|
588
743
|
isAborted() {
|
|
589
744
|
return this.args.options?.signal?.aborted === true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-run.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai/src/planner/planner-run.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { AgentContract } from \"../contracts/agent/agent.contract\";\nimport type { PlannerCapability } from \"../contracts/planner/planner-capability.type\";\nimport type { PlannerConfig } from \"../contracts/planner/planner-config.type\";\nimport type {\n PlannerExecuteOptions,\n PlannerStepDirective,\n} from \"../contracts/planner/planner-execute-options.type\";\nimport type { PlannerPlan, PlannerStep } from \"../contracts/planner/planner-plan.type\";\nimport type {\n PlannerReport,\n PlannerResult,\n PlannerStepSnapshot,\n} from \"../contracts/planner/planner-result.type\";\nimport type { BaseReport } from \"../contracts/result/base-report.type\";\nimport { REPORT_SCHEMA_VERSION } from \"../contracts/result/base-report.type\";\nimport type { BaseResult } from \"../contracts/result/base-result.type\";\nimport type { Usage } from \"../contracts/result/usage.type\";\nimport { AIError } from \"../errors/ai-error\";\nimport { PlannerCancelledError } from \"../errors/planner-cancelled-error\";\nimport { PlannerFailedError } from \"../errors/planner-failed-error\";\nimport { PlannerPlanInvalidError } from \"../errors/planner-plan-invalid-error\";\nimport { SchemaValidationError } from \"../errors/schema-validation-error\";\nimport { notifyObservers } from \"../observe/resolve-observers\";\nimport { accumulateCost } from \"../utils/compute-cost\";\nimport { generateRunId } from \"../utils/generate-run-id\";\nimport { captureChildReport, withoutRunFrame } from \"../utils/run-context\";\nimport { stampReportLineage } from \"../utils/stamp-report-lineage\";\nimport type { DagNode, PlannerDag } from \"./dag-scheduler\";\nimport { buildDag, readyNodes, sinkNodes } from \"./dag-scheduler\";\nimport { planSchema } from \"./plan-schema\";\n\n/**\n * Construction args for one {@link PlannerRun}. Carries everything the\n * factory resolved once (config, capability map, signature, planning\n * agent) plus the per-call goal and options.\n */\nexport type PlannerRunArgs<TOutput> = {\n config: PlannerConfig<TOutput>;\n capabilities: Map<string, PlannerCapability>;\n maxSteps: number;\n signature: string;\n planningAgent: AgentContract<unknown>;\n goal: string;\n options?: PlannerExecuteOptions<TOutput>;\n};\n\n/**\n * Per-call orchestration state for one `planner.execute()` invocation.\n *\n * **Role.** Owns the full bounded-v1 planning lifecycle across four\n * phases that share mutable accumulators: (1) ask the LLM to GENERATE a\n * plan, (2) execute each plan step through its capability's `execute()`,\n * (3) optionally validate the final output, (4) assemble the unified\n * {@link PlannerResult}. Instantiated fresh per call inside the factory\n * so the accumulators (`usage`, `children`, `executedSteps`) are never\n * shared across runs. Unexported — callers only ever see the plain\n * {@link PlannerResult}.\n *\n * **Composition, not a fork.** Plan generation runs through a normal\n * `agent.execute()`; each step runs through the capability's own\n * `executable.execute()`. The planner adds the plan-generation brain and\n * the ordered-dispatch loop on top of the existing executable machinery —\n * it does not reimplement agent or step internals.\n */\nexport class PlannerRun<TOutput> {\n private readonly runId: string;\n private readonly startedAt = new Date().toISOString();\n private readonly startPerf = performance.now();\n\n private readonly usage: Usage = { input: 0, output: 0, total: 0 };\n private readonly children: BaseReport[] = [];\n private readonly executedSteps: PlannerStepSnapshot[] = [];\n\n private plan?: PlannerPlan;\n private data?: TOutput;\n private error?: AIError;\n private cancelledAt?: string;\n\n /** Set when `mode: \"plan-only\"` short-circuited before execution. */\n private awaitingApproval = false;\n\n /** How many times the plan has been regenerated mid-run (≤ maxReplans). */\n private replanCount = 0;\n\n public constructor(private readonly args: PlannerRunArgs<TOutput>) {\n this.runId = args.options?.runId ?? generateRunId(\"planner\");\n }\n\n /**\n * Run the planner end-to-end. Never throws on runtime failure —\n * generation errors, plan-validity errors, step failures, and\n * cancellation all surface on `result.error` with a narrowing\n * `report.status`.\n */\n public async run(): Promise<PlannerResult<TOutput>> {\n const result = await this.runPlan();\n\n // Route the planner's OWN report — the planning trip plus every\n // capability step already nest under it via `absorb`, so this single\n // call surfaces the whole tree as one trace. Mirrors agent/workflow:\n // `notifyObservers` self-routes a root run under observe-all (skipped\n // when nested, via the run-frame gate), then `captureChildReport`\n // auto-nests the planner under any enclosing orchestration run. Without\n // this, observe-all would only ever see the sub-agents as standalone\n // fragments — the planner itself never appeared.\n await notifyObservers(this.args.config.observe, result.report);\n captureChildReport(result.report);\n\n return result;\n }\n\n /**\n * Drive the planner lifecycle and return the built result WITHOUT\n * routing it — `run()` owns observer routing + auto-nesting so the\n * unified tree is emitted exactly once.\n */\n private async runPlan(): Promise<PlannerResult<TOutput>> {\n try {\n if (this.isAborted()) {\n this.markCancelled();\n return this.buildResult();\n }\n\n // Approval fork — an `approvedPlan` skips generation entirely; it is\n // still validated against the live capabilities so a stale plan\n // surfaces a typed PlannerPlanInvalidError rather than mis-dispatching.\n const plan = this.args.options?.approvedPlan ?? (await this.generatePlan());\n\n if (this.error || !plan) {\n return this.buildResult();\n }\n\n this.assertPlanValid(plan);\n\n if (this.error) {\n return this.buildResult();\n }\n\n this.plan = plan;\n\n // Plan-only mode — surface the validated plan for sign-off and execute\n // NOTHING. `approvedPlan` overrides this (execute the supplied plan),\n // mirroring the documented \"approvedPlan wins\" precedence.\n if (this.args.options?.mode === \"plan-only\" && !this.args.options?.approvedPlan) {\n this.awaitingApproval = true;\n return this.buildResult();\n }\n\n await this.executePlan(plan);\n\n await this.finalizeOutput();\n } catch (caught) {\n this.error = this.toAIError(caught);\n }\n\n return this.buildResult();\n }\n\n /**\n * Phase 1 — ask the planning agent for a structured plan. The plan\n * schema (built from the live capability names) is supplied as the\n * agent's per-call `output`, so the model is steered to reference only\n * real capabilities. The planning trip's usage + report roll into the\n * planner's totals regardless of outcome.\n *\n * `feedback` is set only on a RE-plan: the regenerated request is\n * seeded with the executed-step digest plus the caller's feedback so\n * the planner revises the remaining work rather than starting cold.\n */\n private async generatePlan(feedback?: string): Promise<PlannerPlan | undefined> {\n const schema = planSchema([...this.args.capabilities.keys()], this.args.maxSteps);\n\n // `withoutRunFrame` suppresses the planning trip's own self-routing:\n // `absorb` already folds its report into `this.children`, so without\n // this the trip would ALSO route as a standalone top-level trace under\n // observe-all. The planner routes the unified tree once, in `run()`.\n const result = await withoutRunFrame(() =>\n this.args.planningAgent.execute(this.buildPlanPrompt(feedback), {\n output: schema as StandardSchemaV1<unknown>,\n placeholders: this.args.options?.placeholders,\n signal: this.args.options?.signal,\n sessionId: this.args.options?.sessionId,\n }),\n );\n\n this.absorb(result.usage, result.report);\n\n if (result.error) {\n // A schema rejection from the planning trip (e.g. an empty\n // `steps` array tripping the plan schema) is really an invalid\n // plan — re-wrap it into the typed planner contract so callers\n // branch on `PlannerPlanInvalidError` rather than the agent's raw\n // `SchemaValidationError`. Any other child error flows through\n // unchanged.\n this.error =\n result.error instanceof SchemaValidationError\n ? new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): the planner produced no usable plan`,\n { cause: result.error, context: { runId: this.runId } },\n )\n : result.error;\n return undefined;\n }\n\n const plan = result.data as PlannerPlan | undefined;\n\n if (!plan || !Array.isArray(plan.steps) || plan.steps.length === 0) {\n this.error = new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): the planner produced no usable plan`,\n { context: { runId: this.runId } },\n );\n return undefined;\n }\n\n this.assertPlanValid(plan);\n\n if (this.error) {\n return undefined;\n }\n\n return plan;\n }\n\n /**\n * Shared plan-validity guard — used both for a freshly generated plan\n * and for a caller-supplied `approvedPlan`. Sets `this.error` to a\n * typed {@link PlannerPlanInvalidError} when the plan is empty or names\n * an unknown capability; a stale `approvedPlan` thus fails the same way\n * a hallucinated capability does, never silently mis-dispatching.\n */\n private assertPlanValid(plan: PlannerPlan): void {\n if (!Array.isArray(plan.steps) || plan.steps.length === 0) {\n this.error = new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): the planner produced no usable plan`,\n { context: { runId: this.runId } },\n );\n return;\n }\n\n const unknownStep = plan.steps.find((step) => !this.args.capabilities.has(step.capability));\n\n if (unknownStep) {\n this.error = new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): plan references unknown capability \"${unknownStep.capability}\"`,\n { context: { runId: this.runId, capability: unknownStep.capability } },\n );\n }\n }\n\n /**\n * Phase 2 — execute the plan. Branches on `config.dag`: the default is\n * the strict array-order sequential loop (byte-for-byte today's\n * behavior when neither `onStep` nor `replan` is configured); `dag:\n * true` schedules independent `dependsOn` branches in parallel.\n */\n private async executePlan(plan: PlannerPlan): Promise<void> {\n if (this.args.config.dag) {\n return this.executeDag(plan);\n }\n\n return this.executeSequential(plan);\n }\n\n /**\n * Sequential executor — the original strict array-order loop, threading\n * each completed step's output into the next step's input context.\n * Stops at the first step failure or when the abort signal fires\n * between steps; steps beyond `maxSteps` are recorded `skipped`.\n *\n * **Additive hooks (inert by default).** After each step settles it\n * fires the `onStep` directive hook; an `abort` directive stops the run\n * like a failure, and a `replan` directive (or, when `config.replan` is\n * set, an unhandled failure) regenerates the REMAINING plan instead of\n * aborting. With no `onStep` and no `replan`, the behavior is identical\n * to before.\n */\n private async executeSequential(plan: PlannerPlan): Promise<void> {\n const previousOutputs: string[] = [];\n let steps = plan.steps;\n let index = 0;\n\n while (index < steps.length) {\n const step = steps[index] as PlannerStep;\n\n if (index >= this.args.maxSteps) {\n this.recordSkipped(index, step);\n index++;\n continue;\n }\n\n if (this.isAborted()) {\n this.markCancelled();\n this.recordSkipped(index, step);\n index++;\n continue;\n }\n\n const completed = await this.executeStep(index, step, previousOutputs);\n\n const snapshot = this.snapshotFor(index);\n const directive = snapshot\n ? await this.resolveDirective(snapshot, plan, completed)\n : undefined;\n\n if (directive?.type === \"replan\") {\n const remaining = await this.regeneratePlan(directive.feedback);\n\n if (this.error || !remaining) {\n this.skipRest(steps, index + 1);\n return;\n }\n\n // Replace the remaining tail with the regenerated plan and restart\n // the cursor against it (executed steps already recorded stay put).\n // Each new step gets the executed-so-far digest as its context.\n steps = remaining.steps;\n index = 0;\n previousOutputs.length = 0;\n previousOutputs.push(...this.executedDigest());\n continue;\n }\n\n if (directive?.type === \"abort\") {\n // The hook (or an unhandled failure) asked to stop — record the\n // remaining steps as skipped so the report still describes the\n // whole intended plan, then stop.\n this.skipRest(steps, index + 1);\n return;\n }\n\n index++;\n }\n }\n\n /**\n * DAG executor — schedule independent `dependsOn` branches in parallel.\n *\n * Builds the DAG (cycle / unknown-id → `PlannerPlanInvalidError`),\n * then repeatedly computes the ready set (steps whose deps all\n * completed), dispatches up to `maxConcurrency` of them with\n * `Promise.all`, and feeds each step ONLY its dependencies' outputs. A\n * failed step blocks just its descendants (recorded `skipped`);\n * independent branches still settle. With an `output` schema set, the\n * final `data` is the topological SINK's output (multiple sinks → a\n * convergence error).\n */\n private async executeDag(plan: PlannerPlan): Promise<void> {\n const dag = buildDag(plan.steps, this.args.config.name);\n const maxConcurrency = Math.max(1, this.args.config.maxConcurrency ?? 4);\n\n const completed = new Set<string>();\n const done = new Set<string>();\n const outputs = new Map<string, string>();\n const rawOutputs = new Map<string, unknown>();\n let executedCount = 0;\n\n while (done.size < dag.nodes.length) {\n if (this.isAborted()) {\n this.markCancelled();\n this.skipDagRest(dag, done);\n return;\n }\n\n const ready = readyNodes(dag, completed, done);\n\n if (ready.length === 0) {\n // No node can advance — every remaining node transitively depends\n // on a failed/skipped ancestor. Record them skipped and stop.\n this.skipDagRest(dag, done);\n return;\n }\n\n const batch = ready.slice(0, maxConcurrency);\n\n const settled = await Promise.all(\n batch.map(async (node) => {\n // `maxSteps` truncation applies to the count of DISPATCHED steps.\n if (executedCount >= this.args.maxSteps) {\n this.recordSkipped(node.index, node.step);\n return { node, ran: false, completed: false };\n }\n\n executedCount++;\n // Feed this step ONLY its dependencies' output digests — the DAG\n // fix for the sequential loop's \"all prior outputs into every\n // step\" behavior. `executeStep` pushes into the array it is\n // given, so a fresh array per node keeps branches isolated.\n const previousOutputs = node.dependencies.map(\n (dependency) => outputs.get(dependency) as string,\n );\n const stepCompleted = await this.executeStep(\n node.index,\n node.step,\n previousOutputs,\n );\n\n if (stepCompleted) {\n // Read the raw output off the snapshot (NOT shared `this.data`,\n // which races under Promise.all) for both the dependent digest\n // and the eventual sink output.\n const rawOutput = this.snapshotFor(node.index)?.output;\n rawOutputs.set(node.id, rawOutput);\n outputs.set(node.id, this.stringifyOutput(node.step.capability, rawOutput));\n }\n\n return { node, ran: true, completed: stepCompleted };\n }),\n );\n\n for (const entry of settled) {\n done.add(entry.node.id);\n\n if (entry.completed) {\n completed.add(entry.node.id);\n }\n }\n\n // Fire the per-step hook for each settled step (in dispatch order).\n let replanFeedback: string | undefined;\n let shouldAbort = false;\n\n for (const entry of settled) {\n if (!entry.ran) {\n continue;\n }\n\n const snapshot = this.snapshotFor(entry.node.index);\n const directive = snapshot\n ? await this.resolveDirective(snapshot, plan, entry.completed)\n : undefined;\n\n if (directive?.type === \"replan\") {\n replanFeedback = directive.feedback;\n } else if (directive?.type === \"abort\") {\n shouldAbort = true;\n }\n }\n\n if (shouldAbort) {\n this.skipDagRest(dag, done);\n return;\n }\n\n if (replanFeedback !== undefined) {\n const remaining = await this.regeneratePlan(replanFeedback);\n\n if (this.error || !remaining) {\n this.skipDagRest(dag, done);\n return;\n }\n\n // Re-plan in DAG mode regenerates the remaining work as a fresh\n // (sequential) plan and runs it through the DAG scheduler again.\n return this.executeDag(remaining);\n }\n }\n\n this.finalizeDagOutput(dag, completed, rawOutputs);\n }\n\n /**\n * Dispatch one plan step through its capability's `executable.execute()`\n * and fold the outcome into the accumulators. Returns `true` when the\n * step completed, `false` when it failed (setting the run error).\n */\n private async executeStep(\n index: number,\n step: PlannerStep,\n previousOutputs: string[],\n ): Promise<boolean> {\n const capability = this.args.capabilities.get(step.capability) as PlannerCapability;\n const stepStart = performance.now();\n const startedAt = new Date().toISOString();\n const input = this.composeStepInput(step, previousOutputs);\n\n // `withoutRunFrame` keeps each capability step nested under the planner\n // only — `absorb` folds its report into `this.children`, so suppressing\n // its self-route prevents a duplicate standalone trace under observe-all.\n const result = await withoutRunFrame(() =>\n capability.executable.execute(input, {\n signal: this.args.options?.signal,\n sessionId: this.args.options?.sessionId,\n }),\n );\n\n const childReport = \"report\" in result ? (result.report as BaseReport) : undefined;\n this.absorb(result.usage, childReport);\n\n const output = this.extractOutput(result);\n const failed = result.error !== undefined;\n\n this.executedSteps.push({\n index,\n step,\n status: failed ? \"failed\" : \"completed\",\n output: failed ? undefined : output,\n error: result.error,\n startedAt,\n endedAt: new Date().toISOString(),\n duration: performance.now() - stepStart,\n usage: result.usage,\n childReport,\n });\n\n if (failed) {\n this.error = result.error;\n return false;\n }\n\n previousOutputs.push(this.stringifyOutput(step.capability, output));\n this.data = output as TOutput;\n\n return true;\n }\n\n /**\n * Resolve the steering directive for a just-settled step, shared by the\n * sequential and DAG executors. Fires the user's `onStep` hook, then\n * normalizes the result against the `replan` budget:\n *\n * - explicit `replan` directive — honored only when `config.replan` is\n * set and the budget remains; otherwise downgraded to `continue`.\n * - explicit `abort` — honored.\n * - failed step with no overriding directive — auto-`replan` when\n * `config.replan` is set and the budget remains (feedback = the step\n * error message), else `abort` (today's abort-on-first-failure).\n *\n * Returns `undefined` when the run should simply continue. A returned\n * `replan` directive has ALREADY consumed one unit of the replan budget.\n */\n private async resolveDirective(\n snapshot: PlannerStepSnapshot,\n plan: PlannerPlan,\n completed: boolean,\n ): Promise<PlannerStepDirective | undefined> {\n const hook = this.args.options?.onStep;\n const userDirective = hook ? await hook(snapshot, plan) : undefined;\n\n if (userDirective?.type === \"replan\") {\n if (this.canReplan()) {\n this.replanCount++;\n return userDirective;\n }\n\n // Replan requested but unavailable (no config or budget spent) — fall\n // through to the failure/continue defaults below.\n } else if (userDirective?.type === \"abort\") {\n return { type: \"abort\" };\n } else if (userDirective?.type === \"continue\") {\n return undefined;\n }\n\n if (!completed) {\n if (this.canReplan()) {\n this.replanCount++;\n return { type: \"replan\", feedback: snapshot.error?.message ?? \"step failed\" };\n }\n\n return { type: \"abort\" };\n }\n\n return undefined;\n }\n\n /** Whether a re-plan is configured and the budget has room. */\n private canReplan(): boolean {\n const replan = this.args.config.replan;\n\n return replan !== undefined && this.replanCount < replan.maxReplans;\n }\n\n /**\n * Re-ask the planning agent for a plan over the REMAINING work — a\n * second `generatePlan()` seeded with the executed-step digest plus the\n * caller's feedback. Reuses the exact `generatePlan` plumbing (same\n * schema, same `PlannerPlanInvalidError` handling), so a regenerated\n * plan that is empty or names an unknown capability fails identically.\n * The failed step's error is cleared so the regenerated plan runs\n * cleanly; a fresh failure (or exhausted budget) re-sets it.\n */\n private async regeneratePlan(feedback: string): Promise<PlannerPlan | undefined> {\n this.error = undefined;\n return this.generatePlan(feedback);\n }\n\n /**\n * The executed-so-far digest — one context line per completed step, in\n * execution order. Seeds the regenerated plan's first step so it builds\n * on what already ran.\n */\n private executedDigest(): string[] {\n return this.executedSteps\n .filter((snapshot) => snapshot.status === \"completed\")\n .map((snapshot) => this.stringifyOutput(snapshot.step.capability, snapshot.output));\n }\n\n /** The last-pushed snapshot for a given step index, if any. */\n private snapshotFor(index: number): PlannerStepSnapshot | undefined {\n for (let position = this.executedSteps.length - 1; position >= 0; position--) {\n const snapshot = this.executedSteps[position] as PlannerStepSnapshot;\n\n if (snapshot.index === index) {\n return snapshot;\n }\n }\n\n return undefined;\n }\n\n /** Record every step from `from` onward (in a flat array plan) as skipped. */\n private skipRest(steps: PlannerStep[], from: number): void {\n for (let rest = from; rest < steps.length; rest++) {\n this.recordSkipped(rest, steps[rest] as PlannerStep);\n }\n }\n\n /** Record every not-yet-`done` DAG node as skipped, in plan order. */\n private skipDagRest(dag: PlannerDag, done: ReadonlySet<string>): void {\n for (const node of dag.nodes) {\n if (!done.has(node.id)) {\n this.recordSkipped(node.index, node.step);\n }\n }\n }\n\n /**\n * Set `this.data` from the DAG's topological sink for a configured\n * `output` schema. \"Last completed step\" is meaningless under\n * parallelism, so the sink (the step nothing depends on) is the\n * unambiguous final output. Multiple sinks while an `output` schema is\n * set is a convergence error — a typed `PlannerPlanInvalidError`.\n */\n private finalizeDagOutput(\n dag: PlannerDag,\n completed: ReadonlySet<string>,\n rawOutputs: Map<string, unknown>,\n ): void {\n const schema = this.args.options?.output ?? this.args.config.output;\n\n if (!schema || this.error) {\n return;\n }\n\n const sinks = sinkNodes(dag).filter((node) => completed.has(node.id));\n\n if (sinks.length > 1) {\n this.error = new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): DAG has multiple sinks but an \\`output\\` schema is set — the plan must converge to a single final step`,\n { context: { runId: this.runId, sinks: sinks.map((node) => node.id) } },\n );\n this.data = undefined;\n return;\n }\n\n const sink = sinks[0] as DagNode | undefined;\n this.data = (sink ? rawOutputs.get(sink.id) : undefined) as TOutput | undefined;\n }\n\n /**\n * Phase 3 — when an `output` schema is configured (factory or per-call\n * override), validate the final completed step's output into typed\n * `result.data`. A validation failure replaces the run error and flips\n * the status to failed.\n */\n private async finalizeOutput(): Promise<void> {\n const schema = this.args.options?.output ?? this.args.config.output;\n\n if (!schema || this.error) {\n return;\n }\n\n if (this.data === undefined) {\n // An `output` schema is configured but the final completed step\n // produced nothing to validate — returning `{ data: undefined,\n // error: undefined, status: \"completed\" }` would be a silent\n // contract violation. Surface it as an invalid plan instead.\n this.error = new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): plan completed without producing output for the configured \\`output\\` schema`,\n { context: { runId: this.runId } },\n );\n return;\n }\n\n const validation = await schema[\"~standard\"].validate(this.data);\n\n if (validation.issues) {\n this.error = new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): final output failed validation`,\n {\n context: {\n runId: this.runId,\n issues: validation.issues.map((issue) => issue.message),\n },\n },\n );\n this.data = undefined;\n return;\n }\n\n this.data = validation.value as TOutput;\n }\n\n /**\n * Phase 4 — fold the accumulators into the planner's own\n * {@link PlannerReport} node and the final {@link PlannerResult}, then\n * stamp lineage across the whole subtree so every child shares this\n * run's root id.\n */\n private buildResult(): PlannerResult<TOutput> {\n const status = this.resolveStatus();\n\n const report: PlannerReport = {\n runId: this.runId,\n rootRunId: this.runId,\n name: this.args.config.name,\n version: this.args.config.version,\n type: \"planner\",\n status,\n // Stamp the terminal error so the observe path surfaces it on the\n // planner span (no result envelope reaches an observer). Absent on\n // a completed run.\n ...(this.error ? { error: this.error } : {}),\n startedAt: this.startedAt,\n endedAt: new Date().toISOString(),\n duration: performance.now() - this.startPerf,\n usage: this.usage,\n children: this.children,\n signature: this.args.signature,\n plan: this.plan,\n executedSteps: this.executedSteps,\n cancelledAt: this.cancelledAt,\n reportSchemaVersion: REPORT_SCHEMA_VERSION,\n };\n\n stampReportLineage(report, {\n rootRunId: this.runId,\n sessionId: this.args.options?.sessionId,\n });\n\n const result: PlannerResult<TOutput> = {\n type: \"planner\",\n data: this.error ? undefined : this.data,\n error: this.error,\n usage: this.usage,\n report,\n };\n\n // Plan-only mode surfaces the validated plan WITHOUT execution so the\n // caller can sign off and re-run with `approvedPlan`.\n if (this.awaitingApproval) {\n result.plan = this.plan;\n }\n\n return result;\n }\n\n /**\n * Resolve the terminal status from the accumulated outcome.\n * `awaiting-approval` (plan-only short-circuit) wins over everything —\n * nothing executed, so neither cancellation nor error applies.\n * Otherwise cancelled wins over failed (an abort that also produced a\n * step error still reads as cancelled); failed wins over completed.\n */\n private resolveStatus(): PlannerReport[\"status\"] {\n if (this.awaitingApproval) {\n return \"awaiting-approval\";\n }\n\n if (this.cancelledAt !== undefined) {\n return \"cancelled\";\n }\n\n if (this.error) {\n return \"failed\";\n }\n\n return \"completed\";\n }\n\n /**\n * Build the prompt handed to the planning agent. On the first pass this\n * is just the user's goal (byte-for-byte unchanged). On a RE-plan it\n * prepends the executed-step digest and the steering feedback so the\n * planner revises the remaining work.\n */\n private buildPlanPrompt(feedback?: string): string {\n if (feedback === undefined) {\n return this.args.goal;\n }\n\n const digest = this.executedDigest();\n const sections: string[] = [`Goal: ${this.args.goal}`, \"\"];\n\n if (digest.length > 0) {\n sections.push(\"Steps already completed:\", ...digest, \"\");\n }\n\n sections.push(\n `Feedback requiring a revised plan: ${feedback}`,\n \"\",\n \"Produce a plan for the REMAINING work only.\",\n );\n\n return sections.join(\"\\n\");\n }\n\n /**\n * Compose a step's effective input: the step's own `input`, prefixed\n * with a compact digest of every prior step's output so a downstream\n * capability can build on what ran before it. No prior output → the\n * step's raw input.\n */\n private composeStepInput(step: PlannerStep, previousOutputs: string[]): string {\n if (previousOutputs.length === 0) {\n return step.input;\n }\n\n return [\n \"Context from earlier steps:\",\n ...previousOutputs,\n \"\",\n `Task: ${step.input}`,\n ].join(\"\\n\");\n }\n\n /**\n * Pull the usable output off a capability's result. Prefers structured\n * `data` (agents/workflows with an `output` schema, tools), and falls\n * back to an agent's raw `text` when no structured data was produced —\n * the common case for a plain text-producing capability agent.\n */\n private extractOutput(result: BaseResult): unknown {\n const shaped = result as { data?: unknown; text?: unknown };\n\n if (shaped.data !== undefined) {\n return shaped.data;\n }\n\n if (typeof shaped.text === \"string\") {\n return shaped.text;\n }\n\n return undefined;\n }\n\n /** Serialize a capability output into a single context line for the next step. */\n private stringifyOutput(capability: string, output: unknown): string {\n if (output === undefined) {\n return `- ${capability}: (no output)`;\n }\n\n if (typeof output === \"string\") {\n return `- ${capability}: ${output}`;\n }\n\n return `- ${capability}: ${JSON.stringify(output)}`;\n }\n\n /** Push a `skipped` snapshot for a step the planner never dispatched. */\n private recordSkipped(index: number, step: PlannerStep): void {\n const now = new Date().toISOString();\n\n this.executedSteps.push({\n index,\n step,\n status: \"skipped\",\n startedAt: now,\n endedAt: now,\n duration: 0,\n usage: { input: 0, output: 0, total: 0 },\n });\n }\n\n /** Fold a child's usage + report node into the planner's accumulators. */\n private absorb(usage: Usage, report: BaseReport | undefined): void {\n this.mergeUsage(this.usage, usage);\n\n if (report) {\n this.children.push(report);\n }\n }\n\n /**\n * Add a child's usage into the running total. Mirrors the batch\n * primitive's rollup: scalar token channels sum directly, optional\n * sub-channels accumulate only when reported, and the cost breakdown\n * merges via {@link accumulateCost} so one unpriced child can't erase\n * priced siblings.\n */\n private mergeUsage(target: Usage, child: Usage): void {\n target.input += child.input;\n target.output += child.output;\n target.total += child.total;\n\n if (child.cachedTokens !== undefined) {\n target.cachedTokens = (target.cachedTokens ?? 0) + child.cachedTokens;\n }\n\n if (child.reasoningTokens !== undefined) {\n target.reasoningTokens = (target.reasoningTokens ?? 0) + child.reasoningTokens;\n }\n\n if (child.cacheWriteTokens !== undefined) {\n target.cacheWriteTokens = (target.cacheWriteTokens ?? 0) + child.cacheWriteTokens;\n }\n\n const mergedCost = accumulateCost(target.cost, child.cost);\n\n if (mergedCost !== undefined) {\n target.cost = mergedCost;\n }\n }\n\n /** Whether the caller's abort signal has fired. */\n private isAborted(): boolean {\n return this.args.options?.signal?.aborted === true;\n }\n\n /** Record a cancellation observation, setting the run error once. */\n private markCancelled(): void {\n if (this.cancelledAt !== undefined) {\n return;\n }\n\n this.cancelledAt = new Date().toISOString();\n\n const reason = this.args.options?.signal?.reason;\n\n this.error = new PlannerCancelledError(\n `ai.planner(\"${this.args.config.name}\"): run cancelled`,\n {\n cancelledAt: this.cancelledAt,\n reason: typeof reason === \"string\" ? reason : undefined,\n context: { runId: this.runId },\n },\n );\n }\n\n /** Normalize any thrown value into a typed {@link AIError}. */\n private toAIError(caught: unknown): AIError {\n if (caught instanceof AIError) {\n return caught;\n }\n\n const message = caught instanceof Error ? caught.message : String(caught);\n\n return new PlannerFailedError(`ai.planner(\"${this.args.config.name}\"): ${message}`, {\n cause: caught,\n context: { runId: this.runId },\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"planner-run.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai/src/planner/planner-run.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { log } from \"@warlock.js/logger\";\nimport type { AgentContract } from \"../contracts/agent/agent.contract\";\nimport type { PlannerCapability } from \"../contracts/planner/planner-capability.type\";\nimport type { PlannerConfig } from \"../contracts/planner/planner-config.type\";\nimport type {\n PlannerExecuteOptions,\n PlannerStepDirective,\n} from \"../contracts/planner/planner-execute-options.type\";\nimport type { PlannerPlan, PlannerStep } from \"../contracts/planner/planner-plan.type\";\nimport type {\n PlannerReport,\n PlannerResult,\n PlannerStepSnapshot,\n} from \"../contracts/planner/planner-result.type\";\nimport type {\n PlannerSnapshot,\n PlannerSnapshotStatus,\n} from \"../contracts/planner/planner-snapshot.type\";\nimport type { BaseReport } from \"../contracts/result/base-report.type\";\nimport { REPORT_SCHEMA_VERSION } from \"../contracts/result/base-report.type\";\nimport type { BaseResult } from \"../contracts/result/base-result.type\";\nimport type { Usage } from \"../contracts/result/usage.type\";\nimport { AIError } from \"../errors/ai-error\";\nimport { PlannerCancelledError } from \"../errors/planner-cancelled-error\";\nimport { PlannerFailedError } from \"../errors/planner-failed-error\";\nimport { PlannerPlanInvalidError } from \"../errors/planner-plan-invalid-error\";\nimport { SchemaValidationError } from \"../errors/schema-validation-error\";\nimport { notifyObservers } from \"../observe/resolve-observers\";\nimport { accumulateCost } from \"../utils/compute-cost\";\nimport { generateRunId } from \"../utils/generate-run-id\";\nimport { captureChildReport, withoutRunFrame } from \"../utils/run-context\";\nimport { stampReportLineage } from \"../utils/stamp-report-lineage\";\nimport type { DagNode, PlannerDag } from \"./dag-scheduler\";\nimport { buildDag, readyNodes, sinkNodes } from \"./dag-scheduler\";\nimport { planSchema } from \"./plan-schema\";\nimport {\n deletePlannerSnapshot,\n persistPlannerSnapshot,\n} from \"./snapshot\";\n\n/**\n * Construction args for one {@link PlannerRun}. Carries everything the\n * factory resolved once (config, capability map, signature, planning\n * agent) plus the per-call goal and options.\n */\nexport type PlannerRunArgs<TOutput> = {\n config: PlannerConfig<TOutput>;\n capabilities: Map<string, PlannerCapability>;\n maxSteps: number;\n signature: string;\n planningAgent: AgentContract<unknown>;\n goal: string;\n options?: PlannerExecuteOptions<TOutput>;\n /**\n * Durable resume seed. When present the run re-hydrates the frozen plan\n * + executed-node ledger + usage + child reports + replan budget from a\n * prior crash, skips plan generation, and continues scheduling only the\n * unfinished frontier. Absent ⇒ a normal cold run.\n */\n resumeFrom?: PlannerSnapshot;\n};\n\n/**\n * Per-call orchestration state for one `planner.execute()` invocation.\n *\n * **Role.** Owns the full bounded-v1 planning lifecycle across four\n * phases that share mutable accumulators: (1) ask the LLM to GENERATE a\n * plan, (2) execute each plan step through its capability's `execute()`,\n * (3) optionally validate the final output, (4) assemble the unified\n * {@link PlannerResult}. Instantiated fresh per call inside the factory\n * so the accumulators (`usage`, `children`, `executedSteps`) are never\n * shared across runs. Unexported — callers only ever see the plain\n * {@link PlannerResult}.\n *\n * **Composition, not a fork.** Plan generation runs through a normal\n * `agent.execute()`; each step runs through the capability's own\n * `executable.execute()`. The planner adds the plan-generation brain and\n * the ordered-dispatch loop on top of the existing executable machinery —\n * it does not reimplement agent or step internals.\n */\nexport class PlannerRun<TOutput> {\n private readonly runId: string;\n /**\n * Run start timestamp. A resumed run restores it from the snapshot (in\n * the constructor) so the rebuilt report spans the whole run, not just\n * the resumed tail — hence not `readonly`.\n */\n private startedAt = new Date().toISOString();\n private readonly startPerf = performance.now();\n\n private readonly usage: Usage = { input: 0, output: 0, total: 0 };\n private readonly children: BaseReport[] = [];\n private readonly executedSteps: PlannerStepSnapshot[] = [];\n\n private plan?: PlannerPlan;\n private data?: TOutput;\n private error?: AIError;\n private cancelledAt?: string;\n\n /** Set when `mode: \"plan-only\"` short-circuited before execution. */\n private awaitingApproval = false;\n\n /** How many times the plan has been regenerated mid-run (≤ maxReplans). */\n private replanCount = 0;\n\n /**\n * One-shot guard so the DAG resume re-seed runs only on the first\n * `executeDag` pass — a later replan recursion gets a fresh plan with\n * different node ids and must NOT re-seed against the stale ledger.\n */\n private dagResumeConsumed = false;\n\n public constructor(private readonly args: PlannerRunArgs<TOutput>) {\n // A resumed run reuses the snapshot's key so it writes back to the\n // same record; otherwise a caller-supplied `options.runId` wins, else\n // a fresh id is generated.\n this.runId = args.resumeFrom?.runId ?? args.options?.runId ?? generateRunId(\"planner\");\n\n // Seed the accumulators from the snapshot on resume — re-hydrate the\n // frozen plan, the per-node ledger, the rolled-up usage, the child\n // reports, and the replan budget. `startedAt` restores too so the\n // resumed report spans the whole run. Pushing into the ledger rather\n // than re-running nodes is what keeps completed capabilities from\n // re-dispatching — the sequential guard / DAG re-seed read \"what ran\"\n // straight off `executedSteps`. Absent ⇒ accumulators stay empty and\n // the cold path is byte-for-byte unchanged.\n if (args.resumeFrom) {\n this.plan = args.resumeFrom.plan;\n this.executedSteps.push(...args.resumeFrom.executedSteps);\n this.children.push(...args.resumeFrom.children);\n this.mergeUsage(this.usage, args.resumeFrom.usage);\n this.replanCount = args.resumeFrom.replanCount;\n this.startedAt = args.resumeFrom.startedAt;\n }\n }\n\n /**\n * Run the planner end-to-end. Never throws on runtime failure —\n * generation errors, plan-validity errors, step failures, and\n * cancellation all surface on `result.error` with a narrowing\n * `report.status`.\n */\n public async run(): Promise<PlannerResult<TOutput>> {\n const result = await this.runPlan();\n\n // Route the planner's OWN report — the planning trip plus every\n // capability step already nest under it via `absorb`, so this single\n // call surfaces the whole tree as one trace. Mirrors agent/workflow:\n // `notifyObservers` self-routes a root run under observe-all (skipped\n // when nested, via the run-frame gate), then `captureChildReport`\n // auto-nests the planner under any enclosing orchestration run. Without\n // this, observe-all would only ever see the sub-agents as standalone\n // fragments — the planner itself never appeared.\n await notifyObservers(this.args.config.observe, result.report);\n captureChildReport(result.report);\n\n return result;\n }\n\n /**\n * Drive the planner lifecycle and return the built result WITHOUT\n * routing it — `run()` owns observer routing + auto-nesting so the\n * unified tree is emitted exactly once.\n */\n private async runPlan(): Promise<PlannerResult<TOutput>> {\n // Completed-run short-circuit. A resume of a snapshot whose run\n // already COMPLETED re-runs nothing — the stored ledger IS the\n // result. A `failed` / `cancelled` snapshot is NOT short-circuited:\n // those are the runs a caller resumes to retry the unfinished\n // frontier after fixing the cause, so they re-enter execution below.\n if (this.args.resumeFrom && this.args.resumeFrom.status === \"completed\") {\n this.rebuildResumedTerminal(\"completed\");\n return this.buildResult();\n }\n\n try {\n if (this.isAborted()) {\n this.markCancelled();\n await this.checkpoint(this.resolveSnapshotStatus());\n return this.buildResult();\n }\n\n // Resume fork — the plan is frozen (re-asking the LLM would burn\n // tokens and risk a different plan that no longer matches the\n // executed-node ledger). Skip generation entirely and execute the\n // re-hydrated plan; the sequential guard / DAG re-seed skip the\n // nodes already terminal in `executedSteps`.\n const plan = this.args.resumeFrom\n ? (this.plan as PlannerPlan)\n : (this.args.options?.approvedPlan ?? (await this.generatePlan()));\n\n if (this.error || !plan) {\n await this.checkpoint(this.resolveSnapshotStatus());\n return this.buildResult();\n }\n\n // On a fresh run, validate the plan (a generated / approved plan\n // could name an unknown capability). A resumed plan was already\n // valid when persisted, so skip re-validation unless drift `force`\n // is implied — re-validating a frozen plan against the same live\n // capabilities is redundant.\n if (!this.args.resumeFrom) {\n this.assertPlanValid(plan);\n\n if (this.error) {\n await this.checkpoint(this.resolveSnapshotStatus());\n return this.buildResult();\n }\n }\n\n this.plan = plan;\n\n // Plan-only mode — surface the validated plan for sign-off and execute\n // NOTHING. `approvedPlan` overrides this (execute the supplied plan),\n // mirroring the documented \"approvedPlan wins\" precedence. A resume is\n // always an execution, never a plan-only short-circuit.\n if (\n !this.args.resumeFrom &&\n this.args.options?.mode === \"plan-only\" &&\n !this.args.options?.approvedPlan\n ) {\n this.awaitingApproval = true;\n return this.buildResult();\n }\n\n await this.executePlan(plan);\n\n await this.finalizeOutput();\n } catch (caught) {\n this.error = this.toAIError(caught);\n }\n\n // Terminal checkpoint — persist the final state so a completed-run\n // resume short-circuits, then optionally drop the snapshot when\n // `deleteOnComplete` is set and the run succeeded. No-op when\n // `durable` is absent.\n await this.checkpoint(this.resolveSnapshotStatus());\n\n if (!this.error && this.args.config.durable?.deleteOnComplete) {\n const outcome = await deletePlannerSnapshot({\n durable: this.args.config.durable,\n runId: this.runId,\n });\n\n if (!outcome.ok) {\n this.logDurableFailure(\"snapshot.delete.failed\", outcome.error);\n }\n }\n\n return this.buildResult();\n }\n\n /**\n * Phase 1 — ask the planning agent for a structured plan. The plan\n * schema (built from the live capability names) is supplied as the\n * agent's per-call `output`, so the model is steered to reference only\n * real capabilities. The planning trip's usage + report roll into the\n * planner's totals regardless of outcome.\n *\n * `feedback` is set only on a RE-plan: the regenerated request is\n * seeded with the executed-step digest plus the caller's feedback so\n * the planner revises the remaining work rather than starting cold.\n */\n private async generatePlan(feedback?: string): Promise<PlannerPlan | undefined> {\n const schema = planSchema([...this.args.capabilities.keys()], this.args.maxSteps);\n\n // `withoutRunFrame` suppresses the planning trip's own self-routing:\n // `absorb` already folds its report into `this.children`, so without\n // this the trip would ALSO route as a standalone top-level trace under\n // observe-all. The planner routes the unified tree once, in `run()`.\n const result = await withoutRunFrame(() =>\n this.args.planningAgent.execute(this.buildPlanPrompt(feedback), {\n output: schema as StandardSchemaV1<unknown>,\n placeholders: this.args.options?.placeholders,\n signal: this.args.options?.signal,\n sessionId: this.args.options?.sessionId,\n }),\n );\n\n this.absorb(result.usage, result.report);\n\n if (result.error) {\n // A schema rejection from the planning trip (e.g. an empty\n // `steps` array tripping the plan schema) is really an invalid\n // plan — re-wrap it into the typed planner contract so callers\n // branch on `PlannerPlanInvalidError` rather than the agent's raw\n // `SchemaValidationError`. Any other child error flows through\n // unchanged.\n this.error =\n result.error instanceof SchemaValidationError\n ? new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): the planner produced no usable plan`,\n { cause: result.error, context: { runId: this.runId } },\n )\n : result.error;\n return undefined;\n }\n\n const plan = result.data as PlannerPlan | undefined;\n\n if (!plan || !Array.isArray(plan.steps) || plan.steps.length === 0) {\n this.error = new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): the planner produced no usable plan`,\n { context: { runId: this.runId } },\n );\n return undefined;\n }\n\n this.assertPlanValid(plan);\n\n if (this.error) {\n return undefined;\n }\n\n return plan;\n }\n\n /**\n * Shared plan-validity guard — used both for a freshly generated plan\n * and for a caller-supplied `approvedPlan`. Sets `this.error` to a\n * typed {@link PlannerPlanInvalidError} when the plan is empty or names\n * an unknown capability; a stale `approvedPlan` thus fails the same way\n * a hallucinated capability does, never silently mis-dispatching.\n */\n private assertPlanValid(plan: PlannerPlan): void {\n if (!Array.isArray(plan.steps) || plan.steps.length === 0) {\n this.error = new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): the planner produced no usable plan`,\n { context: { runId: this.runId } },\n );\n return;\n }\n\n const unknownStep = plan.steps.find((step) => !this.args.capabilities.has(step.capability));\n\n if (unknownStep) {\n this.error = new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): plan references unknown capability \"${unknownStep.capability}\"`,\n { context: { runId: this.runId, capability: unknownStep.capability } },\n );\n }\n }\n\n /**\n * Phase 2 — execute the plan. Branches on `config.dag`: the default is\n * the strict array-order sequential loop (byte-for-byte today's\n * behavior when neither `onStep` nor `replan` is configured); `dag:\n * true` schedules independent `dependsOn` branches in parallel.\n */\n private async executePlan(plan: PlannerPlan): Promise<void> {\n if (this.args.config.dag) {\n return this.executeDag(plan);\n }\n\n return this.executeSequential(plan);\n }\n\n /**\n * Sequential executor — the original strict array-order loop, threading\n * each completed step's output into the next step's input context.\n * Stops at the first step failure or when the abort signal fires\n * between steps; steps beyond `maxSteps` are recorded `skipped`.\n *\n * **Additive hooks (inert by default).** After each step settles it\n * fires the `onStep` directive hook; an `abort` directive stops the run\n * like a failure, and a `replan` directive (or, when `config.replan` is\n * set, an unhandled failure) regenerates the REMAINING plan instead of\n * aborting. With no `onStep` and no `replan`, the behavior is identical\n * to before.\n */\n private async executeSequential(plan: PlannerPlan): Promise<void> {\n const previousOutputs: string[] = [];\n let steps = plan.steps;\n let index = 0;\n\n // Resume re-seed (sequential). The frozen plan's already-completed\n // prefix lives in the persisted ledger; thread its outputs forward and\n // jump the cursor past it so completed nodes are never re-dispatched.\n // Stale non-completed entries (the failed node that crashed the run,\n // and any `skipped` tail) are pruned so the re-run repopulates them\n // cleanly instead of duplicating. No-op on a cold run (empty ledger).\n if (this.args.resumeFrom) {\n index = this.rehydrateSequentialState(steps, previousOutputs);\n }\n\n while (index < steps.length) {\n const step = steps[index] as PlannerStep;\n\n if (index >= this.args.maxSteps) {\n this.recordSkipped(index, step);\n index++;\n continue;\n }\n\n if (this.isAborted()) {\n this.markCancelled();\n this.recordSkipped(index, step);\n index++;\n continue;\n }\n\n const completed = await this.executeStep(index, step, previousOutputs);\n\n const snapshot = this.snapshotFor(index);\n const directive = snapshot\n ? await this.resolveDirective(snapshot, plan, completed)\n : undefined;\n\n if (directive?.type === \"replan\") {\n const remaining = await this.regeneratePlan(directive.feedback);\n\n if (this.error || !remaining) {\n this.skipRest(steps, index + 1);\n return;\n }\n\n // Replace the remaining tail with the regenerated plan and restart\n // the cursor against it (executed steps already recorded stay put).\n // Each new step gets the executed-so-far digest as its context.\n steps = remaining.steps;\n index = 0;\n previousOutputs.length = 0;\n previousOutputs.push(...this.executedDigest());\n continue;\n }\n\n if (directive?.type === \"abort\") {\n // The hook (or an unhandled failure) asked to stop — record the\n // remaining steps as skipped so the report still describes the\n // whole intended plan, then stop.\n this.skipRest(steps, index + 1);\n return;\n }\n\n index++;\n }\n }\n\n /**\n * DAG executor — schedule independent `dependsOn` branches in parallel.\n *\n * Builds the DAG (cycle / unknown-id → `PlannerPlanInvalidError`),\n * then repeatedly computes the ready set (steps whose deps all\n * completed), dispatches up to `maxConcurrency` of them with\n * `Promise.all`, and feeds each step ONLY its dependencies' outputs. A\n * failed step blocks just its descendants (recorded `skipped`);\n * independent branches still settle. With an `output` schema set, the\n * final `data` is the topological SINK's output (multiple sinks → a\n * convergence error).\n */\n private async executeDag(plan: PlannerPlan): Promise<void> {\n const dag = buildDag(plan.steps, this.args.config.name);\n const maxConcurrency = Math.max(1, this.args.config.maxConcurrency ?? 4);\n\n const completed = new Set<string>();\n const done = new Set<string>();\n const outputs = new Map<string, string>();\n const rawOutputs = new Map<string, unknown>();\n let executedCount = 0;\n\n // Resume re-seed (DAG). Re-derive the scheduler's working sets from\n // the persisted ledger so `readyNodes` schedules only the unfinished\n // frontier — completed nodes go straight into `completed` + `done`\n // with their outputs restored; stale non-completed entries are pruned\n // so the re-run repopulates them. One-shot: consumed on the first DAG\n // pass so a later replan recursion (fresh plan, different node ids)\n // doesn't re-seed against a stale ledger. No-op on a cold run.\n if (this.args.resumeFrom && !this.dagResumeConsumed) {\n this.dagResumeConsumed = true;\n executedCount = this.rehydrateDagState(dag, completed, done, outputs, rawOutputs);\n }\n\n while (done.size < dag.nodes.length) {\n if (this.isAborted()) {\n this.markCancelled();\n this.skipDagRest(dag, done);\n return;\n }\n\n const ready = readyNodes(dag, completed, done);\n\n if (ready.length === 0) {\n // No node can advance — every remaining node transitively depends\n // on a failed/skipped ancestor. Record them skipped and stop.\n this.skipDagRest(dag, done);\n return;\n }\n\n const batch = ready.slice(0, maxConcurrency);\n\n const settled = await Promise.all(\n batch.map(async (node) => {\n // `maxSteps` truncation applies to the count of DISPATCHED steps.\n if (executedCount >= this.args.maxSteps) {\n this.recordSkipped(node.index, node.step);\n return { node, ran: false, completed: false };\n }\n\n executedCount++;\n // Feed this step ONLY its dependencies' output digests — the DAG\n // fix for the sequential loop's \"all prior outputs into every\n // step\" behavior. `executeStep` pushes into the array it is\n // given, so a fresh array per node keeps branches isolated.\n const previousOutputs = node.dependencies.map(\n (dependency) => outputs.get(dependency) as string,\n );\n const stepCompleted = await this.executeStep(\n node.index,\n node.step,\n previousOutputs,\n );\n\n if (stepCompleted) {\n // Read the raw output off the snapshot (NOT shared `this.data`,\n // which races under Promise.all) for both the dependent digest\n // and the eventual sink output.\n const rawOutput = this.snapshotFor(node.index)?.output;\n rawOutputs.set(node.id, rawOutput);\n outputs.set(node.id, this.stringifyOutput(node.step.capability, rawOutput));\n }\n\n return { node, ran: true, completed: stepCompleted };\n }),\n );\n\n for (const entry of settled) {\n done.add(entry.node.id);\n\n if (entry.completed) {\n completed.add(entry.node.id);\n }\n }\n\n // Fire the per-step hook for each settled step (in dispatch order).\n let replanFeedback: string | undefined;\n let shouldAbort = false;\n\n for (const entry of settled) {\n if (!entry.ran) {\n continue;\n }\n\n const snapshot = this.snapshotFor(entry.node.index);\n const directive = snapshot\n ? await this.resolveDirective(snapshot, plan, entry.completed)\n : undefined;\n\n if (directive?.type === \"replan\") {\n replanFeedback = directive.feedback;\n } else if (directive?.type === \"abort\") {\n shouldAbort = true;\n }\n }\n\n if (shouldAbort) {\n this.skipDagRest(dag, done);\n return;\n }\n\n if (replanFeedback !== undefined) {\n const remaining = await this.regeneratePlan(replanFeedback);\n\n if (this.error || !remaining) {\n this.skipDagRest(dag, done);\n return;\n }\n\n // Re-plan in DAG mode regenerates the remaining work as a fresh\n // (sequential) plan and runs it through the DAG scheduler again.\n return this.executeDag(remaining);\n }\n }\n\n this.finalizeDagOutput(dag, completed, rawOutputs);\n }\n\n /**\n * Dispatch one plan step through its capability's `executable.execute()`\n * and fold the outcome into the accumulators. Returns `true` when the\n * step completed, `false` when it failed (setting the run error).\n */\n private async executeStep(\n index: number,\n step: PlannerStep,\n previousOutputs: string[],\n ): Promise<boolean> {\n const capability = this.args.capabilities.get(step.capability) as PlannerCapability;\n const stepStart = performance.now();\n const startedAt = new Date().toISOString();\n const input = this.composeStepInput(step, previousOutputs);\n\n // `withoutRunFrame` keeps each capability step nested under the planner\n // only — `absorb` folds its report into `this.children`, so suppressing\n // its self-route prevents a duplicate standalone trace under observe-all.\n const result = await withoutRunFrame(() =>\n capability.executable.execute(input, {\n signal: this.args.options?.signal,\n sessionId: this.args.options?.sessionId,\n }),\n );\n\n const childReport = \"report\" in result ? (result.report as BaseReport) : undefined;\n this.absorb(result.usage, childReport);\n\n const output = this.extractOutput(result);\n const failed = result.error !== undefined;\n\n this.executedSteps.push({\n index,\n step,\n status: failed ? \"failed\" : \"completed\",\n output: failed ? undefined : output,\n error: result.error,\n startedAt,\n endedAt: new Date().toISOString(),\n duration: performance.now() - stepStart,\n usage: result.usage,\n childReport,\n });\n\n // Per-node durable checkpoint. Sits AFTER the node's snapshot is\n // pushed and `absorb` has folded its usage + child report — the only\n // point where the ledger + usage + children are mutually consistent.\n // A completed node is never re-dispatched on resume (the sequential\n // guard / DAG re-seed skip it). Swallow-and-log; no-op when `durable`\n // is absent.\n await this.checkpoint(\"running\");\n\n if (failed) {\n this.error = result.error;\n return false;\n }\n\n previousOutputs.push(this.stringifyOutput(step.capability, output));\n this.data = output as TOutput;\n\n return true;\n }\n\n /**\n * Resolve the steering directive for a just-settled step, shared by the\n * sequential and DAG executors. Fires the user's `onStep` hook, then\n * normalizes the result against the `replan` budget:\n *\n * - explicit `replan` directive — honored only when `config.replan` is\n * set and the budget remains; otherwise downgraded to `continue`.\n * - explicit `abort` — honored.\n * - failed step with no overriding directive — auto-`replan` when\n * `config.replan` is set and the budget remains (feedback = the step\n * error message), else `abort` (today's abort-on-first-failure).\n *\n * Returns `undefined` when the run should simply continue. A returned\n * `replan` directive has ALREADY consumed one unit of the replan budget.\n */\n private async resolveDirective(\n snapshot: PlannerStepSnapshot,\n plan: PlannerPlan,\n completed: boolean,\n ): Promise<PlannerStepDirective | undefined> {\n const hook = this.args.options?.onStep;\n const userDirective = hook ? await hook(snapshot, plan) : undefined;\n\n if (userDirective?.type === \"replan\") {\n if (this.canReplan()) {\n this.replanCount++;\n return userDirective;\n }\n\n // Replan requested but unavailable (no config or budget spent) — fall\n // through to the failure/continue defaults below.\n } else if (userDirective?.type === \"abort\") {\n return { type: \"abort\" };\n } else if (userDirective?.type === \"continue\") {\n return undefined;\n }\n\n if (!completed) {\n if (this.canReplan()) {\n this.replanCount++;\n return { type: \"replan\", feedback: snapshot.error?.message ?? \"step failed\" };\n }\n\n return { type: \"abort\" };\n }\n\n return undefined;\n }\n\n /** Whether a re-plan is configured and the budget has room. */\n private canReplan(): boolean {\n const replan = this.args.config.replan;\n\n return replan !== undefined && this.replanCount < replan.maxReplans;\n }\n\n /**\n * Re-ask the planning agent for a plan over the REMAINING work — a\n * second `generatePlan()` seeded with the executed-step digest plus the\n * caller's feedback. Reuses the exact `generatePlan` plumbing (same\n * schema, same `PlannerPlanInvalidError` handling), so a regenerated\n * plan that is empty or names an unknown capability fails identically.\n * The failed step's error is cleared so the regenerated plan runs\n * cleanly; a fresh failure (or exhausted budget) re-sets it.\n */\n private async regeneratePlan(feedback: string): Promise<PlannerPlan | undefined> {\n this.error = undefined;\n return this.generatePlan(feedback);\n }\n\n /**\n * The executed-so-far digest — one context line per completed step, in\n * execution order. Seeds the regenerated plan's first step so it builds\n * on what already ran.\n */\n private executedDigest(): string[] {\n return this.executedSteps\n .filter((snapshot) => snapshot.status === \"completed\")\n .map((snapshot) => this.stringifyOutput(snapshot.step.capability, snapshot.output));\n }\n\n /** The last-pushed snapshot for a given step index, if any. */\n private snapshotFor(index: number): PlannerStepSnapshot | undefined {\n for (let position = this.executedSteps.length - 1; position >= 0; position--) {\n const snapshot = this.executedSteps[position] as PlannerStepSnapshot;\n\n if (snapshot.index === index) {\n return snapshot;\n }\n }\n\n return undefined;\n }\n\n /** Record every step from `from` onward (in a flat array plan) as skipped. */\n private skipRest(steps: PlannerStep[], from: number): void {\n for (let rest = from; rest < steps.length; rest++) {\n this.recordSkipped(rest, steps[rest] as PlannerStep);\n }\n }\n\n /** Record every not-yet-`done` DAG node as skipped, in plan order. */\n private skipDagRest(dag: PlannerDag, done: ReadonlySet<string>): void {\n for (const node of dag.nodes) {\n if (!done.has(node.id)) {\n this.recordSkipped(node.index, node.step);\n }\n }\n }\n\n /**\n * Set `this.data` from the DAG's topological sink for a configured\n * `output` schema. \"Last completed step\" is meaningless under\n * parallelism, so the sink (the step nothing depends on) is the\n * unambiguous final output. Multiple sinks while an `output` schema is\n * set is a convergence error — a typed `PlannerPlanInvalidError`.\n */\n private finalizeDagOutput(\n dag: PlannerDag,\n completed: ReadonlySet<string>,\n rawOutputs: Map<string, unknown>,\n ): void {\n const schema = this.args.options?.output ?? this.args.config.output;\n\n if (!schema || this.error) {\n return;\n }\n\n const sinks = sinkNodes(dag).filter((node) => completed.has(node.id));\n\n if (sinks.length > 1) {\n this.error = new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): DAG has multiple sinks but an \\`output\\` schema is set — the plan must converge to a single final step`,\n { context: { runId: this.runId, sinks: sinks.map((node) => node.id) } },\n );\n this.data = undefined;\n return;\n }\n\n const sink = sinks[0] as DagNode | undefined;\n this.data = (sink ? rawOutputs.get(sink.id) : undefined) as TOutput | undefined;\n }\n\n /**\n * Phase 3 — when an `output` schema is configured (factory or per-call\n * override), validate the final completed step's output into typed\n * `result.data`. A validation failure replaces the run error and flips\n * the status to failed.\n */\n private async finalizeOutput(): Promise<void> {\n const schema = this.args.options?.output ?? this.args.config.output;\n\n if (!schema || this.error) {\n return;\n }\n\n if (this.data === undefined) {\n // An `output` schema is configured but the final completed step\n // produced nothing to validate — returning `{ data: undefined,\n // error: undefined, status: \"completed\" }` would be a silent\n // contract violation. Surface it as an invalid plan instead.\n this.error = new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): plan completed without producing output for the configured \\`output\\` schema`,\n { context: { runId: this.runId } },\n );\n return;\n }\n\n const validation = await schema[\"~standard\"].validate(this.data);\n\n if (validation.issues) {\n this.error = new PlannerPlanInvalidError(\n `ai.planner(\"${this.args.config.name}\"): final output failed validation`,\n {\n context: {\n runId: this.runId,\n issues: validation.issues.map((issue) => issue.message),\n },\n },\n );\n this.data = undefined;\n return;\n }\n\n this.data = validation.value as TOutput;\n }\n\n /**\n * Phase 4 — fold the accumulators into the planner's own\n * {@link PlannerReport} node and the final {@link PlannerResult}, then\n * stamp lineage across the whole subtree so every child shares this\n * run's root id.\n */\n private buildResult(): PlannerResult<TOutput> {\n const status = this.resolveStatus();\n\n const report: PlannerReport = {\n runId: this.runId,\n rootRunId: this.runId,\n name: this.args.config.name,\n version: this.args.config.version,\n type: \"planner\",\n status,\n // Stamp the terminal error so the observe path surfaces it on the\n // planner span (no result envelope reaches an observer). Absent on\n // a completed run.\n ...(this.error ? { error: this.error } : {}),\n startedAt: this.startedAt,\n endedAt: new Date().toISOString(),\n duration: performance.now() - this.startPerf,\n usage: this.usage,\n children: this.children,\n signature: this.args.signature,\n plan: this.plan,\n executedSteps: this.executedSteps,\n cancelledAt: this.cancelledAt,\n reportSchemaVersion: REPORT_SCHEMA_VERSION,\n };\n\n stampReportLineage(report, {\n rootRunId: this.runId,\n sessionId: this.args.options?.sessionId,\n });\n\n const result: PlannerResult<TOutput> = {\n type: \"planner\",\n data: this.error ? undefined : this.data,\n error: this.error,\n usage: this.usage,\n report,\n };\n\n // Plan-only mode surfaces the validated plan WITHOUT execution so the\n // caller can sign off and re-run with `approvedPlan`.\n if (this.awaitingApproval) {\n result.plan = this.plan;\n }\n\n return result;\n }\n\n /**\n * Resolve the terminal status from the accumulated outcome.\n * `awaiting-approval` (plan-only short-circuit) wins over everything —\n * nothing executed, so neither cancellation nor error applies.\n * Otherwise cancelled wins over failed (an abort that also produced a\n * step error still reads as cancelled); failed wins over completed.\n */\n private resolveStatus(): PlannerReport[\"status\"] {\n if (this.awaitingApproval) {\n return \"awaiting-approval\";\n }\n\n if (this.cancelledAt !== undefined) {\n return \"cancelled\";\n }\n\n if (this.error) {\n return \"failed\";\n }\n\n return \"completed\";\n }\n\n /**\n * Build the prompt handed to the planning agent. On the first pass this\n * is just the user's goal (byte-for-byte unchanged). On a RE-plan it\n * prepends the executed-step digest and the steering feedback so the\n * planner revises the remaining work.\n */\n private buildPlanPrompt(feedback?: string): string {\n if (feedback === undefined) {\n return this.args.goal;\n }\n\n const digest = this.executedDigest();\n const sections: string[] = [`Goal: ${this.args.goal}`, \"\"];\n\n if (digest.length > 0) {\n sections.push(\"Steps already completed:\", ...digest, \"\");\n }\n\n sections.push(\n `Feedback requiring a revised plan: ${feedback}`,\n \"\",\n \"Produce a plan for the REMAINING work only.\",\n );\n\n return sections.join(\"\\n\");\n }\n\n /**\n * Compose a step's effective input: the step's own `input`, prefixed\n * with a compact digest of every prior step's output so a downstream\n * capability can build on what ran before it. No prior output → the\n * step's raw input.\n */\n private composeStepInput(step: PlannerStep, previousOutputs: string[]): string {\n if (previousOutputs.length === 0) {\n return step.input;\n }\n\n return [\n \"Context from earlier steps:\",\n ...previousOutputs,\n \"\",\n `Task: ${step.input}`,\n ].join(\"\\n\");\n }\n\n /**\n * Pull the usable output off a capability's result. Prefers structured\n * `data` (agents/workflows with an `output` schema, tools), and falls\n * back to an agent's raw `text` when no structured data was produced —\n * the common case for a plain text-producing capability agent.\n */\n private extractOutput(result: BaseResult): unknown {\n const shaped = result as { data?: unknown; text?: unknown };\n\n if (shaped.data !== undefined) {\n return shaped.data;\n }\n\n if (typeof shaped.text === \"string\") {\n return shaped.text;\n }\n\n return undefined;\n }\n\n /** Serialize a capability output into a single context line for the next step. */\n private stringifyOutput(capability: string, output: unknown): string {\n if (output === undefined) {\n return `- ${capability}: (no output)`;\n }\n\n if (typeof output === \"string\") {\n return `- ${capability}: ${output}`;\n }\n\n return `- ${capability}: ${JSON.stringify(output)}`;\n }\n\n /** Push a `skipped` snapshot for a step the planner never dispatched. */\n private recordSkipped(index: number, step: PlannerStep): void {\n const now = new Date().toISOString();\n\n this.executedSteps.push({\n index,\n step,\n status: \"skipped\",\n startedAt: now,\n endedAt: now,\n duration: 0,\n usage: { input: 0, output: 0, total: 0 },\n });\n }\n\n /** Fold a child's usage + report node into the planner's accumulators. */\n private absorb(usage: Usage, report: BaseReport | undefined): void {\n this.mergeUsage(this.usage, usage);\n\n if (report) {\n this.children.push(report);\n }\n }\n\n /**\n * Add a child's usage into the running total. Mirrors the batch\n * primitive's rollup: scalar token channels sum directly, optional\n * sub-channels accumulate only when reported, and the cost breakdown\n * merges via {@link accumulateCost} so one unpriced child can't erase\n * priced siblings.\n */\n private mergeUsage(target: Usage, child: Usage): void {\n target.input += child.input;\n target.output += child.output;\n target.total += child.total;\n\n if (child.cachedTokens !== undefined) {\n target.cachedTokens = (target.cachedTokens ?? 0) + child.cachedTokens;\n }\n\n if (child.reasoningTokens !== undefined) {\n target.reasoningTokens = (target.reasoningTokens ?? 0) + child.reasoningTokens;\n }\n\n if (child.cacheWriteTokens !== undefined) {\n target.cacheWriteTokens = (target.cacheWriteTokens ?? 0) + child.cacheWriteTokens;\n }\n\n const mergedCost = accumulateCost(target.cost, child.cost);\n\n if (mergedCost !== undefined) {\n target.cost = mergedCost;\n }\n }\n\n /**\n * Re-derive the sequential cursor + prior-output context from the\n * persisted ledger on resume. Threads every already-`completed` node's\n * output into `previousOutputs`, returns the first index NOT completed\n * as the resume cursor, and prunes stale non-completed ledger entries\n * (the failed node + any skipped tail) at-or-after that cursor so the\n * re-run repopulates them without duplicating.\n */\n private rehydrateSequentialState(\n steps: PlannerStep[],\n previousOutputs: string[],\n ): number {\n let cursor = 0;\n\n for (let index = 0; index < steps.length; index++) {\n const snapshot = this.snapshotFor(index);\n\n if (snapshot?.status === \"completed\") {\n const step = steps[index] as PlannerStep;\n previousOutputs.push(this.stringifyOutput(step.capability, snapshot.output));\n cursor = index + 1;\n continue;\n }\n\n // First non-completed index — this is where the re-run resumes.\n break;\n }\n\n // Drop any ledger entries at-or-after the cursor (failed / skipped\n // from the crashed run) so the resumed loop's pushes don't duplicate.\n this.pruneLedgerFrom(cursor);\n\n return cursor;\n }\n\n /**\n * Re-derive the DAG scheduler's working sets from the persisted ledger\n * on resume. Completed nodes go into `completed` + `done` with their\n * string + raw outputs restored (so dependents read the right context);\n * stale non-completed entries are pruned so the re-run repopulates them.\n * Returns the count of nodes already dispatched (for the `maxSteps`\n * truncation budget).\n */\n private rehydrateDagState(\n dag: PlannerDag,\n completed: Set<string>,\n done: Set<string>,\n outputs: Map<string, string>,\n rawOutputs: Map<string, unknown>,\n ): number {\n const completedIndices = new Set<number>();\n\n for (const node of dag.nodes) {\n const snapshot = this.snapshotFor(node.index);\n\n if (snapshot?.status !== \"completed\") {\n continue;\n }\n\n completed.add(node.id);\n done.add(node.id);\n completedIndices.add(node.index);\n rawOutputs.set(node.id, snapshot.output);\n outputs.set(node.id, this.stringifyOutput(node.step.capability, snapshot.output));\n }\n\n // Prune every non-completed ledger entry so the re-run's pushes don't\n // duplicate the failed / skipped frontier from the crashed run.\n const retained = this.executedSteps.filter((snapshot) =>\n completedIndices.has(snapshot.index),\n );\n this.executedSteps.length = 0;\n this.executedSteps.push(...retained);\n\n return completedIndices.size;\n }\n\n /**\n * Drop every ledger entry whose index is at or after `from`. Used by\n * the sequential resume re-seed to clear the crashed run's failed /\n * skipped frontier before the re-run repopulates it.\n */\n private pruneLedgerFrom(from: number): void {\n const retained = this.executedSteps.filter((snapshot) => snapshot.index < from);\n this.executedSteps.length = 0;\n this.executedSteps.push(...retained);\n }\n\n /**\n * Map the run's terminal outcome to the persisted snapshot status.\n * `awaiting-approval` (plan-only) never persists a durable snapshot\n * (resume is always an execution), so it folds to `running` here —\n * but the durable + plan-only combination is disallowed at the call\n * site, so this path is effectively unreachable.\n */\n private resolveSnapshotStatus(): PlannerSnapshotStatus {\n if (this.cancelledAt !== undefined) {\n return \"cancelled\";\n }\n\n if (this.error) {\n return \"failed\";\n }\n\n if (this.awaitingApproval) {\n return \"running\";\n }\n\n return \"completed\";\n }\n\n /**\n * Build and persist a {@link PlannerSnapshot} from the current\n * accumulators. The per-node and terminal checkpoints both route\n * through here. No-op when `durable` is absent. A failed persist is\n * logged and swallowed (never aborts the run), matching the supervisor\n * / workflow checkpoint policy.\n */\n private async checkpoint(status: PlannerSnapshotStatus): Promise<void> {\n if (!this.args.config.durable || !this.plan) {\n return;\n }\n\n const outcome = await persistPlannerSnapshot({\n durable: this.args.config.durable,\n runId: this.runId,\n plannerName: this.args.config.name,\n signature: this.args.signature,\n version: this.args.config.version,\n goal: this.args.goal,\n plan: this.plan,\n executedSteps: this.executedSteps,\n usage: this.usage,\n children: this.children,\n replanCount: this.replanCount,\n status,\n startedAt: this.startedAt,\n });\n\n if (!outcome.ok) {\n this.logDurableFailure(\"snapshot.persist.failed\", outcome.error);\n }\n }\n\n /**\n * Re-derive the terminal state when a resume short-circuits a snapshot\n * whose run already COMPLETED. The persisted ledger is the\n * authoritative outcome — `this.data` is restored from the last\n * completed node so the rebuilt result carries the final output.\n *\n * Only reached for a `completed` snapshot — `failed` / `cancelled`\n * snapshots re-enter execution to retry the unfinished frontier instead.\n */\n private rebuildResumedTerminal(_status: PlannerSnapshotStatus): void {\n const lastCompleted = [...this.executedSteps]\n .reverse()\n .find((snapshot) => snapshot.status === \"completed\");\n\n if (lastCompleted) {\n this.data = lastCompleted.output as TOutput;\n }\n }\n\n /** Structured-log a durable persist/delete failure. */\n private logDurableFailure(action: string, error: unknown): void {\n log.warn(\"ai.planner\", action, \"durable snapshot operation failed\", {\n runId: this.runId,\n planner: this.args.config.name,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n\n /** Whether the caller's abort signal has fired. */\n private isAborted(): boolean {\n return this.args.options?.signal?.aborted === true;\n }\n\n /** Record a cancellation observation, setting the run error once. */\n private markCancelled(): void {\n if (this.cancelledAt !== undefined) {\n return;\n }\n\n this.cancelledAt = new Date().toISOString();\n\n const reason = this.args.options?.signal?.reason;\n\n this.error = new PlannerCancelledError(\n `ai.planner(\"${this.args.config.name}\"): run cancelled`,\n {\n cancelledAt: this.cancelledAt,\n reason: typeof reason === \"string\" ? reason : undefined,\n context: { runId: this.runId },\n },\n );\n }\n\n /** Normalize any thrown value into a typed {@link AIError}. */\n private toAIError(caught: unknown): AIError {\n if (caught instanceof AIError) {\n return caught;\n }\n\n const message = caught instanceof Error ? caught.message : String(caught);\n\n return new PlannerFailedError(`ai.planner(\"${this.args.config.name}\"): ${message}`, {\n cause: caught,\n context: { runId: this.runId },\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai/src/planner/planner.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"planner.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai/src/planner/planner.ts"],"mappings":";;;;;;AAgDA;;;;;;;;;;;;;;;;AAE0B;;;;;;;;;;;iBAFV,OAAA,oBACd,MAAA,EAAQ,aAAA,CAAc,OAAA,IACrB,eAAA,CAAgB,OAAA"}
|
package/esm/planner/planner.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { PlannerFailedError } from "../errors/planner-failed-error.mjs";
|
|
|
2
2
|
import "../errors/index.mjs";
|
|
3
3
|
import { agent } from "../agent/agent.mjs";
|
|
4
4
|
import { buildPlanSystemPrompt } from "./plan-prompt.mjs";
|
|
5
|
+
import { loadPlannerSnapshotForResume } from "./snapshot.mjs";
|
|
5
6
|
import { PlannerRun } from "./planner-run.mjs";
|
|
6
7
|
import { computeSignature } from "./signature.mjs";
|
|
7
8
|
import { log } from "@warlock.js/logger";
|
|
@@ -59,10 +60,33 @@ function planner(config) {
|
|
|
59
60
|
options
|
|
60
61
|
}).run();
|
|
61
62
|
}
|
|
63
|
+
async function resume(runId, options) {
|
|
64
|
+
const snapshot = await loadPlannerSnapshotForResume({
|
|
65
|
+
durable: config.durable,
|
|
66
|
+
plannerName: config.name,
|
|
67
|
+
signature,
|
|
68
|
+
runId,
|
|
69
|
+
options
|
|
70
|
+
});
|
|
71
|
+
return new PlannerRun({
|
|
72
|
+
config,
|
|
73
|
+
capabilities,
|
|
74
|
+
maxSteps,
|
|
75
|
+
signature,
|
|
76
|
+
planningAgent,
|
|
77
|
+
goal: snapshot.goal,
|
|
78
|
+
options: {
|
|
79
|
+
...options,
|
|
80
|
+
runId
|
|
81
|
+
},
|
|
82
|
+
resumeFrom: snapshot
|
|
83
|
+
}).run();
|
|
84
|
+
}
|
|
62
85
|
return {
|
|
63
86
|
name: config.name,
|
|
64
87
|
signature,
|
|
65
|
-
execute
|
|
88
|
+
execute,
|
|
89
|
+
resume
|
|
66
90
|
};
|
|
67
91
|
}
|
|
68
92
|
/**
|