wave-agent-sdk 1.0.4 → 1.0.6
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/dist/agent.d.ts +23 -1
- package/dist/agent.js +34 -4
- package/dist/managers/aiManager.d.ts +36 -0
- package/dist/managers/aiManager.js +240 -8
- package/dist/managers/messageManager.d.ts +1 -3
- package/dist/managers/messageManager.js +19 -9
- package/dist/managers/slashCommandManager.js +28 -0
- package/dist/managers/subagentManager.d.ts +0 -2
- package/dist/services/session.js +2 -9
- package/dist/telemetry/instrumentation.d.ts +1 -1
- package/dist/telemetry/instrumentation.js +57 -28
- package/dist/tools/bashTool.js +16 -6
- package/dist/tools/grepTool.js +11 -3
- package/dist/types/index.d.ts +3 -0
- package/dist/types/messaging.d.ts +2 -0
- package/dist/utils/containerSetup.js +0 -3
- package/dist/utils/messageOperations.d.ts +10 -1
- package/dist/utils/messageOperations.js +19 -1
- package/dist/utils/notificationXml.js +5 -0
- package/dist/utils/ripgrep.d.ts +4 -4
- package/dist/utils/ripgrep.js +4 -27
- package/package.json +20 -8
- package/bin/rg +0 -79
- package/dist/managers/forkedAgentManager.d.ts +0 -50
- package/dist/managers/forkedAgentManager.js +0 -130
- package/scripts/install_ripgrep.js +0 -111
- package/scripts/postinstall.js +0 -38
- package/src/agent.ts +0 -1271
- package/src/constants/subagents.ts +0 -4
- package/src/constants/toolLimits.ts +0 -15
- package/src/constants/tools.ts +0 -24
- package/src/core/plugin.ts +0 -237
- package/src/core/session.ts +0 -9
- package/src/index.ts +0 -38
- package/src/managers/MemoryRuleManager.ts +0 -198
- package/src/managers/aiManager.ts +0 -2429
- package/src/managers/backgroundTaskManager.ts +0 -480
- package/src/managers/bangManager.ts +0 -119
- package/src/managers/cronManager.ts +0 -401
- package/src/managers/foregroundTaskManager.ts +0 -30
- package/src/managers/forkedAgentManager.ts +0 -216
- package/src/managers/hookManager.ts +0 -1089
- package/src/managers/liveConfigManager.ts +0 -445
- package/src/managers/lspManager.ts +0 -470
- package/src/managers/mcpManager.ts +0 -1010
- package/src/managers/messageManager.ts +0 -1180
- package/src/managers/messageQueue.ts +0 -157
- package/src/managers/permissionManager.ts +0 -1140
- package/src/managers/planManager.ts +0 -112
- package/src/managers/pluginManager.ts +0 -330
- package/src/managers/pluginScopeManager.ts +0 -124
- package/src/managers/reversionManager.ts +0 -178
- package/src/managers/skillManager.ts +0 -578
- package/src/managers/slashCommandManager.ts +0 -564
- package/src/managers/subagentManager.ts +0 -915
- package/src/managers/toolManager.ts +0 -489
- package/src/managers/workflowManager.ts +0 -493
- package/src/prompts/autoMemory.ts +0 -33
- package/src/prompts/autoMemoryExtraction.ts +0 -146
- package/src/prompts/index.ts +0 -506
- package/src/prompts/planModeReminders.ts +0 -126
- package/src/services/GitService.ts +0 -131
- package/src/services/MarketplaceService.ts +0 -1052
- package/src/services/MemoryRuleService.ts +0 -71
- package/src/services/aiService.ts +0 -919
- package/src/services/authService.ts +0 -565
- package/src/services/autoMemoryService.ts +0 -280
- package/src/services/configurationService.ts +0 -1520
- package/src/services/fileWatcher.ts +0 -319
- package/src/services/hook.ts +0 -348
- package/src/services/initializationService.ts +0 -357
- package/src/services/interactionService.ts +0 -229
- package/src/services/jsonlHandler.ts +0 -295
- package/src/services/memory.ts +0 -234
- package/src/services/pluginLoader.ts +0 -252
- package/src/services/remoteSettingsService.ts +0 -366
- package/src/services/reversionService.ts +0 -122
- package/src/services/session.ts +0 -922
- package/src/services/taskManager.ts +0 -321
- package/src/telemetry/events.ts +0 -65
- package/src/telemetry/instrumentation.ts +0 -499
- package/src/telemetry/sessionTracing.ts +0 -348
- package/src/tools/agentTool.ts +0 -322
- package/src/tools/askUserQuestion.ts +0 -160
- package/src/tools/bashTool.ts +0 -621
- package/src/tools/buildTool.ts +0 -61
- package/src/tools/cronCreateTool.ts +0 -161
- package/src/tools/cronDeleteTool.ts +0 -51
- package/src/tools/cronListTool.ts +0 -47
- package/src/tools/editTool.ts +0 -313
- package/src/tools/enterPlanMode.ts +0 -124
- package/src/tools/enterWorktreeTool.ts +0 -190
- package/src/tools/exitPlanMode.ts +0 -139
- package/src/tools/exitWorktreeTool.ts +0 -236
- package/src/tools/globTool.ts +0 -167
- package/src/tools/grepTool.ts +0 -399
- package/src/tools/lspTool.ts +0 -883
- package/src/tools/readTool.ts +0 -426
- package/src/tools/skillTool.ts +0 -248
- package/src/tools/taskManagementTools.ts +0 -574
- package/src/tools/taskStopTool.ts +0 -78
- package/src/tools/types.ts +0 -137
- package/src/tools/webFetchTool.ts +0 -373
- package/src/tools/workflowTool.ts +0 -205
- package/src/tools/writeTool.ts +0 -267
- package/src/types/agent.ts +0 -128
- package/src/types/auth.ts +0 -19
- package/src/types/commands.ts +0 -30
- package/src/types/config.ts +0 -43
- package/src/types/configuration.ts +0 -168
- package/src/types/core.ts +0 -105
- package/src/types/cron.ts +0 -13
- package/src/types/environment.ts +0 -106
- package/src/types/fileSearch.ts +0 -4
- package/src/types/history.ts +0 -7
- package/src/types/hooks.ts +0 -256
- package/src/types/index.ts +0 -41
- package/src/types/lsp.ts +0 -98
- package/src/types/marketplace.ts +0 -71
- package/src/types/mcp.ts +0 -43
- package/src/types/memoryRule.ts +0 -31
- package/src/types/messaging.ts +0 -114
- package/src/types/permissions.ts +0 -80
- package/src/types/plugins.ts +0 -39
- package/src/types/processes.ts +0 -99
- package/src/types/reversion.ts +0 -29
- package/src/types/session.ts +0 -12
- package/src/types/skills.ts +0 -108
- package/src/types/tasks.ts +0 -13
- package/src/types/telemetry.ts +0 -98
- package/src/types/tools.ts +0 -44
- package/src/types/workflow.ts +0 -6
- package/src/utils/abortUtils.ts +0 -118
- package/src/utils/atomicWrite.ts +0 -61
- package/src/utils/bashParser.ts +0 -899
- package/src/utils/cacheControlUtils.ts +0 -426
- package/src/utils/commandArgumentParser.ts +0 -161
- package/src/utils/commandPathResolver.ts +0 -88
- package/src/utils/configPaths.ts +0 -220
- package/src/utils/configValidator.ts +0 -100
- package/src/utils/constants.ts +0 -39
- package/src/utils/container.ts +0 -92
- package/src/utils/containerSetup.ts +0 -371
- package/src/utils/convertMessagesForAPI.ts +0 -326
- package/src/utils/cronTasks.ts +0 -128
- package/src/utils/cronTasksLock.ts +0 -182
- package/src/utils/cronToHuman.ts +0 -99
- package/src/utils/customCommands.ts +0 -95
- package/src/utils/editUtils.ts +0 -20
- package/src/utils/fileFormat.ts +0 -40
- package/src/utils/fileSearch.ts +0 -151
- package/src/utils/fileUtils.ts +0 -223
- package/src/utils/gitUtils.ts +0 -315
- package/src/utils/globalLogger.ts +0 -128
- package/src/utils/groupMessagesByApiRound.ts +0 -121
- package/src/utils/hookMatcher.ts +0 -176
- package/src/utils/markdownParser.ts +0 -317
- package/src/utils/mcpUtils.ts +0 -138
- package/src/utils/messageOperations.ts +0 -641
- package/src/utils/modelCapabilities.ts +0 -30
- package/src/utils/nameGenerator.ts +0 -99
- package/src/utils/notificationXml.ts +0 -57
- package/src/utils/openaiClient.ts +0 -251
- package/src/utils/parseCronExpression.ts +0 -78
- package/src/utils/path.ts +0 -72
- package/src/utils/pathEncoder.ts +0 -400
- package/src/utils/pathSafety.ts +0 -40
- package/src/utils/promptHistory.ts +0 -168
- package/src/utils/ripgrep.ts +0 -35
- package/src/utils/shellResolver.ts +0 -190
- package/src/utils/skillParser.ts +0 -246
- package/src/utils/stringUtils.ts +0 -156
- package/src/utils/subagentParser.ts +0 -279
- package/src/utils/taskReminder.ts +0 -96
- package/src/utils/tokenCalculation.ts +0 -43
- package/src/utils/tokenEstimate.ts +0 -34
- package/src/utils/toolResultStorage.ts +0 -117
- package/src/utils/worktreeSession.ts +0 -26
- package/src/utils/worktreeUtils.ts +0 -786
- package/src/workflow/budgetTracker.ts +0 -34
- package/src/workflow/concurrencyLimiter.ts +0 -47
- package/src/workflow/journal.ts +0 -95
- package/src/workflow/progressReporter.ts +0 -141
- package/src/workflow/runState.ts +0 -65
- package/src/workflow/scriptRuntime.ts +0 -274
- package/src/workflow/structuredOutput.ts +0 -123
- package/src/workflow/types.ts +0 -101
- package/src/workflow/workflowApis.ts +0 -410
- package/vendor/ripgrep/linux-aarch64/rg +0 -0
- package/vendor/ripgrep/linux-x86_64/rg +0 -0
- package/vendor/ripgrep/macos-aarch64/rg +0 -0
- package/vendor/ripgrep/macos-x86_64/rg +0 -0
- package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
- package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export class BudgetTracker {
|
|
2
|
-
private totalSpent = 0;
|
|
3
|
-
|
|
4
|
-
constructor(private _total: number | null = null) {}
|
|
5
|
-
|
|
6
|
-
addUsage(tokens: number): void {
|
|
7
|
-
this.totalSpent += tokens;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
spent(): number {
|
|
11
|
-
return this.totalSpent;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
remaining(): number {
|
|
15
|
-
if (this._total === null) return Infinity;
|
|
16
|
-
return Math.max(0, this._total - this.totalSpent);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
isExceeded(): boolean {
|
|
20
|
-
return this._total !== null && this.totalSpent >= this._total;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
get total(): number | null {
|
|
24
|
-
return this._total;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
toBudgetInfo(): import("./types.js").BudgetInfo {
|
|
28
|
-
return {
|
|
29
|
-
total: this._total,
|
|
30
|
-
spent: () => this.totalSpent,
|
|
31
|
-
remaining: () => this.remaining(),
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
export class ConcurrencyLimiter {
|
|
2
|
-
private running = 0;
|
|
3
|
-
private queue: Array<() => void> = [];
|
|
4
|
-
private readonly maxConcurrency: number;
|
|
5
|
-
private activeSet = new Set<Promise<unknown>>();
|
|
6
|
-
|
|
7
|
-
constructor(maxConcurrency: number) {
|
|
8
|
-
this.maxConcurrency = Math.max(1, maxConcurrency);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async acquire(): Promise<void> {
|
|
12
|
-
if (this.running < this.maxConcurrency) {
|
|
13
|
-
this.running++;
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
return new Promise<void>((resolve) => {
|
|
17
|
-
this.queue.push(resolve);
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
release(): void {
|
|
22
|
-
this.running--;
|
|
23
|
-
if (this.queue.length > 0) {
|
|
24
|
-
this.running++;
|
|
25
|
-
const next = this.queue.shift()!;
|
|
26
|
-
next();
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
track<T>(promise: Promise<T>): Promise<T> {
|
|
31
|
-
this.activeSet.add(promise);
|
|
32
|
-
promise.finally(() => this.activeSet.delete(promise));
|
|
33
|
-
return promise;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async drain(): Promise<void> {
|
|
37
|
-
await Promise.allSettled(this.activeSet);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
get activeCount(): number {
|
|
41
|
-
return this.running;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
get pendingCount(): number {
|
|
45
|
-
return this.queue.length;
|
|
46
|
-
}
|
|
47
|
-
}
|
package/src/workflow/journal.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import type { JournalLine } from "./types.js";
|
|
4
|
-
|
|
5
|
-
export class Journal {
|
|
6
|
-
private entries: JournalLine[] = [];
|
|
7
|
-
private stream: fs.WriteStream | null = null;
|
|
8
|
-
|
|
9
|
-
constructor(public readonly filePath: string) {}
|
|
10
|
-
|
|
11
|
-
async init(): Promise<void> {
|
|
12
|
-
// Ensure directory exists
|
|
13
|
-
await fs.promises.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
14
|
-
// Create append-only write stream
|
|
15
|
-
this.stream = fs.createWriteStream(this.filePath, { flags: "a" });
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
append(entry: JournalLine): void {
|
|
19
|
-
this.entries.push(entry);
|
|
20
|
-
if (this.stream && !this.stream.destroyed && !this.stream.writableEnded) {
|
|
21
|
-
this.stream.write(JSON.stringify(entry) + "\n");
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
appendLog(message: string): void {
|
|
26
|
-
this.append({ type: "log", message });
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
getCachedResult(agentIndex: number): unknown | undefined {
|
|
30
|
-
const agentEntries = this.entries.filter(
|
|
31
|
-
(e): e is import("./types.js").JournalEntry => !("type" in e),
|
|
32
|
-
);
|
|
33
|
-
// Check if this agent was marked as failed
|
|
34
|
-
const failedEntry = this.entries.find(
|
|
35
|
-
(e): e is import("./types.js").AgentFailedEntry =>
|
|
36
|
-
"type" in e && e.type === "agent_failed" && e.agentIndex === agentIndex,
|
|
37
|
-
);
|
|
38
|
-
if (failedEntry) {
|
|
39
|
-
return undefined; // Failed agents don't have cached results
|
|
40
|
-
}
|
|
41
|
-
if (agentIndex < agentEntries.length) {
|
|
42
|
-
return agentEntries[agentIndex].result;
|
|
43
|
-
}
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
get length(): number {
|
|
48
|
-
return this.entries.length;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Count of agent entries (excluding log entries) */
|
|
52
|
-
get agentEntryCount(): number {
|
|
53
|
-
return this.entries.filter(
|
|
54
|
-
(e): e is import("./types.js").JournalEntry => !("type" in e),
|
|
55
|
-
).length;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Remove the agent_failed entry for a given agent index (for retry support) */
|
|
59
|
-
removeFailedEntry(agentIndex: number): void {
|
|
60
|
-
this.entries = this.entries.filter(
|
|
61
|
-
(e) =>
|
|
62
|
-
!(
|
|
63
|
-
"type" in e &&
|
|
64
|
-
e.type === "agent_failed" &&
|
|
65
|
-
e.agentIndex === agentIndex
|
|
66
|
-
),
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async close(): Promise<void> {
|
|
71
|
-
const s = this.stream;
|
|
72
|
-
this.stream = null;
|
|
73
|
-
if (s) {
|
|
74
|
-
await new Promise<void>((resolve) => {
|
|
75
|
-
s.end(() => resolve());
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
static async load(filePath: string): Promise<Journal> {
|
|
81
|
-
const journal = new Journal(filePath);
|
|
82
|
-
try {
|
|
83
|
-
const content = await fs.promises.readFile(filePath, "utf-8");
|
|
84
|
-
for (const line of content.split("\n")) {
|
|
85
|
-
const trimmed = line.trim();
|
|
86
|
-
if (trimmed) {
|
|
87
|
-
journal.entries.push(JSON.parse(trimmed));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
} catch {
|
|
91
|
-
// File doesn't exist yet — empty journal
|
|
92
|
-
}
|
|
93
|
-
return journal;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
WorkflowPhaseState,
|
|
3
|
-
WorkflowMeta,
|
|
4
|
-
WorkflowProgressEvent,
|
|
5
|
-
} from "./types.js";
|
|
6
|
-
|
|
7
|
-
export class ProgressReporter {
|
|
8
|
-
private phases: WorkflowPhaseState[] = [];
|
|
9
|
-
private currentPhaseIndex = -1;
|
|
10
|
-
private agentCounter = 0;
|
|
11
|
-
private listeners: Array<(event: WorkflowProgressEvent) => void> = [];
|
|
12
|
-
private runId: string;
|
|
13
|
-
|
|
14
|
-
constructor(
|
|
15
|
-
private meta: WorkflowMeta,
|
|
16
|
-
runId?: string,
|
|
17
|
-
) {
|
|
18
|
-
this.runId = runId || "";
|
|
19
|
-
// Pre-initialize phases from meta so they appear even if the script
|
|
20
|
-
// doesn't call phase() explicitly
|
|
21
|
-
if (meta.phases?.length) {
|
|
22
|
-
for (const p of meta.phases) {
|
|
23
|
-
this.phases.push({
|
|
24
|
-
title: p.title,
|
|
25
|
-
agentCount: 0,
|
|
26
|
-
tokens: 0,
|
|
27
|
-
elapsed: 0,
|
|
28
|
-
startTime: Date.now(),
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
// Default to the first phase so agentStarted/agentCompleted
|
|
32
|
-
// track into it even without an explicit phase() call
|
|
33
|
-
this.currentPhaseIndex = 0;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
setPhase(title: string): void {
|
|
38
|
-
// Emit phase_completed for the previous phase
|
|
39
|
-
if (this.currentPhaseIndex >= 0) {
|
|
40
|
-
this.emit({
|
|
41
|
-
type: "phase_completed",
|
|
42
|
-
phaseIndex: this.currentPhaseIndex,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const existing = this.phases.findIndex((p) => p.title === title);
|
|
47
|
-
if (existing >= 0) {
|
|
48
|
-
this.currentPhaseIndex = existing;
|
|
49
|
-
} else {
|
|
50
|
-
this.phases.push({
|
|
51
|
-
title,
|
|
52
|
-
agentCount: 0,
|
|
53
|
-
tokens: 0,
|
|
54
|
-
elapsed: 0,
|
|
55
|
-
startTime: Date.now(),
|
|
56
|
-
});
|
|
57
|
-
this.currentPhaseIndex = this.phases.length - 1;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
this.emit({
|
|
61
|
-
type: "phase_started",
|
|
62
|
-
phaseIndex: this.currentPhaseIndex,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
agentStarted(): void {
|
|
67
|
-
this.agentCounter++;
|
|
68
|
-
if (this.currentPhaseIndex >= 0) {
|
|
69
|
-
this.phases[this.currentPhaseIndex].agentCount++;
|
|
70
|
-
}
|
|
71
|
-
this.emit({
|
|
72
|
-
type: "agent_started",
|
|
73
|
-
phaseIndex: this.currentPhaseIndex,
|
|
74
|
-
agentIndex: this.agentCounter - 1,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
agentCompleted(tokens: number): void {
|
|
79
|
-
if (this.currentPhaseIndex >= 0) {
|
|
80
|
-
this.phases[this.currentPhaseIndex].tokens += tokens;
|
|
81
|
-
this.phases[this.currentPhaseIndex].elapsed =
|
|
82
|
-
Date.now() - this.phases[this.currentPhaseIndex].startTime;
|
|
83
|
-
}
|
|
84
|
-
this.emit({
|
|
85
|
-
type: "agent_completed",
|
|
86
|
-
phaseIndex: this.currentPhaseIndex,
|
|
87
|
-
agentIndex: this.agentCounter - 1,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
agentFailed(agentIndex: number): void {
|
|
92
|
-
this.emit({
|
|
93
|
-
type: "agent_failed",
|
|
94
|
-
phaseIndex: this.currentPhaseIndex,
|
|
95
|
-
agentIndex,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
formatSummary(): string {
|
|
100
|
-
const phaseInfo =
|
|
101
|
-
this.currentPhaseIndex >= 0
|
|
102
|
-
? `Phase ${this.currentPhaseIndex + 1}/${this.phases.length}: ${this.phases[this.currentPhaseIndex].title}`
|
|
103
|
-
: "Initializing";
|
|
104
|
-
const totalTokens = this.phases.reduce((sum, p) => sum + p.tokens, 0);
|
|
105
|
-
const elapsed = this.phases.reduce((sum, p) => sum + p.elapsed, 0);
|
|
106
|
-
return `${phaseInfo} | ${this.agentCounter} agents | ${(totalTokens / 1000).toFixed(1)}k tokens | ${Math.round(elapsed / 1000)}s`;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
getPhaseStates(): WorkflowPhaseState[] {
|
|
110
|
-
return [...this.phases];
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
get totalAgents(): number {
|
|
114
|
-
return this.agentCounter;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
get totalTokens(): number {
|
|
118
|
-
return this.phases.reduce((sum, p) => sum + p.tokens, 0);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
onEvent(listener: (event: WorkflowProgressEvent) => void): void {
|
|
122
|
-
this.listeners.push(listener);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
private emit(
|
|
126
|
-
event: Omit<WorkflowProgressEvent, "runId" | "timestamp">,
|
|
127
|
-
): void {
|
|
128
|
-
const fullEvent: WorkflowProgressEvent = {
|
|
129
|
-
...event,
|
|
130
|
-
runId: this.runId,
|
|
131
|
-
timestamp: Date.now(),
|
|
132
|
-
};
|
|
133
|
-
for (const listener of this.listeners) {
|
|
134
|
-
try {
|
|
135
|
-
listener(fullEvent);
|
|
136
|
-
} catch {
|
|
137
|
-
// Swallow listener errors
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
package/src/workflow/runState.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import type { WorkflowRun } from "./types.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Persists WorkflowRun state to <runDir>/run-state.json.
|
|
7
|
-
* Enables run recovery across process restarts.
|
|
8
|
-
*/
|
|
9
|
-
export class RunStateStore {
|
|
10
|
-
constructor(private baseDir: string) {}
|
|
11
|
-
|
|
12
|
-
/** Persist a run's state to disk */
|
|
13
|
-
async save(run: WorkflowRun): Promise<void> {
|
|
14
|
-
const runDir = path.join(this.baseDir, run.runId);
|
|
15
|
-
await fs.promises.mkdir(runDir, { recursive: true });
|
|
16
|
-
const statePath = path.join(runDir, "run-state.json");
|
|
17
|
-
// Omit non-serializable fields
|
|
18
|
-
const { completionPromise, ...serializable } = run;
|
|
19
|
-
void completionPromise;
|
|
20
|
-
await fs.promises.writeFile(
|
|
21
|
-
statePath,
|
|
22
|
-
JSON.stringify(serializable, null, 2),
|
|
23
|
-
"utf-8",
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Load a single run's state from disk */
|
|
28
|
-
async load(runId: string): Promise<WorkflowRun | null> {
|
|
29
|
-
const statePath = path.join(this.baseDir, runId, "run-state.json");
|
|
30
|
-
try {
|
|
31
|
-
const content = await fs.promises.readFile(statePath, "utf-8");
|
|
32
|
-
return JSON.parse(content) as WorkflowRun;
|
|
33
|
-
} catch {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** List all persisted run IDs */
|
|
39
|
-
async listRuns(): Promise<string[]> {
|
|
40
|
-
try {
|
|
41
|
-
const entries = await fs.promises.readdir(this.baseDir, {
|
|
42
|
-
withFileTypes: true,
|
|
43
|
-
});
|
|
44
|
-
const runIds: string[] = [];
|
|
45
|
-
for (const entry of entries) {
|
|
46
|
-
if (entry.isDirectory() && entry.name.startsWith("wf_")) {
|
|
47
|
-
const statePath = path.join(
|
|
48
|
-
this.baseDir,
|
|
49
|
-
entry.name,
|
|
50
|
-
"run-state.json",
|
|
51
|
-
);
|
|
52
|
-
try {
|
|
53
|
-
await fs.promises.access(statePath);
|
|
54
|
-
runIds.push(entry.name);
|
|
55
|
-
} catch {
|
|
56
|
-
// Directory exists but no run-state.json — skip
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return runIds;
|
|
61
|
-
} catch {
|
|
62
|
-
return [];
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import type { WorkflowMeta } from "./types.js";
|
|
2
|
-
import { logger } from "../utils/globalLogger.js";
|
|
3
|
-
|
|
4
|
-
/** Patterns that are banned in workflow scripts for safety and determinism */
|
|
5
|
-
const BANNED_PATTERNS = [
|
|
6
|
-
{
|
|
7
|
-
pattern: /\brequire\s*\(/,
|
|
8
|
-
message: "require() is not available in workflow scripts",
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
pattern: /\bprocess\./,
|
|
12
|
-
message: "process.* is not available in workflow scripts",
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
pattern: /\beval\s*\(/,
|
|
16
|
-
message: "eval() is not available in workflow scripts",
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
pattern: /\bimport\s+/,
|
|
20
|
-
message: "import statements are not available in workflow scripts",
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
pattern: /\bDate\.now\s*\(/,
|
|
24
|
-
message: "Date.now() would break resume determinism",
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
pattern: /\bMath\.random\s*\(/,
|
|
28
|
-
message: "Math.random() would break resume determinism",
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
pattern: /\bnew\s+Date\s*\(\s*\)/,
|
|
32
|
-
message: "argless new Date() would break resume determinism",
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
pattern: /\brequire\s*\(\s*['"]fs['"]\s*\)/,
|
|
36
|
-
message: "filesystem access is not available in workflow scripts",
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
pattern: /\bfs\.\w+/,
|
|
40
|
-
message: "filesystem access is not available in workflow scripts",
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
pattern: /\bchild_process\b/,
|
|
44
|
-
message: "child_process is not available in workflow scripts",
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
pattern: /\b__dirname\b/,
|
|
48
|
-
message: "__dirname is not available in workflow scripts",
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
pattern: /\b__filename\b/,
|
|
52
|
-
message: "__filename is not available in workflow scripts",
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
pattern: /\bglobal\./,
|
|
56
|
-
message: "global.* is not available in workflow scripts",
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
pattern: /\bglobalThis\b/,
|
|
60
|
-
message: "globalThis is not available in workflow scripts",
|
|
61
|
-
},
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
export interface ParsedScript {
|
|
65
|
-
meta: WorkflowMeta;
|
|
66
|
-
body: string;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface ScriptValidationResult {
|
|
70
|
-
valid: boolean;
|
|
71
|
-
errors: string[];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Validate a workflow script for banned patterns and required structure
|
|
76
|
-
*/
|
|
77
|
-
export function validateScript(script: string): ScriptValidationResult {
|
|
78
|
-
const errors: string[] = [];
|
|
79
|
-
|
|
80
|
-
// Check for export const meta = {...}
|
|
81
|
-
if (!/export\s+const\s+meta\s*=/.test(script)) {
|
|
82
|
-
errors.push("Script must start with 'export const meta = {...}'");
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Check banned patterns
|
|
86
|
-
for (const { pattern, message } of BANNED_PATTERNS) {
|
|
87
|
-
if (pattern.test(script)) {
|
|
88
|
-
errors.push(message);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return { valid: errors.length === 0, errors };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Parse a workflow script into meta and body.
|
|
97
|
-
* Extracts the `export const meta = {...}` declaration and the rest of the script.
|
|
98
|
-
*/
|
|
99
|
-
export function parseScript(script: string): ParsedScript {
|
|
100
|
-
// Match: export const meta = { ... }
|
|
101
|
-
// We need to find the matching closing brace for the meta object
|
|
102
|
-
const metaStartMatch = script.match(/export\s+const\s+meta\s*=\s*/);
|
|
103
|
-
if (!metaStartMatch) {
|
|
104
|
-
throw new Error("Script must contain 'export const meta = {...}'");
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const metaStartIndex = metaStartMatch.index! + metaStartMatch[0].length;
|
|
108
|
-
|
|
109
|
-
// Find the matching closing brace for the meta object
|
|
110
|
-
let depth = 0;
|
|
111
|
-
let metaEndIndex = -1;
|
|
112
|
-
let inString: string | null = null;
|
|
113
|
-
let escaped = false;
|
|
114
|
-
|
|
115
|
-
for (let i = metaStartIndex; i < script.length; i++) {
|
|
116
|
-
const char = script[i];
|
|
117
|
-
|
|
118
|
-
if (escaped) {
|
|
119
|
-
escaped = false;
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (char === "\\") {
|
|
124
|
-
escaped = true;
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (inString) {
|
|
129
|
-
if (char === inString) {
|
|
130
|
-
inString = null;
|
|
131
|
-
}
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (char === '"' || char === "'" || char === "`") {
|
|
136
|
-
inString = char;
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (char === "{") {
|
|
141
|
-
depth++;
|
|
142
|
-
} else if (char === "}") {
|
|
143
|
-
depth--;
|
|
144
|
-
if (depth === 0) {
|
|
145
|
-
metaEndIndex = i + 1;
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (metaEndIndex === -1) {
|
|
152
|
-
throw new Error("Could not find matching closing brace for meta object");
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const metaString = script.slice(metaStartIndex, metaEndIndex);
|
|
156
|
-
|
|
157
|
-
// Parse meta as JSON (allowing JS object syntax — unquoted keys, trailing commas)
|
|
158
|
-
// We use a safe approach: wrap in parentheses and use Function to evaluate
|
|
159
|
-
let meta: WorkflowMeta;
|
|
160
|
-
try {
|
|
161
|
-
// Remove trailing comma before closing brace/bracket (common in JS objects)
|
|
162
|
-
const cleaned = metaString.replace(/,\s*([}\]])/g, "$1");
|
|
163
|
-
// Wrap unquoted keys with double quotes
|
|
164
|
-
const quoted = cleaned.replace(/(\w+)\s*:/g, '"$1":');
|
|
165
|
-
meta = JSON.parse(quoted);
|
|
166
|
-
} catch {
|
|
167
|
-
// Fallback: use Function constructor for safe evaluation
|
|
168
|
-
try {
|
|
169
|
-
const fn = new Function(`"use strict"; return (${metaString});`);
|
|
170
|
-
meta = fn();
|
|
171
|
-
} catch (e) {
|
|
172
|
-
throw new Error(
|
|
173
|
-
`Failed to parse meta object: ${e instanceof Error ? e.message : String(e)}`,
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Validate required meta fields
|
|
179
|
-
if (!meta.name || typeof meta.name !== "string") {
|
|
180
|
-
throw new Error("meta.name is required and must be a string");
|
|
181
|
-
}
|
|
182
|
-
if (!meta.description || typeof meta.description !== "string") {
|
|
183
|
-
throw new Error("meta.description is required and must be a string");
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Body is everything after the meta declaration (and any trailing semicolons/newlines)
|
|
187
|
-
const bodyStart = metaEndIndex;
|
|
188
|
-
const body = script.slice(bodyStart).replace(/^\s*;?\s*/, "");
|
|
189
|
-
|
|
190
|
-
return { meta, body };
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Execute a workflow script with the given API closures.
|
|
195
|
-
* The script body is wrapped in an async IIFE and executed via new Function().
|
|
196
|
-
*/
|
|
197
|
-
export async function executeScript(
|
|
198
|
-
script: string,
|
|
199
|
-
apis: {
|
|
200
|
-
agent: (prompt: string, opts?: Record<string, unknown>) => Promise<unknown>;
|
|
201
|
-
parallel: (thunks: Array<() => Promise<unknown>>) => Promise<unknown[]>;
|
|
202
|
-
pipeline: (
|
|
203
|
-
items: unknown[],
|
|
204
|
-
...stages: Array<
|
|
205
|
-
(prev: unknown, item: unknown, index: number) => Promise<unknown>
|
|
206
|
-
>
|
|
207
|
-
) => Promise<unknown[]>;
|
|
208
|
-
phase: (title: string) => void;
|
|
209
|
-
log: (message: string) => void;
|
|
210
|
-
args: unknown;
|
|
211
|
-
budget: unknown;
|
|
212
|
-
},
|
|
213
|
-
abortSignal?: AbortSignal,
|
|
214
|
-
): Promise<{ meta: WorkflowMeta; result: unknown }> {
|
|
215
|
-
// Validate first
|
|
216
|
-
const validation = validateScript(script);
|
|
217
|
-
if (!validation.valid) {
|
|
218
|
-
throw new Error(
|
|
219
|
-
`Script validation failed:\n${validation.errors.join("\n")}`,
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Parse script
|
|
224
|
-
const { meta, body } = parseScript(script);
|
|
225
|
-
|
|
226
|
-
logger.info(
|
|
227
|
-
`[Workflow] Executing script "${meta.name}": ${meta.description}`,
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
// Wrap the body in an async function and inject API closures
|
|
231
|
-
const fn = new Function(
|
|
232
|
-
"agent",
|
|
233
|
-
"parallel",
|
|
234
|
-
"pipeline",
|
|
235
|
-
"phase",
|
|
236
|
-
"log",
|
|
237
|
-
"args",
|
|
238
|
-
"budget",
|
|
239
|
-
`"use strict"; return (async () => { ${body} })();`,
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
// Execute with abort support
|
|
243
|
-
const scriptPromise = fn(
|
|
244
|
-
apis.agent,
|
|
245
|
-
apis.parallel,
|
|
246
|
-
apis.pipeline,
|
|
247
|
-
apis.phase,
|
|
248
|
-
apis.log,
|
|
249
|
-
apis.args,
|
|
250
|
-
apis.budget,
|
|
251
|
-
);
|
|
252
|
-
|
|
253
|
-
let result: unknown;
|
|
254
|
-
if (abortSignal) {
|
|
255
|
-
result = await Promise.race([
|
|
256
|
-
scriptPromise,
|
|
257
|
-
new Promise<never>((_, reject) => {
|
|
258
|
-
if (abortSignal.aborted) {
|
|
259
|
-
reject(new Error("Workflow aborted"));
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
abortSignal.addEventListener(
|
|
263
|
-
"abort",
|
|
264
|
-
() => reject(new Error("Workflow aborted")),
|
|
265
|
-
{ once: true },
|
|
266
|
-
);
|
|
267
|
-
}),
|
|
268
|
-
]);
|
|
269
|
-
} else {
|
|
270
|
-
result = await scriptPromise;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return { meta, result };
|
|
274
|
-
}
|