@webpresso/agent-kit 0.21.4 → 0.21.5
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/.claude-plugin/marketplace.json +2 -2
- package/README.md +105 -41
- package/catalog/agent/skills/plan-refine/SKILL.md +5 -4
- package/catalog/base-kit/commitlint.config.ts.tmpl +1 -3
- package/catalog/base-kit/e2e/fixtures/smoke.html.tmpl +13 -0
- package/catalog/base-kit/e2e/smoke.spec.ts.tmpl +13 -0
- package/catalog/base-kit/oxlint.config.ts.tmpl +26 -0
- package/catalog/base-kit/playwright.config.ts.tmpl +10 -0
- package/catalog/base-kit/src/quality-sample.test.ts.tmpl +19 -0
- package/catalog/base-kit/src/quality-sample.ts.tmpl +11 -0
- package/catalog/base-kit/stryker.config.ts.tmpl +14 -0
- package/catalog/base-kit/tsconfig.json.tmpl +9 -0
- package/catalog/base-kit/vitest.config.ts.tmpl +10 -0
- package/catalog/docs/templates/adr.md +1 -1
- package/catalog/docs/templates/blueprint.md +1 -0
- package/catalog/docs/templates/blueprint.yaml +6 -3
- package/catalog/docs/templates/guide.md +1 -1
- package/catalog/docs/templates/postmortem.md +1 -1
- package/catalog/docs/templates/research.md +1 -1
- package/catalog/docs/templates/runbook.md +1 -1
- package/catalog/docs/templates/system.md +12 -3
- package/catalog/docs/templates/tech-debt.md +1 -0
- package/commands/blueprint.md +37 -4
- package/dist/esm/audit/resolve-audit-script.d.ts +24 -0
- package/dist/esm/audit/resolve-audit-script.js +27 -0
- package/dist/esm/blueprint/index.d.ts +0 -1
- package/dist/esm/blueprint/index.js +0 -2
- package/dist/esm/blueprint/local.d.ts +0 -3
- package/dist/esm/blueprint/local.js +0 -2
- package/dist/esm/blueprint/service/BlueprintCreationService.js +5 -2
- package/dist/esm/blueprint/utils/package-assets.d.ts +11 -0
- package/dist/esm/blueprint/utils/package-assets.js +33 -4
- package/dist/esm/build/sync-catalog-doc-templates.d.ts +23 -0
- package/dist/esm/build/sync-catalog-doc-templates.js +93 -0
- package/dist/esm/cli/commands/audit.js +2 -7
- package/dist/esm/cli/commands/blueprint/router.js +5 -2
- package/dist/esm/cli/commands/blueprint/template-resolver.js +8 -4
- package/dist/esm/cli/commands/init/host-visibility.js +4 -2
- package/dist/esm/cli/commands/init/index.js +46 -7
- package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +12 -0
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +141 -6
- package/dist/esm/cli/commands/typecheck.js +10 -4
- package/dist/esm/e2e/command-builder.js +26 -7
- package/dist/esm/e2e/execution.js +4 -0
- package/dist/esm/e2e/run-planner.js +1 -0
- package/dist/esm/e2e/types.d.ts +1 -0
- package/dist/esm/format/index.js +7 -1
- package/dist/esm/lint/index.js +3 -1
- package/dist/esm/mcp/blueprint-server.js +361 -66
- package/dist/esm/mcp/tools/audit.js +2 -8
- package/dist/esm/mcp/tools/e2e.d.ts +1 -1
- package/dist/esm/package.json +3 -0
- package/dist/esm/test/command-builder.d.ts +1 -0
- package/dist/esm/test/command-builder.js +8 -2
- package/dist/esm/test-helpers/hermetic-env.d.ts +25 -0
- package/dist/esm/test-helpers/hermetic-env.js +31 -0
- package/dist/esm/tool-runtime/index.d.ts +5 -0
- package/dist/esm/tool-runtime/index.js +23 -0
- package/dist/esm/tool-runtime/resolve-runner.d.ts +13 -0
- package/dist/esm/tool-runtime/resolve-runner.js +40 -0
- package/package.json +12 -19
- package/skills/plan-refine/SKILL.md +5 -4
- package/dist/esm/blueprint/dag/cycle-detector.d.ts +0 -12
- package/dist/esm/blueprint/dag/cycle-detector.js +0 -46
- package/dist/esm/blueprint/dag/executor.d.ts +0 -140
- package/dist/esm/blueprint/dag/executor.js +0 -292
- package/dist/esm/blueprint/dag/index.d.ts +0 -20
- package/dist/esm/blueprint/dag/index.js +0 -17
- package/dist/esm/blueprint/dag/interfaces.d.ts +0 -56
- package/dist/esm/blueprint/dag/interfaces.js +0 -13
- package/dist/esm/blueprint/dag/local/independence.d.ts +0 -107
- package/dist/esm/blueprint/dag/local/independence.js +0 -231
- package/dist/esm/blueprint/dag/local/index.d.ts +0 -14
- package/dist/esm/blueprint/dag/local/index.js +0 -14
- package/dist/esm/blueprint/dag/local/package-graph.d.ts +0 -66
- package/dist/esm/blueprint/dag/local/package-graph.js +0 -148
- package/dist/esm/blueprint/dag/plan-parser.d.ts +0 -54
- package/dist/esm/blueprint/dag/plan-parser.js +0 -236
- package/dist/esm/blueprint/dag/task-graph-algorithms.d.ts +0 -13
- package/dist/esm/blueprint/dag/task-graph-algorithms.js +0 -236
- package/dist/esm/blueprint/dag/task-graph.d.ts +0 -171
- package/dist/esm/blueprint/dag/task-graph.js +0 -370
- package/dist/esm/blueprint/dag/types.d.ts +0 -17
- package/dist/esm/blueprint/dag/types.js +0 -2
- package/dist/esm/blueprint/graph/index.d.ts +0 -5
- package/dist/esm/blueprint/graph/index.js +0 -5
- package/dist/esm/blueprint/graph/mermaid-parser.d.ts +0 -3
- package/dist/esm/blueprint/graph/mermaid-parser.js +0 -93
- package/dist/esm/blueprint/graph/mermaid-serializer.d.ts +0 -3
- package/dist/esm/blueprint/graph/mermaid-serializer.js +0 -20
- package/dist/esm/blueprint/graph/schema.d.ts +0 -89
- package/dist/esm/blueprint/graph/schema.js +0 -104
- package/dist/esm/blueprint/graph/task-graph-adapter.d.ts +0 -6
- package/dist/esm/blueprint/graph/task-graph-adapter.js +0 -30
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ManagedRunnerResolution {
|
|
2
|
+
readonly tool: string;
|
|
3
|
+
readonly command: string;
|
|
4
|
+
readonly args: readonly string[];
|
|
5
|
+
readonly source: 'managed' | 'fallback';
|
|
6
|
+
}
|
|
7
|
+
export interface ResolveRunnerOptions {
|
|
8
|
+
readonly fallbackCommand?: string;
|
|
9
|
+
readonly fallbackArgs?: readonly string[];
|
|
10
|
+
readonly filterOutput?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function resolveRunner(tool: string, options?: ResolveRunnerOptions): ManagedRunnerResolution;
|
|
13
|
+
//# sourceMappingURL=resolve-runner.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const MANAGED_TOOL_PREFIX = {
|
|
2
|
+
playwright: { command: 'vp', args: ['exec', 'playwright'] },
|
|
3
|
+
vitest: { command: 'vp', args: ['exec', 'vitest'] },
|
|
4
|
+
vp: { command: 'vp', args: [] },
|
|
5
|
+
};
|
|
6
|
+
function withOptionalRtk(resolution, filterOutput) {
|
|
7
|
+
if (!filterOutput)
|
|
8
|
+
return resolution;
|
|
9
|
+
return {
|
|
10
|
+
...resolution,
|
|
11
|
+
command: 'rtk',
|
|
12
|
+
args: [resolution.command, ...resolution.args],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function resolveRunner(tool, options = {}) {
|
|
16
|
+
const normalized = tool.trim();
|
|
17
|
+
if (!normalized) {
|
|
18
|
+
throw new Error('tool runtime resolution requires a non-empty tool name');
|
|
19
|
+
}
|
|
20
|
+
const filterOutput = options.filterOutput ?? true;
|
|
21
|
+
const managed = MANAGED_TOOL_PREFIX[normalized];
|
|
22
|
+
if (managed) {
|
|
23
|
+
return withOptionalRtk({
|
|
24
|
+
tool: normalized,
|
|
25
|
+
command: managed.command,
|
|
26
|
+
args: [...managed.args],
|
|
27
|
+
source: 'managed',
|
|
28
|
+
}, filterOutput);
|
|
29
|
+
}
|
|
30
|
+
if (options.fallbackCommand) {
|
|
31
|
+
return withOptionalRtk({
|
|
32
|
+
tool: normalized,
|
|
33
|
+
command: options.fallbackCommand,
|
|
34
|
+
args: [...(options.fallbackArgs ?? [])],
|
|
35
|
+
source: 'fallback',
|
|
36
|
+
}, filterOutput);
|
|
37
|
+
}
|
|
38
|
+
throw new Error(`No managed runtime runner is defined for tool "${normalized}"`);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=resolve-runner.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpresso/agent-kit",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"registry": "https://registry.npmjs.org/",
|
|
15
15
|
"access": "public"
|
|
16
16
|
},
|
|
17
|
-
"description": "
|
|
17
|
+
"description": "TypeScript infrastructure for AI-agent-driven development: the `wp` CLI + MCP server give agents planning (blueprints), tests, mutation, e2e, CI, docs, and tech-debt — summary-first so they keep context, and enforced as pre-commit/CI contracts. Manages AGENTS.md / CLAUDE.md and per-agent surfaces.",
|
|
18
18
|
"keywords": [
|
|
19
19
|
"agent",
|
|
20
20
|
"webpresso",
|
|
@@ -113,6 +113,9 @@
|
|
|
113
113
|
"#mcp/*": "./src/mcp/*.ts",
|
|
114
114
|
"#content/*.js": "./src/content/*.ts",
|
|
115
115
|
"#content/*": "./src/content/*.ts",
|
|
116
|
+
"#tool-runtime": "./src/tool-runtime/index.ts",
|
|
117
|
+
"#tool-runtime/*.js": "./src/tool-runtime/*.ts",
|
|
118
|
+
"#tool-runtime/*": "./src/tool-runtime/*.ts",
|
|
116
119
|
"#output-transforms/*.js": "./src/output-transforms/*.ts",
|
|
117
120
|
"#output-transforms/*": "./src/output-transforms/*.ts",
|
|
118
121
|
"#lint/*.js": "./src/lint/*.ts",
|
|
@@ -167,18 +170,6 @@
|
|
|
167
170
|
"default": "./dist/esm/blueprint/index.js"
|
|
168
171
|
}
|
|
169
172
|
},
|
|
170
|
-
"./blueprint/dag": {
|
|
171
|
-
"import": {
|
|
172
|
-
"types": "./dist/esm/blueprint/dag/index.d.ts",
|
|
173
|
-
"default": "./dist/esm/blueprint/dag/index.js"
|
|
174
|
-
}
|
|
175
|
-
},
|
|
176
|
-
"./blueprint/dag/local": {
|
|
177
|
-
"import": {
|
|
178
|
-
"types": "./dist/esm/blueprint/dag/local/index.d.ts",
|
|
179
|
-
"default": "./dist/esm/blueprint/dag/local/index.js"
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
173
|
"./blueprint/local": {
|
|
183
174
|
"import": {
|
|
184
175
|
"types": "./dist/esm/blueprint/local.d.ts",
|
|
@@ -514,18 +505,21 @@
|
|
|
514
505
|
"scripts": {
|
|
515
506
|
"setup:agent": "wp setup",
|
|
516
507
|
"build": "tshy && vp run chmod-bins && vp run link-self-bins",
|
|
517
|
-
"postbuild": "vp run generate-skills",
|
|
508
|
+
"postbuild": "vp run generate-skills && vp run sync-doc-templates",
|
|
518
509
|
"chmod-bins": "bun scripts/chmod-bins.ts",
|
|
519
510
|
"link-self-bins": "bun scripts/link-self-bins.ts",
|
|
520
511
|
"dev:link": "bun scripts/link-edge-local.ts",
|
|
521
512
|
"prepack": "bun src/build/package-manifest.ts prepare",
|
|
522
513
|
"postpack": "bun src/build/package-manifest.ts restore",
|
|
523
514
|
"generate-skills": "bun src/build/generate-skills-dir.ts",
|
|
515
|
+
"sync-doc-templates": "bun src/build/sync-catalog-doc-templates.ts",
|
|
524
516
|
"sync-marketplace-version": "bun scripts/sync-marketplace-version.ts",
|
|
525
517
|
"version": "changeset version && vp run sync-marketplace-version",
|
|
526
|
-
"release:publish": "
|
|
518
|
+
"release:publish": "bun scripts/release-publish.ts",
|
|
527
519
|
"typecheck": "tsc --noEmit",
|
|
528
|
-
"test": "vitest run",
|
|
520
|
+
"test": "vitest run --exclude '**/*.integration.test.ts' && vitest run --no-file-parallelism .integration.test.ts",
|
|
521
|
+
"test:unit": "vitest run --exclude '**/*.integration.test.ts'",
|
|
522
|
+
"test:integration": "vitest run --no-file-parallelism .integration.test.ts",
|
|
529
523
|
"test:mutation": "bun stryker run stryker.config.ts",
|
|
530
524
|
"eval": "bun src/runners/evals/index.ts",
|
|
531
525
|
"lint": "vp lint",
|
|
@@ -540,6 +534,7 @@
|
|
|
540
534
|
"imports:check": "WP_SKIP_UPDATE_CHECK=1 wp audit no-relative-parent-imports",
|
|
541
535
|
"verify:secrets": "bun scripts/check-no-dev-vars.ts",
|
|
542
536
|
"audit:secret-provider-quarantine": "bun scripts/audit-secret-provider-quarantine.ts",
|
|
537
|
+
"public:consumer-smoke": "bun scripts/public-consumer-smoke.ts",
|
|
543
538
|
"public:readiness": "bun scripts/public-readiness.ts",
|
|
544
539
|
"audits:check": "WP_SKIP_UPDATE_CHECK=1 wp audit guardrails && vp run hooks:doctor:ci",
|
|
545
540
|
"hooks:doctor": "WP_SKIP_UPDATE_CHECK=1 wp hooks doctor",
|
|
@@ -608,8 +603,6 @@
|
|
|
608
603
|
"exports": {
|
|
609
604
|
".": "./src/index.ts",
|
|
610
605
|
"./blueprint": "./src/blueprint/index.ts",
|
|
611
|
-
"./blueprint/dag": "./src/blueprint/dag/index.ts",
|
|
612
|
-
"./blueprint/dag/local": "./src/blueprint/dag/local/index.ts",
|
|
613
606
|
"./blueprint/local": "./src/blueprint/local.ts",
|
|
614
607
|
"./blueprint/tech-debt": "./src/blueprint/tech-debt/index.ts",
|
|
615
608
|
"./blueprint/test-utils": "./src/blueprint/test-utils/blueprint-mocks.ts",
|
|
@@ -16,10 +16,11 @@ description: Methodology for refining, fact-checking, and hardening implementati
|
|
|
16
16
|
>
|
|
17
17
|
> Blueprint hardening and lifecycle infrastructure are production-ready:
|
|
18
18
|
>
|
|
19
|
-
> -
|
|
20
|
-
> -
|
|
21
|
-
> -
|
|
22
|
-
>
|
|
19
|
+
> - Blueprint dependency parsing, format validation, and the lifecycle state
|
|
20
|
+
> machine with evidence-gated task completion (fully tested, in-package)
|
|
21
|
+
> - Parallel execution is delegated to the `/pll` runtime adapter (OMX), which
|
|
22
|
+
> batches independent tasks by dependency order and blocks dependents of
|
|
23
|
+
> failed tasks
|
|
23
24
|
>
|
|
24
25
|
> **Remaining gaps:** execution docs and wrappers must stay aligned with the currently shipped runtime and CLI surface.
|
|
25
26
|
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export declare class CycleDetector {
|
|
2
|
-
private edges;
|
|
3
|
-
private visited;
|
|
4
|
-
private recStack;
|
|
5
|
-
private path;
|
|
6
|
-
private cycles;
|
|
7
|
-
constructor(edges: Map<string, Set<string>>);
|
|
8
|
-
detect(nodes: IterableIterator<string>): string[][] | null;
|
|
9
|
-
private dfs;
|
|
10
|
-
private processNeighbor;
|
|
11
|
-
}
|
|
12
|
-
//# sourceMappingURL=cycle-detector.d.ts.map
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
export class CycleDetector {
|
|
2
|
-
edges;
|
|
3
|
-
visited = new Set();
|
|
4
|
-
recStack = new Set();
|
|
5
|
-
path = [];
|
|
6
|
-
cycles = [];
|
|
7
|
-
constructor(edges) {
|
|
8
|
-
this.edges = edges;
|
|
9
|
-
}
|
|
10
|
-
detect(nodes) {
|
|
11
|
-
for (const nodeId of nodes) {
|
|
12
|
-
if (!this.visited.has(nodeId) && this.dfs(nodeId)) {
|
|
13
|
-
return this.cycles;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return this.cycles.length > 0 ? this.cycles : null;
|
|
17
|
-
}
|
|
18
|
-
dfs(nodeId) {
|
|
19
|
-
this.visited.add(nodeId);
|
|
20
|
-
this.recStack.add(nodeId);
|
|
21
|
-
this.path.push(nodeId);
|
|
22
|
-
const edges = this.edges.get(nodeId);
|
|
23
|
-
if (edges) {
|
|
24
|
-
for (const neighbor of edges) {
|
|
25
|
-
if (this.processNeighbor(neighbor))
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
this.path.pop();
|
|
30
|
-
this.recStack.delete(nodeId);
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
processNeighbor(neighbor) {
|
|
34
|
-
if (!this.visited.has(neighbor)) {
|
|
35
|
-
if (this.dfs(neighbor))
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
else if (this.recStack.has(neighbor)) {
|
|
39
|
-
const cycleStart = this.path.indexOf(neighbor);
|
|
40
|
-
this.cycles.push([...this.path.slice(cycleStart), neighbor]);
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
//# sourceMappingURL=cycle-detector.js.map
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import type { IClock } from './interfaces.js';
|
|
2
|
-
import type { Task } from './types.js';
|
|
3
|
-
import { TaskGraph } from './task-graph.js';
|
|
4
|
-
/**
|
|
5
|
-
* Task execution result
|
|
6
|
-
*/
|
|
7
|
-
export interface TaskResult<T = unknown> {
|
|
8
|
-
taskId: string;
|
|
9
|
-
status: 'completed' | 'failed' | 'skipped';
|
|
10
|
-
output?: T;
|
|
11
|
-
error?: Error;
|
|
12
|
-
durationMs: number;
|
|
13
|
-
startedAt: number;
|
|
14
|
-
completedAt: number;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Concurrency configuration by task type
|
|
18
|
-
*/
|
|
19
|
-
export interface ConcurrencyConfig {
|
|
20
|
-
/** Global max concurrent tasks across all types */
|
|
21
|
-
global?: number;
|
|
22
|
-
/** Default max concurrent tasks per type (when no type-specific limit) */
|
|
23
|
-
default: number;
|
|
24
|
-
/** Per-type limits (overrides default) */
|
|
25
|
-
byType?: Record<string, number>;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Task executor function signature
|
|
29
|
-
* Receives task and returns result (or throws)
|
|
30
|
-
*/
|
|
31
|
-
export type TaskExecutorFn<T, R> = (task: Task<T>) => Promise<R>;
|
|
32
|
-
/**
|
|
33
|
-
* Execution progress callback
|
|
34
|
-
*/
|
|
35
|
-
export interface ExecutionProgress<T = unknown> {
|
|
36
|
-
totalTasks: number;
|
|
37
|
-
completedTasks: number;
|
|
38
|
-
failedTasks: number;
|
|
39
|
-
runningTasks: string[];
|
|
40
|
-
pendingTasks: number;
|
|
41
|
-
currentWave: number;
|
|
42
|
-
totalWaves: number;
|
|
43
|
-
latestResult?: TaskResult<T>;
|
|
44
|
-
}
|
|
45
|
-
export type ProgressCallback<T = unknown> = (progress: ExecutionProgress<T>) => void;
|
|
46
|
-
/**
|
|
47
|
-
* Executor options
|
|
48
|
-
*/
|
|
49
|
-
export interface ExecutorOptions<R> {
|
|
50
|
-
concurrency?: ConcurrencyConfig;
|
|
51
|
-
onProgress?: ProgressCallback<R>;
|
|
52
|
-
/** Clock for time operations (injectable for testing) */
|
|
53
|
-
clock?: IClock;
|
|
54
|
-
/** Skip tasks whose dependencies failed */
|
|
55
|
-
skipOnFailedDependency?: boolean;
|
|
56
|
-
/** Timeout for individual tasks in milliseconds (0 = no timeout) */
|
|
57
|
-
taskTimeoutMs?: number;
|
|
58
|
-
/** AbortSignal for cancellation */
|
|
59
|
-
signal?: AbortSignal;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Parallel DAG executor with concurrency controls.
|
|
63
|
-
*
|
|
64
|
-
* Uses Kahn's algorithm for topological execution order.
|
|
65
|
-
* Supports per-task-type concurrency limits.
|
|
66
|
-
*
|
|
67
|
-
* Runtime-agnostic - works in Node.js, Bun, Deno, Cloudflare Workers.
|
|
68
|
-
*/
|
|
69
|
-
export declare class ParallelExecutor<T = unknown, R = unknown> {
|
|
70
|
-
private graph;
|
|
71
|
-
private concurrency;
|
|
72
|
-
private executorFn;
|
|
73
|
-
private onProgress?;
|
|
74
|
-
private clock;
|
|
75
|
-
private skipOnFailedDependency;
|
|
76
|
-
private taskTimeoutMs;
|
|
77
|
-
private completed;
|
|
78
|
-
private failed;
|
|
79
|
-
private skipped;
|
|
80
|
-
private running;
|
|
81
|
-
private results;
|
|
82
|
-
private signal?;
|
|
83
|
-
private abortController?;
|
|
84
|
-
constructor(graph: TaskGraph<T>, executorFn: TaskExecutorFn<T, R>, options?: ExecutorOptions<R>);
|
|
85
|
-
/**
|
|
86
|
-
* Execute all tasks in parallel, respecting dependencies and concurrency limits.
|
|
87
|
-
* Returns results in completion order.
|
|
88
|
-
* @throws {Error} If graph is invalid or execution is aborted
|
|
89
|
-
*/
|
|
90
|
-
execute(): Promise<TaskResult<R>[]>;
|
|
91
|
-
/**
|
|
92
|
-
* Check if execution has been aborted and throw if so.
|
|
93
|
-
* Checks both internal controller and external signal.
|
|
94
|
-
*/
|
|
95
|
-
private throwIfAborted;
|
|
96
|
-
/**
|
|
97
|
-
* Get current execution state.
|
|
98
|
-
*/
|
|
99
|
-
getState(): {
|
|
100
|
-
completed: string[];
|
|
101
|
-
failed: string[];
|
|
102
|
-
skipped: string[];
|
|
103
|
-
running: string[];
|
|
104
|
-
};
|
|
105
|
-
private executeWave;
|
|
106
|
-
private startPendingTasks;
|
|
107
|
-
private shouldSkipTask;
|
|
108
|
-
private skipTask;
|
|
109
|
-
private canStartTask;
|
|
110
|
-
private getTaskType;
|
|
111
|
-
/**
|
|
112
|
-
* Count running tasks of a specific type.
|
|
113
|
-
* FIX: Now properly tracks task types.
|
|
114
|
-
*/
|
|
115
|
-
private countRunningByType;
|
|
116
|
-
private startTask;
|
|
117
|
-
/**
|
|
118
|
-
* Execute task with optional timeout support.
|
|
119
|
-
* State-of-the-art: proper timeout handling with AbortController pattern.
|
|
120
|
-
*/
|
|
121
|
-
private executeWithTimeout;
|
|
122
|
-
private createTimeoutPromise;
|
|
123
|
-
private emitTaskStart;
|
|
124
|
-
private waitForAny;
|
|
125
|
-
private emitProgress;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Create executor from task array with dependencies.
|
|
129
|
-
* Convenience function for common use case.
|
|
130
|
-
*/
|
|
131
|
-
export declare function createExecutor<T, R>(tasks: Array<{
|
|
132
|
-
task: Task<T>;
|
|
133
|
-
dependsOn?: string[];
|
|
134
|
-
}>, executorFn: TaskExecutorFn<T, R>, options?: ExecutorOptions<R>): ParallelExecutor<T, R>;
|
|
135
|
-
/**
|
|
136
|
-
* Create executor directly from tasks using their dependencies arrays.
|
|
137
|
-
* Most convenient for LLM agents.
|
|
138
|
-
*/
|
|
139
|
-
export declare function createExecutorFromTasks<T, R>(tasks: Task<T>[], executorFn: TaskExecutorFn<T, R>, options?: ExecutorOptions<R>): ParallelExecutor<T, R>;
|
|
140
|
-
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
import { realClock } from './interfaces.js';
|
|
2
|
-
import { TaskGraph } from './task-graph.js';
|
|
3
|
-
/**
|
|
4
|
-
* Parallel DAG executor with concurrency controls.
|
|
5
|
-
*
|
|
6
|
-
* Uses Kahn's algorithm for topological execution order.
|
|
7
|
-
* Supports per-task-type concurrency limits.
|
|
8
|
-
*
|
|
9
|
-
* Runtime-agnostic - works in Node.js, Bun, Deno, Cloudflare Workers.
|
|
10
|
-
*/
|
|
11
|
-
export class ParallelExecutor {
|
|
12
|
-
graph;
|
|
13
|
-
concurrency;
|
|
14
|
-
executorFn;
|
|
15
|
-
onProgress;
|
|
16
|
-
clock;
|
|
17
|
-
skipOnFailedDependency;
|
|
18
|
-
taskTimeoutMs;
|
|
19
|
-
// Execution state
|
|
20
|
-
completed = new Set();
|
|
21
|
-
failed = new Set();
|
|
22
|
-
skipped = new Set();
|
|
23
|
-
running = new Map();
|
|
24
|
-
results = [];
|
|
25
|
-
signal;
|
|
26
|
-
abortController;
|
|
27
|
-
constructor(graph, executorFn, options = {}) {
|
|
28
|
-
this.graph = graph;
|
|
29
|
-
this.executorFn = executorFn;
|
|
30
|
-
this.concurrency = options.concurrency ?? { default: 6 };
|
|
31
|
-
this.onProgress = options.onProgress;
|
|
32
|
-
this.clock = options.clock ?? realClock;
|
|
33
|
-
this.skipOnFailedDependency = options.skipOnFailedDependency ?? true;
|
|
34
|
-
this.taskTimeoutMs = options.taskTimeoutMs ?? 0;
|
|
35
|
-
this.signal = options.signal;
|
|
36
|
-
// Create internal abort controller for cleanup
|
|
37
|
-
this.abortController = new AbortController();
|
|
38
|
-
// Link external signal if provided
|
|
39
|
-
if (options.signal) {
|
|
40
|
-
options.signal.addEventListener('abort', () => {
|
|
41
|
-
this.abortController?.abort(options.signal?.reason);
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Execute all tasks in parallel, respecting dependencies and concurrency limits.
|
|
47
|
-
* Returns results in completion order.
|
|
48
|
-
* @throws {Error} If graph is invalid or execution is aborted
|
|
49
|
-
*/
|
|
50
|
-
async execute() {
|
|
51
|
-
// Check for abort before starting
|
|
52
|
-
this.throwIfAborted();
|
|
53
|
-
// Validate graph first
|
|
54
|
-
const validation = this.graph.validate();
|
|
55
|
-
if (!validation.valid) {
|
|
56
|
-
throw new Error(`Cannot execute invalid graph: ${validation.errors.join('; ')}`);
|
|
57
|
-
}
|
|
58
|
-
const waves = this.graph.getWaves();
|
|
59
|
-
const totalTasks = waves.flat().length;
|
|
60
|
-
for (let waveIdx = 0; waveIdx < waves.length; waveIdx++) {
|
|
61
|
-
this.throwIfAborted();
|
|
62
|
-
const wave = waves[waveIdx];
|
|
63
|
-
if (!wave)
|
|
64
|
-
continue;
|
|
65
|
-
await this.executeWave(wave, waveIdx + 1, waves.length, totalTasks);
|
|
66
|
-
}
|
|
67
|
-
return this.results;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Check if execution has been aborted and throw if so.
|
|
71
|
-
* Checks both internal controller and external signal.
|
|
72
|
-
*/
|
|
73
|
-
throwIfAborted() {
|
|
74
|
-
// Check external signal first (passed in via options)
|
|
75
|
-
if (this.signal?.aborted) {
|
|
76
|
-
throw new Error(`Execution aborted: ${this.signal.reason || 'user request'}`);
|
|
77
|
-
}
|
|
78
|
-
// Check internal controller
|
|
79
|
-
if (this.abortController?.signal.aborted) {
|
|
80
|
-
throw new Error(`Execution aborted: ${this.abortController.signal.reason || 'user request'}`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Get current execution state.
|
|
85
|
-
*/
|
|
86
|
-
getState() {
|
|
87
|
-
return {
|
|
88
|
-
completed: Array.from(this.completed),
|
|
89
|
-
failed: Array.from(this.failed),
|
|
90
|
-
skipped: Array.from(this.skipped),
|
|
91
|
-
running: Array.from(this.running.keys()),
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
async executeWave(tasks, waveNum, totalWaves, totalTasks) {
|
|
95
|
-
const pending = [...tasks];
|
|
96
|
-
while (pending.length > 0 || this.running.size > 0) {
|
|
97
|
-
this.startPendingTasks(pending, waveNum, totalWaves, totalTasks);
|
|
98
|
-
if (this.running.size === 0)
|
|
99
|
-
break;
|
|
100
|
-
const result = await this.waitForAny();
|
|
101
|
-
this.results.push(result);
|
|
102
|
-
this.emitProgress(waveNum, totalWaves, totalTasks, result);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
startPendingTasks(pending, waveNum, totalWaves, totalTasks) {
|
|
106
|
-
while (pending[0] && this.canStartTask(pending[0])) {
|
|
107
|
-
const task = pending.shift();
|
|
108
|
-
if (!task)
|
|
109
|
-
break;
|
|
110
|
-
if (this.shouldSkipTask(task)) {
|
|
111
|
-
this.skipTask(task, waveNum, totalWaves, totalTasks);
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
this.startTask(task);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
shouldSkipTask(task) {
|
|
119
|
-
if (!this.skipOnFailedDependency)
|
|
120
|
-
return false;
|
|
121
|
-
// Check if any dependency failed
|
|
122
|
-
const deps = this.graph.getDependencies(task.id);
|
|
123
|
-
for (const dep of deps) {
|
|
124
|
-
if (this.failed.has(dep) || this.skipped.has(dep)) {
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
skipTask(task, waveNum, totalWaves, totalTasks) {
|
|
131
|
-
const now = this.clock.now();
|
|
132
|
-
this.skipped.add(task.id);
|
|
133
|
-
const result = {
|
|
134
|
-
taskId: task.id,
|
|
135
|
-
status: 'skipped',
|
|
136
|
-
durationMs: 0,
|
|
137
|
-
startedAt: now,
|
|
138
|
-
completedAt: now,
|
|
139
|
-
error: new Error('Skipped due to failed dependency'),
|
|
140
|
-
};
|
|
141
|
-
this.results.push(result);
|
|
142
|
-
this.emitProgress(waveNum, totalWaves, totalTasks, result);
|
|
143
|
-
}
|
|
144
|
-
canStartTask(task) {
|
|
145
|
-
const taskType = this.getTaskType(task);
|
|
146
|
-
const typeLimit = this.concurrency.byType?.[taskType] ?? this.concurrency.default;
|
|
147
|
-
const currentOfType = this.countRunningByType(taskType);
|
|
148
|
-
// Check type-specific limit
|
|
149
|
-
if (currentOfType >= typeLimit)
|
|
150
|
-
return false;
|
|
151
|
-
// Check global limit (defaults to default if not specified)
|
|
152
|
-
const globalLimit = this.concurrency.global ?? this.concurrency.default;
|
|
153
|
-
if (this.running.size >= globalLimit)
|
|
154
|
-
return false;
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
getTaskType(task) {
|
|
158
|
-
// Extract type from task metadata if available
|
|
159
|
-
const meta = task.data;
|
|
160
|
-
const typeValue = meta?.type;
|
|
161
|
-
// Validate type is a string
|
|
162
|
-
if (typeof typeValue === 'string' && typeValue.length > 0) {
|
|
163
|
-
return typeValue;
|
|
164
|
-
}
|
|
165
|
-
return 'default';
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Count running tasks of a specific type.
|
|
169
|
-
* FIX: Now properly tracks task types.
|
|
170
|
-
*/
|
|
171
|
-
countRunningByType(type) {
|
|
172
|
-
let count = 0;
|
|
173
|
-
for (const info of this.running.values()) {
|
|
174
|
-
if (info.taskType === type) {
|
|
175
|
-
count++;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return count;
|
|
179
|
-
}
|
|
180
|
-
startTask(task) {
|
|
181
|
-
const startTime = this.clock.now();
|
|
182
|
-
const taskType = this.getTaskType(task);
|
|
183
|
-
// Emit task start via progress callback (state-of-the-art observability)
|
|
184
|
-
this.emitTaskStart(task.id, taskType);
|
|
185
|
-
const promise = this.executeWithTimeout(task, startTime)
|
|
186
|
-
.then((output) => ({
|
|
187
|
-
taskId: task.id,
|
|
188
|
-
status: 'completed',
|
|
189
|
-
output,
|
|
190
|
-
durationMs: this.clock.now() - startTime,
|
|
191
|
-
startedAt: startTime,
|
|
192
|
-
completedAt: this.clock.now(),
|
|
193
|
-
}))
|
|
194
|
-
.catch((error) => ({
|
|
195
|
-
taskId: task.id,
|
|
196
|
-
status: 'failed',
|
|
197
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
198
|
-
durationMs: this.clock.now() - startTime,
|
|
199
|
-
startedAt: startTime,
|
|
200
|
-
completedAt: this.clock.now(),
|
|
201
|
-
}))
|
|
202
|
-
.finally(() => {
|
|
203
|
-
this.running.delete(task.id);
|
|
204
|
-
});
|
|
205
|
-
// Track completion status (fire-and-forget)
|
|
206
|
-
void promise.then((r) => {
|
|
207
|
-
if (r.status === 'completed') {
|
|
208
|
-
this.completed.add(task.id);
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
this.failed.add(task.id);
|
|
212
|
-
}
|
|
213
|
-
return;
|
|
214
|
-
});
|
|
215
|
-
this.running.set(task.id, {
|
|
216
|
-
taskId: task.id,
|
|
217
|
-
taskType,
|
|
218
|
-
promise,
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Execute task with optional timeout support.
|
|
223
|
-
* State-of-the-art: proper timeout handling with AbortController pattern.
|
|
224
|
-
*/
|
|
225
|
-
executeWithTimeout(task, _startTime) {
|
|
226
|
-
if (this.taskTimeoutMs <= 0) {
|
|
227
|
-
return this.executorFn(task);
|
|
228
|
-
}
|
|
229
|
-
return Promise.race([this.executorFn(task), this.createTimeoutPromise(task.id)]);
|
|
230
|
-
}
|
|
231
|
-
createTimeoutPromise(taskId) {
|
|
232
|
-
return new Promise((resolve) => {
|
|
233
|
-
setTimeout(() => resolve(), this.taskTimeoutMs);
|
|
234
|
-
}).then(() => {
|
|
235
|
-
throw new Error(`Task "${taskId}" timed out after ${this.taskTimeoutMs}ms`);
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
emitTaskStart(_taskId, _taskType) {
|
|
239
|
-
if (!this.onProgress)
|
|
240
|
-
return;
|
|
241
|
-
// Progress callback gets task start info via runningTasks update
|
|
242
|
-
// This is called before the task is added to running, so it appears in next progress update
|
|
243
|
-
}
|
|
244
|
-
waitForAny() {
|
|
245
|
-
const promises = Array.from(this.running.values()).map((info) => info.promise);
|
|
246
|
-
return Promise.race(promises);
|
|
247
|
-
}
|
|
248
|
-
emitProgress(currentWave, totalWaves, totalTasks, latestResult) {
|
|
249
|
-
if (!this.onProgress)
|
|
250
|
-
return;
|
|
251
|
-
this.onProgress({
|
|
252
|
-
totalTasks,
|
|
253
|
-
completedTasks: this.completed.size,
|
|
254
|
-
failedTasks: this.failed.size,
|
|
255
|
-
runningTasks: Array.from(this.running.keys()),
|
|
256
|
-
pendingTasks: totalTasks - this.completed.size - this.failed.size - this.skipped.size - this.running.size,
|
|
257
|
-
currentWave,
|
|
258
|
-
totalWaves,
|
|
259
|
-
latestResult,
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Create executor from task array with dependencies.
|
|
265
|
-
* Convenience function for common use case.
|
|
266
|
-
*/
|
|
267
|
-
export function createExecutor(tasks, executorFn, options) {
|
|
268
|
-
const graph = new TaskGraph();
|
|
269
|
-
// Add all tasks first
|
|
270
|
-
for (const { task } of tasks) {
|
|
271
|
-
graph.addTask(task);
|
|
272
|
-
}
|
|
273
|
-
// Add dependencies
|
|
274
|
-
for (const { task, dependsOn } of tasks) {
|
|
275
|
-
if (dependsOn) {
|
|
276
|
-
for (const dep of dependsOn) {
|
|
277
|
-
graph.addDependency(dep, task.id);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
return new ParallelExecutor(graph, executorFn, options);
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* Create executor directly from tasks using their dependencies arrays.
|
|
285
|
-
* Most convenient for LLM agents.
|
|
286
|
-
*/
|
|
287
|
-
export function createExecutorFromTasks(tasks, executorFn, options) {
|
|
288
|
-
const graph = new TaskGraph();
|
|
289
|
-
graph.addTasksWithDependencies(tasks);
|
|
290
|
-
return new ParallelExecutor(graph, executorFn, options);
|
|
291
|
-
}
|
|
292
|
-
//# sourceMappingURL=executor.js.map
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* webpresso blueprint/dag
|
|
3
|
-
*
|
|
4
|
-
* Workers-safe DAG (Directed Acyclic Graph) analysis utilities.
|
|
5
|
-
* Zero Node.js dependencies — safe for Cloudflare Workers, Deno, Bun, and Node.js.
|
|
6
|
-
*
|
|
7
|
-
* For Node-only utilities (PackageGraph, IndependenceDetector),
|
|
8
|
-
* use the `dag/local` subpath instead.
|
|
9
|
-
*
|
|
10
|
-
* @packageDocumentation
|
|
11
|
-
*/
|
|
12
|
-
export type { ConcurrencyConfig, ExecutionProgress, ExecutorOptions, ProgressCallback, TaskExecutorFn, TaskResult, } from './executor.js';
|
|
13
|
-
export { createExecutor, createExecutorFromTasks, ParallelExecutor } from './executor.js';
|
|
14
|
-
export type { GraphStats, IClock, IFileSystem, IPackageGraph, ValidationResult, } from './interfaces.js';
|
|
15
|
-
export { realClock } from './interfaces.js';
|
|
16
|
-
export type { ParsedPlan, PlanTask } from './plan-parser.js';
|
|
17
|
-
export { parsePlan, planTasksToGraphTasks } from './plan-parser.js';
|
|
18
|
-
export { CycleDetector, TaskGraph } from './task-graph.js';
|
|
19
|
-
export type { Task, TaskNode } from './types.js';
|
|
20
|
-
//# sourceMappingURL=index.d.ts.map
|