slavedriver 0.1.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/README.md +165 -0
- package/agents/executor.md +26 -0
- package/agents/planner.md +41 -0
- package/agents/researcher.md +28 -0
- package/agents/verifier.md +25 -0
- package/dist/agents/backends/claude-code.d.ts +2 -0
- package/dist/agents/backends/claude-code.js +215 -0
- package/dist/agents/backends/claude-code.js.map +1 -0
- package/dist/agents/backends/mock.d.ts +9 -0
- package/dist/agents/backends/mock.js +31 -0
- package/dist/agents/backends/mock.js.map +1 -0
- package/dist/agents/context-builder.d.ts +10 -0
- package/dist/agents/context-builder.js +61 -0
- package/dist/agents/context-builder.js.map +1 -0
- package/dist/agents/prompt-compiler.d.ts +27 -0
- package/dist/agents/prompt-compiler.js +549 -0
- package/dist/agents/prompt-compiler.js.map +1 -0
- package/dist/agents/runtime.d.ts +40 -0
- package/dist/agents/runtime.js +2 -0
- package/dist/agents/runtime.js.map +1 -0
- package/dist/cli/arg-parser.d.ts +6 -0
- package/dist/cli/arg-parser.js +59 -0
- package/dist/cli/arg-parser.js.map +1 -0
- package/dist/cli/commands/config.d.ts +9 -0
- package/dist/cli/commands/config.js +120 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/dashboard.d.ts +1 -0
- package/dist/cli/commands/dashboard.js +54 -0
- package/dist/cli/commands/dashboard.js.map +1 -0
- package/dist/cli/commands/find-root.d.ts +14 -0
- package/dist/cli/commands/find-root.js +55 -0
- package/dist/cli/commands/find-root.js.map +1 -0
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.js +65 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/next.d.ts +1 -0
- package/dist/cli/commands/next.js +61 -0
- package/dist/cli/commands/next.js.map +1 -0
- package/dist/cli/commands/plan.d.ts +2 -0
- package/dist/cli/commands/plan.js +53 -0
- package/dist/cli/commands/plan.js.map +1 -0
- package/dist/cli/commands/replan.d.ts +1 -0
- package/dist/cli/commands/replan.js +54 -0
- package/dist/cli/commands/replan.js.map +1 -0
- package/dist/cli/commands/run-pipeline.d.ts +2 -0
- package/dist/cli/commands/run-pipeline.js +74 -0
- package/dist/cli/commands/run-pipeline.js.map +1 -0
- package/dist/cli/commands/run.d.ts +2 -0
- package/dist/cli/commands/run.js +106 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/status.d.ts +1 -0
- package/dist/cli/commands/status.js +51 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/verify.d.ts +1 -0
- package/dist/cli/commands/verify.js +63 -0
- package/dist/cli/commands/verify.js.map +1 -0
- package/dist/cli/commands/wizard.d.ts +8 -0
- package/dist/cli/commands/wizard.js +39 -0
- package/dist/cli/commands/wizard.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +82 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/wizard/index.d.ts +11 -0
- package/dist/cli/wizard/index.js +40 -0
- package/dist/cli/wizard/index.js.map +1 -0
- package/dist/cli/wizard/interview.d.ts +26 -0
- package/dist/cli/wizard/interview.js +284 -0
- package/dist/cli/wizard/interview.js.map +1 -0
- package/dist/cli/wizard/prompt.d.ts +18 -0
- package/dist/cli/wizard/prompt.js +72 -0
- package/dist/cli/wizard/prompt.js.map +1 -0
- package/dist/cli/wizard/template-generator.d.ts +8 -0
- package/dist/cli/wizard/template-generator.js +133 -0
- package/dist/cli/wizard/template-generator.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +68 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/protocol.d.ts +33 -0
- package/dist/mcp/protocol.js +82 -0
- package/dist/mcp/protocol.js.map +1 -0
- package/dist/mcp/resources.d.ts +20 -0
- package/dist/mcp/resources.js +101 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/run-manager.d.ts +36 -0
- package/dist/mcp/run-manager.js +179 -0
- package/dist/mcp/run-manager.js.map +1 -0
- package/dist/mcp/server.d.ts +13 -0
- package/dist/mcp/server.js +99 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +32 -0
- package/dist/mcp/tools.js +259 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/orchestrator/alert-types.d.ts +16 -0
- package/dist/orchestrator/alert-types.js +2 -0
- package/dist/orchestrator/alert-types.js.map +1 -0
- package/dist/orchestrator/alerts.d.ts +20 -0
- package/dist/orchestrator/alerts.js +76 -0
- package/dist/orchestrator/alerts.js.map +1 -0
- package/dist/orchestrator/checkpoints.d.ts +8 -0
- package/dist/orchestrator/checkpoints.js +24 -0
- package/dist/orchestrator/checkpoints.js.map +1 -0
- package/dist/orchestrator/engine.d.ts +71 -0
- package/dist/orchestrator/engine.js +420 -0
- package/dist/orchestrator/engine.js.map +1 -0
- package/dist/orchestrator/phase-gates.d.ts +6 -0
- package/dist/orchestrator/phase-gates.js +127 -0
- package/dist/orchestrator/phase-gates.js.map +1 -0
- package/dist/orchestrator/plan-approval.d.ts +10 -0
- package/dist/orchestrator/plan-approval.js +51 -0
- package/dist/orchestrator/plan-approval.js.map +1 -0
- package/dist/orchestrator/safety.d.ts +22 -0
- package/dist/orchestrator/safety.js +126 -0
- package/dist/orchestrator/safety.js.map +1 -0
- package/dist/orchestrator/task-graph.d.ts +17 -0
- package/dist/orchestrator/task-graph.js +156 -0
- package/dist/orchestrator/task-graph.js.map +1 -0
- package/dist/orchestrator/wave-executor.d.ts +37 -0
- package/dist/orchestrator/wave-executor.js +237 -0
- package/dist/orchestrator/wave-executor.js.map +1 -0
- package/dist/session/in-process.d.ts +2 -0
- package/dist/session/in-process.js +149 -0
- package/dist/session/in-process.js.map +1 -0
- package/dist/session/log-capture.d.ts +7 -0
- package/dist/session/log-capture.js +56 -0
- package/dist/session/log-capture.js.map +1 -0
- package/dist/session/manager.d.ts +20 -0
- package/dist/session/manager.js +2 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/state/file-store.d.ts +3 -0
- package/dist/state/file-store.js +124 -0
- package/dist/state/file-store.js.map +1 -0
- package/dist/state/lock.d.ts +6 -0
- package/dist/state/lock.js +71 -0
- package/dist/state/lock.js.map +1 -0
- package/dist/state/plan-parser.d.ts +6 -0
- package/dist/state/plan-parser.js +54 -0
- package/dist/state/plan-parser.js.map +1 -0
- package/dist/state/store.d.ts +27 -0
- package/dist/state/store.js +2 -0
- package/dist/state/store.js.map +1 -0
- package/dist/steps/events.d.ts +49 -0
- package/dist/steps/events.js +2 -0
- package/dist/steps/events.js.map +1 -0
- package/dist/steps/pipeline.d.ts +14 -0
- package/dist/steps/pipeline.js +284 -0
- package/dist/steps/pipeline.js.map +1 -0
- package/dist/steps/plan-parser.d.ts +35 -0
- package/dist/steps/plan-parser.js +147 -0
- package/dist/steps/plan-parser.js.map +1 -0
- package/dist/steps/runner.d.ts +13 -0
- package/dist/steps/runner.js +155 -0
- package/dist/steps/runner.js.map +1 -0
- package/dist/steps/store.d.ts +26 -0
- package/dist/steps/store.js +164 -0
- package/dist/steps/store.js.map +1 -0
- package/dist/steps/types.d.ts +36 -0
- package/dist/steps/types.js +2 -0
- package/dist/steps/types.js.map +1 -0
- package/dist/tui/app.d.ts +15 -0
- package/dist/tui/app.js +297 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/banner.d.ts +1 -0
- package/dist/tui/banner.js +11 -0
- package/dist/tui/banner.js.map +1 -0
- package/dist/tui/colors.d.ts +22 -0
- package/dist/tui/colors.js +30 -0
- package/dist/tui/colors.js.map +1 -0
- package/dist/tui/components/agent-panel.d.ts +8 -0
- package/dist/tui/components/agent-panel.js +80 -0
- package/dist/tui/components/agent-panel.js.map +1 -0
- package/dist/tui/components/header.d.ts +15 -0
- package/dist/tui/components/header.js +69 -0
- package/dist/tui/components/header.js.map +1 -0
- package/dist/tui/components/status-bar.d.ts +2 -0
- package/dist/tui/components/status-bar.js +8 -0
- package/dist/tui/components/status-bar.js.map +1 -0
- package/dist/tui/components/task-board.d.ts +3 -0
- package/dist/tui/components/task-board.js +96 -0
- package/dist/tui/components/task-board.js.map +1 -0
- package/dist/tui/display.d.ts +23 -0
- package/dist/tui/display.js +125 -0
- package/dist/tui/display.js.map +1 -0
- package/dist/tui/input.d.ts +2 -0
- package/dist/tui/input.js +44 -0
- package/dist/tui/input.js.map +1 -0
- package/dist/tui/layout-master.d.ts +7 -0
- package/dist/tui/layout-master.js +31 -0
- package/dist/tui/layout-master.js.map +1 -0
- package/dist/tui/layout.d.ts +13 -0
- package/dist/tui/layout.js +37 -0
- package/dist/tui/layout.js.map +1 -0
- package/dist/tui/pane-formatter.d.ts +27 -0
- package/dist/tui/pane-formatter.js +153 -0
- package/dist/tui/pane-formatter.js.map +1 -0
- package/dist/tui/renderer.d.ts +8 -0
- package/dist/tui/renderer.js +30 -0
- package/dist/tui/renderer.js.map +1 -0
- package/dist/tui/screen.d.ts +12 -0
- package/dist/tui/screen.js +32 -0
- package/dist/tui/screen.js.map +1 -0
- package/dist/tui/structured-display.d.ts +5 -0
- package/dist/tui/structured-display.js +74 -0
- package/dist/tui/structured-display.js.map +1 -0
- package/dist/tui/tmux-display.d.ts +6 -0
- package/dist/tui/tmux-display.js +187 -0
- package/dist/tui/tmux-display.js.map +1 -0
- package/dist/tui/tmux.d.ts +26 -0
- package/dist/tui/tmux.js +265 -0
- package/dist/tui/tmux.js.map +1 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/git.d.ts +6 -0
- package/dist/utils/git.js +35 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/hello.d.ts +1 -0
- package/dist/utils/hello.js +4 -0
- package/dist/utils/hello.js.map +1 -0
- package/dist/utils/id.d.ts +1 -0
- package/dist/utils/id.js +5 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/jsonl.d.ts +2 -0
- package/dist/utils/jsonl.js +18 -0
- package/dist/utils/jsonl.js.map +1 -0
- package/dist/utils/logger.d.ts +20 -0
- package/dist/utils/logger.js +40 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/pricing.d.ts +3 -0
- package/dist/utils/pricing.js +26 -0
- package/dist/utils/pricing.js.map +1 -0
- package/dist/utils/xml.d.ts +13 -0
- package/dist/utils/xml.js +67 -0
- package/dist/utils/xml.js.map +1 -0
- package/dist/utils/yaml.d.ts +5 -0
- package/dist/utils/yaml.js +126 -0
- package/dist/utils/yaml.js.map +1 -0
- package/package.json +45 -0
- package/templates/CONSTITUTION.md +10 -0
- package/templates/STATE.md +3 -0
- package/templates/config.json +11 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
2
|
+
export async function requestCliApproval(plan) {
|
|
3
|
+
console.log('\n' + '='.repeat(60));
|
|
4
|
+
console.log('PLAN APPROVAL REQUIRED');
|
|
5
|
+
console.log('='.repeat(60));
|
|
6
|
+
console.log(plan);
|
|
7
|
+
console.log('='.repeat(60));
|
|
8
|
+
const answer = await askQuestion('Approve this plan? [y/N] ');
|
|
9
|
+
const approved = answer.trim().toLowerCase() === 'y';
|
|
10
|
+
return {
|
|
11
|
+
approved,
|
|
12
|
+
reason: approved ? undefined : 'Plan rejected by user',
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function askQuestion(prompt) {
|
|
16
|
+
const rl = createInterface({
|
|
17
|
+
input: process.stdin,
|
|
18
|
+
output: process.stdout,
|
|
19
|
+
});
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
rl.question(prompt, (answer) => {
|
|
22
|
+
rl.close();
|
|
23
|
+
resolve(answer);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function createApprovalCallback(mode) {
|
|
28
|
+
if (mode === 'cli') {
|
|
29
|
+
return async (plan) => {
|
|
30
|
+
const result = await requestCliApproval(plan);
|
|
31
|
+
return result.approved;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// TUI mode: approval is handled by the TUI overlay
|
|
35
|
+
// The engine emits 'approval_required' event and waits for callback resolution
|
|
36
|
+
let resolveApproval = null;
|
|
37
|
+
const callback = async (_plan) => {
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
resolveApproval = resolve;
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
// Attach resolver so TUI can call it
|
|
43
|
+
callback.resolve = (approved) => {
|
|
44
|
+
if (resolveApproval) {
|
|
45
|
+
resolveApproval(approved);
|
|
46
|
+
resolveApproval = null;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
return callback;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=plan-approval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-approval.js","sourceRoot":"","sources":["../../src/orchestrator/plan-approval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAOhD,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAY;IACnD,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAE5B,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,2BAA2B,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC;IAErD,OAAO;QACL,QAAQ;QACR,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,uBAAuB;KACvD,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,MAAc;IACjC,MAAM,EAAE,GAAG,eAAe,CAAC;QACzB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IAEH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;YAC7B,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,IAAmB;IACxD,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,OAAO,KAAK,EAAE,IAAY,EAAE,EAAE;YAC5B,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAC;YAC9C,OAAO,MAAM,CAAC,QAAQ,CAAC;QACzB,CAAC,CAAC;IACJ,CAAC;IAED,mDAAmD;IACnD,+EAA+E;IAC/E,IAAI,eAAe,GAAyC,IAAI,CAAC;IAEjE,MAAM,QAAQ,GAAG,KAAK,EAAE,KAAa,EAAoB,EAAE;QACzD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,eAAe,GAAG,OAAO,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,qCAAqC;IACpC,QAAyC,CAAC,OAAO,GAAG,CAAC,QAAiB,EAAE,EAAE;QACzE,IAAI,eAAe,EAAE,CAAC;YACpB,eAAe,CAAC,QAAQ,CAAC,CAAC;YAC1B,eAAe,GAAG,IAAI,CAAC;QACzB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ProjectConfig } from '../types.js';
|
|
2
|
+
export interface SafetyCheck {
|
|
3
|
+
readonly passed: boolean;
|
|
4
|
+
readonly code: string;
|
|
5
|
+
readonly message: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SafetyState {
|
|
8
|
+
totalInputTokens: number;
|
|
9
|
+
totalOutputTokens: number;
|
|
10
|
+
totalIterations: number;
|
|
11
|
+
totalAgentInvocations: number;
|
|
12
|
+
activeAgents: number;
|
|
13
|
+
startTimeMs: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function checkIterations(state: SafetyState, config: ProjectConfig): SafetyCheck;
|
|
16
|
+
export declare function checkConcurrency(state: SafetyState, config: ProjectConfig): SafetyCheck;
|
|
17
|
+
export declare function checkPath(filePath: string, config: ProjectConfig): SafetyCheck;
|
|
18
|
+
export declare function checkCommand(command: string, config: ProjectConfig): SafetyCheck;
|
|
19
|
+
export declare function checkTokens(state: SafetyState, config: ProjectConfig): SafetyCheck;
|
|
20
|
+
export declare function checkWallTime(state: SafetyState, config: ProjectConfig): SafetyCheck;
|
|
21
|
+
export declare function checkInvocations(state: SafetyState, config: ProjectConfig): SafetyCheck;
|
|
22
|
+
export declare function checkAll(state: SafetyState, config: ProjectConfig): SafetyCheck[];
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export function checkIterations(state, config) {
|
|
2
|
+
const passed = state.totalIterations < config.maxIterations;
|
|
3
|
+
return {
|
|
4
|
+
passed,
|
|
5
|
+
code: 'ITERATIONS_EXCEEDED',
|
|
6
|
+
message: passed
|
|
7
|
+
? 'Iterations within limits'
|
|
8
|
+
: `Iterations exceeded: ${state.totalIterations} >= ${config.maxIterations}`,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function checkConcurrency(state, config) {
|
|
12
|
+
const passed = state.activeAgents < config.maxConcurrentAgents;
|
|
13
|
+
return {
|
|
14
|
+
passed,
|
|
15
|
+
code: 'CONCURRENCY_EXCEEDED',
|
|
16
|
+
message: passed
|
|
17
|
+
? 'Concurrency within limits'
|
|
18
|
+
: `Concurrency exceeded: ${state.activeAgents} >= ${config.maxConcurrentAgents}`,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function matchesGlob(filePath, pattern) {
|
|
22
|
+
// Handle directory patterns like "credentials/"
|
|
23
|
+
if (pattern.endsWith('/')) {
|
|
24
|
+
const dir = pattern.slice(0, -1);
|
|
25
|
+
return filePath === dir || filePath.startsWith(dir + '/');
|
|
26
|
+
}
|
|
27
|
+
// Handle glob patterns like "*.key"
|
|
28
|
+
if (pattern.includes('*')) {
|
|
29
|
+
const regex = new RegExp('^' +
|
|
30
|
+
pattern
|
|
31
|
+
.split('*')
|
|
32
|
+
.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
|
33
|
+
.join('.*') +
|
|
34
|
+
'$');
|
|
35
|
+
// Match against the full path and the basename
|
|
36
|
+
const basename = filePath.split('/').pop() ?? filePath;
|
|
37
|
+
return regex.test(filePath) || regex.test(basename);
|
|
38
|
+
}
|
|
39
|
+
// Exact match or basename match
|
|
40
|
+
const basename = filePath.split('/').pop() ?? filePath;
|
|
41
|
+
return filePath === pattern || basename === pattern;
|
|
42
|
+
}
|
|
43
|
+
export function checkPath(filePath, config) {
|
|
44
|
+
for (const pattern of config.excludedPaths) {
|
|
45
|
+
if (matchesGlob(filePath, pattern)) {
|
|
46
|
+
return {
|
|
47
|
+
passed: false,
|
|
48
|
+
code: 'PATH_EXCLUDED',
|
|
49
|
+
message: `Path excluded: '${filePath}' matches pattern '${pattern}'`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
passed: true,
|
|
55
|
+
code: 'PATH_EXCLUDED',
|
|
56
|
+
message: 'Path is allowed',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export function checkCommand(command, config) {
|
|
60
|
+
for (const excluded of config.excludedCommands) {
|
|
61
|
+
if (command.includes(excluded)) {
|
|
62
|
+
return {
|
|
63
|
+
passed: false,
|
|
64
|
+
code: 'COMMAND_EXCLUDED',
|
|
65
|
+
message: `Command excluded: '${command}' contains '${excluded}'`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
passed: true,
|
|
71
|
+
code: 'COMMAND_EXCLUDED',
|
|
72
|
+
message: 'Command is allowed',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function checkTokens(state, config) {
|
|
76
|
+
if (config.maxTotalTokens === undefined) {
|
|
77
|
+
return { passed: true, code: 'TOKENS_EXCEEDED', message: 'Token limit not configured' };
|
|
78
|
+
}
|
|
79
|
+
const total = state.totalInputTokens + state.totalOutputTokens;
|
|
80
|
+
const passed = total < config.maxTotalTokens;
|
|
81
|
+
return {
|
|
82
|
+
passed,
|
|
83
|
+
code: 'TOKENS_EXCEEDED',
|
|
84
|
+
message: passed
|
|
85
|
+
? 'Tokens within limits'
|
|
86
|
+
: `Tokens exceeded: ${total} >= ${config.maxTotalTokens}`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export function checkWallTime(state, config) {
|
|
90
|
+
if (config.maxWallTimeSeconds === undefined) {
|
|
91
|
+
return { passed: true, code: 'WALL_TIME_EXCEEDED', message: 'Wall time limit not configured' };
|
|
92
|
+
}
|
|
93
|
+
const elapsedSeconds = (Date.now() - state.startTimeMs) / 1000;
|
|
94
|
+
const passed = elapsedSeconds < config.maxWallTimeSeconds;
|
|
95
|
+
return {
|
|
96
|
+
passed,
|
|
97
|
+
code: 'WALL_TIME_EXCEEDED',
|
|
98
|
+
message: passed
|
|
99
|
+
? 'Wall time within limits'
|
|
100
|
+
: `Wall time exceeded: ${elapsedSeconds.toFixed(0)}s >= ${config.maxWallTimeSeconds}s`,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export function checkInvocations(state, config) {
|
|
104
|
+
if (config.maxAgentInvocations === undefined) {
|
|
105
|
+
return { passed: true, code: 'INVOCATIONS_EXCEEDED', message: 'Invocation limit not configured' };
|
|
106
|
+
}
|
|
107
|
+
const passed = state.totalAgentInvocations < config.maxAgentInvocations;
|
|
108
|
+
return {
|
|
109
|
+
passed,
|
|
110
|
+
code: 'INVOCATIONS_EXCEEDED',
|
|
111
|
+
message: passed
|
|
112
|
+
? 'Invocations within limits'
|
|
113
|
+
: `Invocations exceeded: ${state.totalAgentInvocations} >= ${config.maxAgentInvocations}`,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
export function checkAll(state, config) {
|
|
117
|
+
const checks = [
|
|
118
|
+
checkTokens(state, config),
|
|
119
|
+
checkIterations(state, config),
|
|
120
|
+
checkConcurrency(state, config),
|
|
121
|
+
checkWallTime(state, config),
|
|
122
|
+
checkInvocations(state, config),
|
|
123
|
+
];
|
|
124
|
+
return checks.filter((c) => !c.passed);
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=safety.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safety.js","sourceRoot":"","sources":["../../src/orchestrator/safety.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Task } from '../types.js';
|
|
2
|
+
export interface TaskGraph {
|
|
3
|
+
addTask(task: Task): void;
|
|
4
|
+
removeTask(id: string): void;
|
|
5
|
+
getTask(id: string): Task | undefined;
|
|
6
|
+
getAllTasks(): Task[];
|
|
7
|
+
getWave(waveNumber: number): Task[];
|
|
8
|
+
getWaves(): Task[][];
|
|
9
|
+
getDependencies(taskId: string): Task[];
|
|
10
|
+
getDependents(taskId: string): Task[];
|
|
11
|
+
isReady(taskId: string): boolean;
|
|
12
|
+
getReadyTasks(): Task[];
|
|
13
|
+
topologicalSort(): Task[];
|
|
14
|
+
hasCycle(): boolean;
|
|
15
|
+
validateDependencies(): string[];
|
|
16
|
+
}
|
|
17
|
+
export declare function createTaskGraph(): TaskGraph;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
export function createTaskGraph() {
|
|
2
|
+
const tasks = new Map();
|
|
3
|
+
function addTask(task) {
|
|
4
|
+
if (tasks.has(task.id)) {
|
|
5
|
+
throw new Error(`Task with id '${task.id}' already exists`);
|
|
6
|
+
}
|
|
7
|
+
tasks.set(task.id, task);
|
|
8
|
+
}
|
|
9
|
+
function removeTask(id) {
|
|
10
|
+
tasks.delete(id);
|
|
11
|
+
}
|
|
12
|
+
function getTask(id) {
|
|
13
|
+
return tasks.get(id);
|
|
14
|
+
}
|
|
15
|
+
function getAllTasks() {
|
|
16
|
+
return [...tasks.values()];
|
|
17
|
+
}
|
|
18
|
+
function getWave(waveNumber) {
|
|
19
|
+
return getAllTasks().filter((t) => t.wave === waveNumber);
|
|
20
|
+
}
|
|
21
|
+
function getWaves() {
|
|
22
|
+
const waveMap = new Map();
|
|
23
|
+
for (const task of tasks.values()) {
|
|
24
|
+
const list = waveMap.get(task.wave);
|
|
25
|
+
if (list) {
|
|
26
|
+
list.push(task);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
waveMap.set(task.wave, [task]);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const waveNumbers = [...waveMap.keys()].sort((a, b) => a - b);
|
|
33
|
+
return waveNumbers.map((n) => waveMap.get(n));
|
|
34
|
+
}
|
|
35
|
+
function getDependencies(taskId) {
|
|
36
|
+
const task = tasks.get(taskId);
|
|
37
|
+
if (!task)
|
|
38
|
+
return [];
|
|
39
|
+
const deps = [];
|
|
40
|
+
for (const depId of task.dependsOn) {
|
|
41
|
+
const dep = tasks.get(depId);
|
|
42
|
+
if (dep)
|
|
43
|
+
deps.push(dep);
|
|
44
|
+
}
|
|
45
|
+
return deps;
|
|
46
|
+
}
|
|
47
|
+
function getDependents(taskId) {
|
|
48
|
+
const result = [];
|
|
49
|
+
for (const task of tasks.values()) {
|
|
50
|
+
if (task.dependsOn.includes(taskId)) {
|
|
51
|
+
result.push(task);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
function isReady(taskId) {
|
|
57
|
+
const task = tasks.get(taskId);
|
|
58
|
+
if (!task)
|
|
59
|
+
return false;
|
|
60
|
+
if (task.status !== 'pending')
|
|
61
|
+
return false;
|
|
62
|
+
for (const depId of task.dependsOn) {
|
|
63
|
+
const dep = tasks.get(depId);
|
|
64
|
+
if (!dep || dep.status !== 'completed')
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
function getReadyTasks() {
|
|
70
|
+
const result = [];
|
|
71
|
+
for (const task of tasks.values()) {
|
|
72
|
+
if (isReady(task.id)) {
|
|
73
|
+
result.push(task);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
function kahnSort() {
|
|
79
|
+
// Build adjacency and in-degree from dependsOn
|
|
80
|
+
const inDegree = new Map();
|
|
81
|
+
const adjacency = new Map();
|
|
82
|
+
for (const task of tasks.values()) {
|
|
83
|
+
if (!inDegree.has(task.id))
|
|
84
|
+
inDegree.set(task.id, 0);
|
|
85
|
+
if (!adjacency.has(task.id))
|
|
86
|
+
adjacency.set(task.id, []);
|
|
87
|
+
}
|
|
88
|
+
for (const task of tasks.values()) {
|
|
89
|
+
for (const depId of task.dependsOn) {
|
|
90
|
+
if (tasks.has(depId)) {
|
|
91
|
+
// depId -> task.id (dependency must come before dependent)
|
|
92
|
+
const adj = adjacency.get(depId);
|
|
93
|
+
if (adj)
|
|
94
|
+
adj.push(task.id);
|
|
95
|
+
inDegree.set(task.id, (inDegree.get(task.id) ?? 0) + 1);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const queue = [];
|
|
100
|
+
for (const [id, degree] of inDegree) {
|
|
101
|
+
if (degree === 0)
|
|
102
|
+
queue.push(id);
|
|
103
|
+
}
|
|
104
|
+
const sorted = [];
|
|
105
|
+
while (queue.length > 0) {
|
|
106
|
+
const id = queue.shift();
|
|
107
|
+
const task = tasks.get(id);
|
|
108
|
+
if (task)
|
|
109
|
+
sorted.push(task);
|
|
110
|
+
for (const neighbor of adjacency.get(id) ?? []) {
|
|
111
|
+
const newDegree = (inDegree.get(neighbor) ?? 1) - 1;
|
|
112
|
+
inDegree.set(neighbor, newDegree);
|
|
113
|
+
if (newDegree === 0)
|
|
114
|
+
queue.push(neighbor);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return { sorted, hasCycle: sorted.length < tasks.size };
|
|
118
|
+
}
|
|
119
|
+
function topologicalSort() {
|
|
120
|
+
const result = kahnSort();
|
|
121
|
+
if (result.hasCycle) {
|
|
122
|
+
throw new Error('Task graph contains a cycle');
|
|
123
|
+
}
|
|
124
|
+
return result.sorted;
|
|
125
|
+
}
|
|
126
|
+
function hasCycle() {
|
|
127
|
+
return kahnSort().hasCycle;
|
|
128
|
+
}
|
|
129
|
+
function validateDependencies() {
|
|
130
|
+
const errors = [];
|
|
131
|
+
for (const task of tasks.values()) {
|
|
132
|
+
for (const depId of task.dependsOn) {
|
|
133
|
+
if (!tasks.has(depId)) {
|
|
134
|
+
errors.push(`Task '${task.id}' depends on '${depId}' which does not exist`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return errors;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
addTask,
|
|
142
|
+
removeTask,
|
|
143
|
+
getTask,
|
|
144
|
+
getAllTasks,
|
|
145
|
+
getWave,
|
|
146
|
+
getWaves,
|
|
147
|
+
getDependencies,
|
|
148
|
+
getDependents,
|
|
149
|
+
isReady,
|
|
150
|
+
getReadyTasks,
|
|
151
|
+
topologicalSort,
|
|
152
|
+
hasCycle,
|
|
153
|
+
validateDependencies,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=task-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-graph.js","sourceRoot":"","sources":["../../src/orchestrator/task-graph.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Task, TaskEvent, ProjectConfig } from '../types.js';
|
|
2
|
+
import type { AgentRuntime, AgentEvent } from '../agents/runtime.js';
|
|
3
|
+
import type { TaskGraph } from './task-graph.js';
|
|
4
|
+
import type { SafetyState } from './safety.js';
|
|
5
|
+
import type { AgentAlert, AlertResponse } from './alert-types.js';
|
|
6
|
+
export interface ExecutionConfig {
|
|
7
|
+
readonly systemPrompt: string;
|
|
8
|
+
readonly model: string;
|
|
9
|
+
readonly maxTurns: number;
|
|
10
|
+
}
|
|
11
|
+
export interface WaveExecutorParams {
|
|
12
|
+
readonly graph: TaskGraph;
|
|
13
|
+
readonly runtime: AgentRuntime;
|
|
14
|
+
readonly config: ProjectConfig;
|
|
15
|
+
readonly workingDirectory: string;
|
|
16
|
+
readonly buildPrompt: (task: Task) => Promise<string>;
|
|
17
|
+
readonly buildExecutionConfig: (task: Task) => ExecutionConfig;
|
|
18
|
+
readonly onTaskEvent: (event: TaskEvent) => Promise<void>;
|
|
19
|
+
readonly onAgentEvent: (taskId: string, event: AgentEvent) => void;
|
|
20
|
+
readonly onAlert?: (alert: AgentAlert) => Promise<AlertResponse>;
|
|
21
|
+
readonly abortSignal?: AbortSignal;
|
|
22
|
+
}
|
|
23
|
+
export interface WaveResult {
|
|
24
|
+
readonly wave: number;
|
|
25
|
+
readonly tasks: readonly TaskResult[];
|
|
26
|
+
readonly totalInputTokens: number;
|
|
27
|
+
readonly totalOutputTokens: number;
|
|
28
|
+
}
|
|
29
|
+
export interface TaskResult {
|
|
30
|
+
readonly taskId: string;
|
|
31
|
+
readonly status: 'completed' | 'failed' | 'skipped';
|
|
32
|
+
readonly inputTokens: number;
|
|
33
|
+
readonly outputTokens: number;
|
|
34
|
+
readonly error?: string;
|
|
35
|
+
readonly result?: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function executeWave(wave: number, params: WaveExecutorParams, safetyState: SafetyState): Promise<WaveResult>;
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { checkAll } from './safety.js';
|
|
2
|
+
import { generateId } from '../utils/id.js';
|
|
3
|
+
function createSemaphore(max) {
|
|
4
|
+
let current = 0;
|
|
5
|
+
const queue = [];
|
|
6
|
+
return {
|
|
7
|
+
acquire() {
|
|
8
|
+
if (current < max) {
|
|
9
|
+
current++;
|
|
10
|
+
return Promise.resolve();
|
|
11
|
+
}
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
queue.push(resolve);
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
release() {
|
|
17
|
+
const next = queue.shift();
|
|
18
|
+
if (next) {
|
|
19
|
+
next();
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
current--;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const MAX_ALERT_RETRIES = 5;
|
|
28
|
+
async function executeTask(task, params, semaphore, retriesRemaining, safetyState) {
|
|
29
|
+
await semaphore.acquire();
|
|
30
|
+
try {
|
|
31
|
+
return await runTask(task, params, semaphore, retriesRemaining, safetyState, 0);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
// runTask manages its own semaphore release and activeAgents cleanup.
|
|
35
|
+
// If we reach here, the task is in an unexpected state — just return failed.
|
|
36
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
37
|
+
if (task.status !== 'failed') {
|
|
38
|
+
task.status = 'failed';
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
taskId: task.id,
|
|
42
|
+
status: 'failed',
|
|
43
|
+
inputTokens: 0,
|
|
44
|
+
outputTokens: 0,
|
|
45
|
+
error: errorMsg,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function runTask(task, params, semaphore, retriesRemaining, safetyState, alertRetryCount, alertContext) {
|
|
50
|
+
if (params.abortSignal?.aborted) {
|
|
51
|
+
semaphore.release();
|
|
52
|
+
task.status = 'skipped';
|
|
53
|
+
return { taskId: task.id, status: 'skipped', inputTokens: 0, outputTokens: 0 };
|
|
54
|
+
}
|
|
55
|
+
task.status = 'running';
|
|
56
|
+
safetyState.totalAgentInvocations++;
|
|
57
|
+
safetyState.activeAgents++;
|
|
58
|
+
let inputTokens = 0;
|
|
59
|
+
let outputTokens = 0;
|
|
60
|
+
let result;
|
|
61
|
+
let error;
|
|
62
|
+
let retryWithContext;
|
|
63
|
+
let shouldSkip = false;
|
|
64
|
+
try {
|
|
65
|
+
await params.onTaskEvent({
|
|
66
|
+
ts: new Date().toISOString(),
|
|
67
|
+
type: 'task_started',
|
|
68
|
+
taskId: task.id,
|
|
69
|
+
});
|
|
70
|
+
const executionId = generateId();
|
|
71
|
+
const basePrompt = await params.buildPrompt(task);
|
|
72
|
+
const prompt = alertContext
|
|
73
|
+
? `${basePrompt}\n\n# Additional Context from Previous Attempt\n${alertContext}`
|
|
74
|
+
: basePrompt;
|
|
75
|
+
const execConfig = params.buildExecutionConfig(task);
|
|
76
|
+
const stream = params.runtime.execute({
|
|
77
|
+
executionId,
|
|
78
|
+
prompt,
|
|
79
|
+
systemPrompt: execConfig.systemPrompt,
|
|
80
|
+
workingDirectory: params.workingDirectory,
|
|
81
|
+
model: execConfig.model,
|
|
82
|
+
maxTurns: execConfig.maxTurns,
|
|
83
|
+
outputFormat: 'stream-json',
|
|
84
|
+
dangerouslySkipPermissions: true,
|
|
85
|
+
});
|
|
86
|
+
for await (const event of stream) {
|
|
87
|
+
params.onAgentEvent(task.id, event);
|
|
88
|
+
if (event.type === 'cost_update') {
|
|
89
|
+
inputTokens = event.inputTokens;
|
|
90
|
+
outputTokens = event.outputTokens;
|
|
91
|
+
}
|
|
92
|
+
else if (event.type === 'complete') {
|
|
93
|
+
result = event.result;
|
|
94
|
+
}
|
|
95
|
+
else if (event.type === 'error') {
|
|
96
|
+
error = event.error;
|
|
97
|
+
}
|
|
98
|
+
else if (event.type === 'alert' && params.onAlert) {
|
|
99
|
+
const alert = {
|
|
100
|
+
taskId: task.id,
|
|
101
|
+
executionId,
|
|
102
|
+
severity: event.severity,
|
|
103
|
+
message: event.message,
|
|
104
|
+
context: event.context,
|
|
105
|
+
timestamp: Date.now(),
|
|
106
|
+
};
|
|
107
|
+
const response = await params.onAlert(alert);
|
|
108
|
+
if (response.action === 'abort') {
|
|
109
|
+
await params.runtime.abort(executionId).catch((err) => {
|
|
110
|
+
process.stderr.write(`[wave-executor] Failed to abort execution ${executionId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
111
|
+
});
|
|
112
|
+
error = `Aborted due to alert: ${event.message}`;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
if (response.action === 'skip') {
|
|
116
|
+
await params.runtime.abort(executionId).catch((err) => {
|
|
117
|
+
process.stderr.write(`[wave-executor] Failed to abort execution ${executionId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
118
|
+
});
|
|
119
|
+
shouldSkip = true;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
if (response.action === 'retry') {
|
|
123
|
+
await params.runtime.abort(executionId).catch((err) => {
|
|
124
|
+
process.stderr.write(`[wave-executor] Failed to abort execution ${executionId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
125
|
+
});
|
|
126
|
+
retryWithContext = `Previous attempt raised an alert: ${event.message}`;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
if (response.action === 'answer') {
|
|
130
|
+
await params.runtime.abort(executionId).catch((err) => {
|
|
131
|
+
process.stderr.write(`[wave-executor] Failed to abort execution ${executionId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
132
|
+
});
|
|
133
|
+
retryWithContext = response.message
|
|
134
|
+
? `Previous attempt raised a question: ${event.message}\nUser provided guidance: ${response.message}`
|
|
135
|
+
: `Previous attempt raised a question: ${event.message}`;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
error = err instanceof Error ? err.message : String(err);
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
semaphore.release();
|
|
146
|
+
safetyState.activeAgents--;
|
|
147
|
+
}
|
|
148
|
+
// Alert-triggered retry: re-run with context injected into prompt (bounded)
|
|
149
|
+
if (retryWithContext !== undefined) {
|
|
150
|
+
if (alertRetryCount >= MAX_ALERT_RETRIES) {
|
|
151
|
+
task.status = 'failed';
|
|
152
|
+
const failError = `Max alert retries (${MAX_ALERT_RETRIES}) exceeded`;
|
|
153
|
+
await params.onTaskEvent({
|
|
154
|
+
ts: new Date().toISOString(),
|
|
155
|
+
type: 'task_failed',
|
|
156
|
+
taskId: task.id,
|
|
157
|
+
error: failError,
|
|
158
|
+
}).catch((err) => {
|
|
159
|
+
process.stderr.write(`[wave-executor] Failed to persist task event for ${task.id}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
160
|
+
});
|
|
161
|
+
return { taskId: task.id, status: 'failed', inputTokens, outputTokens, error: failError };
|
|
162
|
+
}
|
|
163
|
+
await semaphore.acquire();
|
|
164
|
+
return runTask(task, params, semaphore, retriesRemaining, safetyState, alertRetryCount + 1, retryWithContext);
|
|
165
|
+
}
|
|
166
|
+
// Alert-triggered skip: mark task as skipped
|
|
167
|
+
if (shouldSkip) {
|
|
168
|
+
task.status = 'skipped';
|
|
169
|
+
await params.onTaskEvent({
|
|
170
|
+
ts: new Date().toISOString(),
|
|
171
|
+
type: 'task_skipped',
|
|
172
|
+
taskId: task.id,
|
|
173
|
+
}).catch((err) => {
|
|
174
|
+
process.stderr.write(`[wave-executor] Failed to persist task event for ${task.id}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
175
|
+
});
|
|
176
|
+
return { taskId: task.id, status: 'skipped', inputTokens, outputTokens };
|
|
177
|
+
}
|
|
178
|
+
if (error !== undefined) {
|
|
179
|
+
if (retriesRemaining > 0) {
|
|
180
|
+
await semaphore.acquire();
|
|
181
|
+
return runTask(task, params, semaphore, retriesRemaining - 1, safetyState, 0);
|
|
182
|
+
}
|
|
183
|
+
task.status = 'failed';
|
|
184
|
+
await params.onTaskEvent({
|
|
185
|
+
ts: new Date().toISOString(),
|
|
186
|
+
type: 'task_failed',
|
|
187
|
+
taskId: task.id,
|
|
188
|
+
error,
|
|
189
|
+
}).catch((err) => {
|
|
190
|
+
process.stderr.write(`[wave-executor] Failed to persist task event for ${task.id}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
191
|
+
});
|
|
192
|
+
return { taskId: task.id, status: 'failed', inputTokens, outputTokens, error };
|
|
193
|
+
}
|
|
194
|
+
task.status = 'completed';
|
|
195
|
+
await params.onTaskEvent({
|
|
196
|
+
ts: new Date().toISOString(),
|
|
197
|
+
type: 'task_completed',
|
|
198
|
+
taskId: task.id,
|
|
199
|
+
inputTokens,
|
|
200
|
+
outputTokens,
|
|
201
|
+
}).catch((err) => {
|
|
202
|
+
process.stderr.write(`[wave-executor] Failed to persist task event for ${task.id}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
203
|
+
});
|
|
204
|
+
return { taskId: task.id, status: 'completed', inputTokens, outputTokens, result };
|
|
205
|
+
}
|
|
206
|
+
export async function executeWave(wave, params, safetyState) {
|
|
207
|
+
// Check safety limits before starting
|
|
208
|
+
const failedChecks = checkAll(safetyState, params.config);
|
|
209
|
+
if (failedChecks.length > 0) {
|
|
210
|
+
const first = failedChecks[0];
|
|
211
|
+
throw new Error(`Safety check failed [${first.code}]: ${first.message}`);
|
|
212
|
+
}
|
|
213
|
+
const tasks = params.graph.getWave(wave);
|
|
214
|
+
if (tasks.length === 0) {
|
|
215
|
+
return { wave, tasks: [], totalInputTokens: 0, totalOutputTokens: 0 };
|
|
216
|
+
}
|
|
217
|
+
const semaphore = createSemaphore(params.config.maxConcurrentAgents);
|
|
218
|
+
const maxRetries = params.config.maxTaskRetries;
|
|
219
|
+
const promises = tasks.map((task) => executeTask(task, params, semaphore, task.maxRetries ?? maxRetries, safetyState));
|
|
220
|
+
const settled = await Promise.allSettled(promises);
|
|
221
|
+
const taskResults = settled.map((outcome) => {
|
|
222
|
+
if (outcome.status === 'fulfilled') {
|
|
223
|
+
return outcome.value;
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
taskId: 'unknown',
|
|
227
|
+
status: 'failed',
|
|
228
|
+
inputTokens: 0,
|
|
229
|
+
outputTokens: 0,
|
|
230
|
+
error: outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason),
|
|
231
|
+
};
|
|
232
|
+
});
|
|
233
|
+
const totalInputTokens = taskResults.reduce((sum, r) => sum + r.inputTokens, 0);
|
|
234
|
+
const totalOutputTokens = taskResults.reduce((sum, r) => sum + r.outputTokens, 0);
|
|
235
|
+
return { wave, tasks: taskResults, totalInputTokens, totalOutputTokens };
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=wave-executor.js.map
|