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,205 +0,0 @@
|
|
|
1
|
-
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
2
|
-
import { WORKFLOW_TOOL_NAME } from "../constants/tools.js";
|
|
3
|
-
import { logger } from "../utils/globalLogger.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Workflow tool plugin for executing deterministic multi-subagent orchestration scripts.
|
|
7
|
-
*
|
|
8
|
-
* The AI model calls this tool with a JavaScript script that orchestrates
|
|
9
|
-
* multiple subagents via agent(), parallel(), pipeline(), phase(), log() APIs.
|
|
10
|
-
* Workflows run in the background — the tool returns immediately with a run ID,
|
|
11
|
-
* and a <task-notification> arrives when the workflow completes.
|
|
12
|
-
*/
|
|
13
|
-
export const workflowTool: ToolPlugin = {
|
|
14
|
-
name: WORKFLOW_TOOL_NAME,
|
|
15
|
-
config: {
|
|
16
|
-
type: "function" as const,
|
|
17
|
-
function: {
|
|
18
|
-
name: WORKFLOW_TOOL_NAME,
|
|
19
|
-
description:
|
|
20
|
-
"Execute a workflow script that orchestrates multiple subagents deterministically. Workflows run in the background — this tool returns immediately with a run ID, and a <task-notification> arrives when the workflow completes. Use /workflows to watch live progress.",
|
|
21
|
-
parameters: {
|
|
22
|
-
type: "object",
|
|
23
|
-
properties: {
|
|
24
|
-
script: {
|
|
25
|
-
type: "string",
|
|
26
|
-
description:
|
|
27
|
-
"Inline workflow script (JavaScript). Must start with 'export const meta = {name, description, phases}'. Pass the script inline — do not Write it to a file first. Every Workflow invocation automatically persists its script.",
|
|
28
|
-
},
|
|
29
|
-
scriptPath: {
|
|
30
|
-
type: "string",
|
|
31
|
-
description:
|
|
32
|
-
"Path to a saved workflow script file. Use this to re-run or iterate on a previously saved script. One of 'script' or 'scriptPath' is required.",
|
|
33
|
-
},
|
|
34
|
-
args: {
|
|
35
|
-
description:
|
|
36
|
-
"Arguments passed to the workflow script as the `args` global. Pass arrays/objects as actual JSON values, NOT as a JSON-encoded string.",
|
|
37
|
-
},
|
|
38
|
-
resumeFromRunId: {
|
|
39
|
-
type: "string",
|
|
40
|
-
description:
|
|
41
|
-
"Resume from a previous run's journal. Cached agent results are replayed instantly; the first edited/new call and everything after it runs live.",
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
prompt: () =>
|
|
49
|
-
`Execute a workflow script that orchestrates multiple subagents deterministically. Workflows run in the background — this tool returns immediately with a task ID, and a <task-notification> arrives when the workflow completes. Use /workflows to watch live progress.
|
|
50
|
-
|
|
51
|
-
A workflow structures work across many agents — to be comprehensive (decompose and cover in parallel), to be confident (independent perspectives and adversarial checks before committing), or to take on scale one context can't hold (migrations, audits, broad sweeps). The script is where you encode that structure: what fans out, what verifies, what synthesizes.
|
|
52
|
-
|
|
53
|
-
ONLY call this tool when the user has explicitly opted into multi-agent orchestration. Workflows can spawn dozens of agents and consume a large amount of tokens; the user must request that scale, not have it inferred. Explicit opt-in means one of:
|
|
54
|
-
- The user directly asked you to run a workflow or use multi-agent orchestration in their own words ("use a workflow", "run a workflow", "fan out agents", "orchestrate this with subagents"). The ask must be in the user's words — a task that would merely benefit from a workflow does not count.
|
|
55
|
-
- The user invoked a skill or slash command whose instructions tell you to call Workflow.
|
|
56
|
-
- The user asked you to run a specific named or saved workflow.
|
|
57
|
-
|
|
58
|
-
For any other task — even one that would clearly benefit from parallelism — do NOT call this tool. Use the Agent tool for individual subagents, or briefly describe what a multi-agent workflow could do and how much it would roughly cost, and ask the user whether to run it.
|
|
59
|
-
|
|
60
|
-
When you do call it, the right move is often **hybrid**: scout inline first (list the files, find the channels, scope the diff) to discover the work-list, then call Workflow to pipeline over it.
|
|
61
|
-
|
|
62
|
-
Common single-phase workflows you can chain across turns:
|
|
63
|
-
- **Understand** — parallel readers over relevant subsystems → structured map
|
|
64
|
-
- **Design** — judge panel of N independent approaches → scored synthesis
|
|
65
|
-
- **Review** — dimensions → find → adversarially verify
|
|
66
|
-
- **Research** — multi-modal sweep → deep-read → synthesize
|
|
67
|
-
- **Migrate** — discover sites → transform each (worktree isolation) → verify
|
|
68
|
-
|
|
69
|
-
For larger work, run several in sequence — read each result before deciding the next phase.
|
|
70
|
-
|
|
71
|
-
Every script must begin with \`export const meta = {...}\`:
|
|
72
|
-
export const meta = {
|
|
73
|
-
name: 'find-flaky-tests',
|
|
74
|
-
description: 'Find flaky tests and propose fixes',
|
|
75
|
-
phases: [
|
|
76
|
-
{ title: 'Scan', detail: 'grep test logs for retries' },
|
|
77
|
-
{ title: 'Fix', detail: 'one agent per flaky test' },
|
|
78
|
-
],
|
|
79
|
-
}
|
|
80
|
-
// script body starts here — use agent()/parallel()/pipeline()/phase()/log()
|
|
81
|
-
|
|
82
|
-
Script body hooks:
|
|
83
|
-
- **agent(prompt, opts?)**: Promise<any> — spawn a subagent. Without schema, returns its final text as a string. With schema (a JSON Schema), the subagent is forced to call a StructuredOutput tool and agent() returns the validated object — no parsing needed. Returns null if the user skips the agent mid-run or the subagent dies on a terminal API error (filter with .filter(Boolean)). opts.label overrides the display label. opts.phase assigns this agent to a progress group. opts.model overrides the model for this agent call. opts.agentType uses a custom subagent type instead of the default.
|
|
84
|
-
- **pipeline(items, stage1, stage2, ...)**: Promise<any[]> — run each item through all stages independently, NO barrier between stages. Item A can be in stage 3 while item B is still in stage 1. This is the DEFAULT for multi-stage work. Every stage callback receives (prevResult, originalItem, index). In the first stage prevResult is undefined; in later stages it is the return value of the previous stage. A stage that throws drops that item to null. Example single-stage: \`pipeline(files, (prev, file) => agent('Read ' + file))\`. Example two-stage: \`pipeline(files, (prev, file) => agent('Read ' + file), (prev, file) => agent('Summarize: ' + prev))\`.
|
|
85
|
-
- **parallel(thunks: Array<() => Promise<any>>)**: Promise<any[]> — run tasks concurrently. This is a BARRIER: awaits all thunks before returning. A thunk that throws resolves to null in the result array. Use ONLY when you genuinely need all results together.
|
|
86
|
-
- **log(message: string)**: void — emit a progress message
|
|
87
|
-
- **phase(title: string)**: void — start a new phase; subsequent agent() calls are grouped under this title
|
|
88
|
-
- **args**: any — the value passed as Workflow's args input, verbatim
|
|
89
|
-
- **budget**: {total: number|null, spent(): number, remaining(): number} — the turn's token target
|
|
90
|
-
|
|
91
|
-
Scripts are plain JavaScript, NOT TypeScript — type annotations fail to parse. The script body runs in an async context — use await directly. Standard JS built-ins (JSON, Math, Array, etc.) are available — EXCEPT Date.now()/Math.random()/argless new Date(), which throw (they would break resume). No filesystem or Node.js API access.
|
|
92
|
-
|
|
93
|
-
DEFAULT TO pipeline(). Only reach for a barrier (parallel between stages) when you genuinely need ALL prior-stage results together.
|
|
94
|
-
|
|
95
|
-
Concurrent agent() calls are capped at min(16, cpu cores - 2) per workflow. Total agent count is capped at 1000 per run. A single parallel()/pipeline() call accepts at most 4096 items.
|
|
96
|
-
|
|
97
|
-
Quality patterns:
|
|
98
|
-
- **Adversarial verify**: spawn N independent skeptics per finding, each prompted to REFUTE. Kill if >=majority refute.
|
|
99
|
-
- **Judge panel**: generate N independent approaches, score with parallel judges, synthesize from the winner.
|
|
100
|
-
- **Loop-until-dry**: keep spawning finders until K consecutive rounds return nothing new.
|
|
101
|
-
- **Multi-modal sweep**: parallel agents each searching a different way (by-container, by-content, by-entity).
|
|
102
|
-
- **Completeness critic**: a final agent that asks "what's missing?" — findings become next round of work.
|
|
103
|
-
- **No silent caps**: if a workflow bounds coverage, log() what was dropped.
|
|
104
|
-
|
|
105
|
-
## Resume
|
|
106
|
-
|
|
107
|
-
The tool result includes a runId. To resume after a pause, kill, or script edit, relaunch with Workflow({scriptPath, resumeFromRunId}) — the longest unchanged prefix of agent() calls returns cached results instantly; the first edited/new call and everything after it runs live.
|
|
108
|
-
|
|
109
|
-
Use this tool for multi-step orchestration where control flow should be deterministic (loops, conditionals, fan-out) rather than model-driven.`,
|
|
110
|
-
|
|
111
|
-
execute: async (
|
|
112
|
-
args: Record<string, unknown>,
|
|
113
|
-
context: ToolContext,
|
|
114
|
-
): Promise<ToolResult> => {
|
|
115
|
-
const workflowManager = context.workflowManager;
|
|
116
|
-
if (!workflowManager) {
|
|
117
|
-
return {
|
|
118
|
-
success: false,
|
|
119
|
-
content: "",
|
|
120
|
-
error: "Workflow manager not available in tool context",
|
|
121
|
-
shortResult: "Workflow execution failed",
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const script = args.script as string | undefined;
|
|
126
|
-
const scriptPath = args.scriptPath as string | undefined;
|
|
127
|
-
const workflowArgs = args.args;
|
|
128
|
-
const resumeFromRunId = args.resumeFromRunId as string | undefined;
|
|
129
|
-
|
|
130
|
-
// Resolve script text
|
|
131
|
-
let scriptText: string;
|
|
132
|
-
if (script) {
|
|
133
|
-
scriptText = script;
|
|
134
|
-
} else if (scriptPath) {
|
|
135
|
-
try {
|
|
136
|
-
const fs = await import("fs/promises");
|
|
137
|
-
scriptText = await fs.readFile(scriptPath, "utf-8");
|
|
138
|
-
} catch (error) {
|
|
139
|
-
return {
|
|
140
|
-
success: false,
|
|
141
|
-
content: "",
|
|
142
|
-
error: `Failed to read script file: ${error instanceof Error ? error.message : String(error)}`,
|
|
143
|
-
shortResult: "Workflow script read failed",
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
} else {
|
|
147
|
-
return {
|
|
148
|
-
success: false,
|
|
149
|
-
content: "",
|
|
150
|
-
error: "Either 'script' or 'scriptPath' parameter is required",
|
|
151
|
-
shortResult: "Workflow execution failed",
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
try {
|
|
156
|
-
// Create run
|
|
157
|
-
const run = await workflowManager.createRun(scriptText, workflowArgs, {
|
|
158
|
-
resumeFromRunId,
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
// Start execution in background
|
|
162
|
-
await workflowManager.startRun(run.runId);
|
|
163
|
-
|
|
164
|
-
return {
|
|
165
|
-
success: true,
|
|
166
|
-
content: [
|
|
167
|
-
`Workflow started with run ID: ${run.runId}`,
|
|
168
|
-
`Name: ${run.meta.name}`,
|
|
169
|
-
`Description: ${run.meta.description}`,
|
|
170
|
-
run.meta.phases?.length
|
|
171
|
-
? `Phases: ${run.meta.phases.map((p) => p.title).join(" → ")}`
|
|
172
|
-
: "",
|
|
173
|
-
`The workflow is running in the background. You will be notified automatically when it completes.`,
|
|
174
|
-
`Use /workflows to watch live progress.`,
|
|
175
|
-
`Script saved to: ${run.scriptPath}`,
|
|
176
|
-
]
|
|
177
|
-
.filter(Boolean)
|
|
178
|
-
.join("\n"),
|
|
179
|
-
shortResult: `Workflow started: ${run.meta.name} (${run.runId})`,
|
|
180
|
-
};
|
|
181
|
-
} catch (error) {
|
|
182
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
183
|
-
logger.error(`[Workflow Tool] execution failed: ${msg}`);
|
|
184
|
-
return {
|
|
185
|
-
success: false,
|
|
186
|
-
content: `Workflow failed: ${msg}. Fix the error and try again.`,
|
|
187
|
-
error: `Workflow execution failed: ${msg}`,
|
|
188
|
-
shortResult: "Workflow execution failed",
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
},
|
|
192
|
-
|
|
193
|
-
formatCompactParams: (params: Record<string, unknown>) => {
|
|
194
|
-
if (params.scriptPath) {
|
|
195
|
-
return `scriptPath: ${params.scriptPath}`;
|
|
196
|
-
}
|
|
197
|
-
const script = params.script as string;
|
|
198
|
-
if (script) {
|
|
199
|
-
// Extract meta.name from the script
|
|
200
|
-
const nameMatch = script.match(/name:\s*['"]([^'"]+)['"]/);
|
|
201
|
-
return nameMatch ? nameMatch[1] : script.slice(0, 50) + "...";
|
|
202
|
-
}
|
|
203
|
-
return "workflow";
|
|
204
|
-
},
|
|
205
|
-
};
|
package/src/tools/writeTool.ts
DELETED
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
import { readFile, writeFile, mkdir, stat } from "fs/promises";
|
|
2
|
-
import { createHash } from "crypto";
|
|
3
|
-
import { dirname } from "path";
|
|
4
|
-
import { logger } from "../utils/globalLogger.js";
|
|
5
|
-
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
6
|
-
import { resolvePath, getDisplayPath } from "../utils/path.js";
|
|
7
|
-
import { READ_TOOL_NAME } from "../constants/tools.js";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* File Write Tool Plugin
|
|
11
|
-
*/
|
|
12
|
-
export const writeTool: ToolPlugin = {
|
|
13
|
-
name: "Write",
|
|
14
|
-
isConcurrencySafe: false,
|
|
15
|
-
config: {
|
|
16
|
-
type: "function",
|
|
17
|
-
function: {
|
|
18
|
-
name: "Write",
|
|
19
|
-
description: "Writes a file to the local filesystem.",
|
|
20
|
-
parameters: {
|
|
21
|
-
type: "object",
|
|
22
|
-
properties: {
|
|
23
|
-
file_path: {
|
|
24
|
-
type: "string",
|
|
25
|
-
description:
|
|
26
|
-
"The absolute path to the file to write (must be absolute, not relative)",
|
|
27
|
-
},
|
|
28
|
-
content: {
|
|
29
|
-
type: "string",
|
|
30
|
-
description: "The content to write to the file",
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
required: ["file_path", "content"],
|
|
34
|
-
additionalProperties: false,
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
prompt: () => `Writes a file to the local filesystem.
|
|
39
|
-
|
|
40
|
-
Usage:
|
|
41
|
-
- This tool will overwrite the existing file if there is one at the provided path.
|
|
42
|
-
- If this is an existing file, you MUST use the \`${READ_TOOL_NAME}\` tool first to read the file's contents. This tool will fail if you did not read the file first.
|
|
43
|
-
- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
|
|
44
|
-
- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
|
|
45
|
-
- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.
|
|
46
|
-
- IMPORTANT: Always provide file_path parameter before content parameter when calling this tool.`,
|
|
47
|
-
execute: async (
|
|
48
|
-
args: Record<string, unknown>,
|
|
49
|
-
context: ToolContext,
|
|
50
|
-
): Promise<ToolResult> => {
|
|
51
|
-
const filePath = args.file_path as string;
|
|
52
|
-
const content = args.content as string;
|
|
53
|
-
|
|
54
|
-
// Validate required parameters
|
|
55
|
-
if (!filePath || typeof filePath !== "string") {
|
|
56
|
-
return {
|
|
57
|
-
success: false,
|
|
58
|
-
content: "",
|
|
59
|
-
error: "file_path parameter is required and must be a string",
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (typeof content !== "string") {
|
|
64
|
-
return {
|
|
65
|
-
success: false,
|
|
66
|
-
content: "",
|
|
67
|
-
error: "content parameter is required and must be a string",
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Trigger conditional rule loading for this file
|
|
72
|
-
context.messageManager?.triggerFileRead(filePath);
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
const resolvedPath = resolvePath(filePath, context.workdir);
|
|
76
|
-
|
|
77
|
-
// Check if file already exists
|
|
78
|
-
let originalContent = "";
|
|
79
|
-
let isExistingFile = false;
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
originalContent = await readFile(resolvedPath, "utf-8");
|
|
83
|
-
isExistingFile = true;
|
|
84
|
-
} catch {
|
|
85
|
-
// File doesn't exist, this is normal for new file creation
|
|
86
|
-
isExistingFile = false;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Read-before-write + staleness guards (aligned with Claude Code).
|
|
90
|
-
// Only enforced for existing files when readFileState is available
|
|
91
|
-
// (production always injects it; new-file creation always bypasses).
|
|
92
|
-
// Grep does not register a file as read, so Grep-then-Write on an
|
|
93
|
-
// existing file is still rejected. Staleness uses the same `>` + full-
|
|
94
|
-
// read content-hash fallback as editTool to avoid false positives from
|
|
95
|
-
// git checkout / editor round-trip save / cloud sync / antivirus.
|
|
96
|
-
// Plan mode is excluded: it has its own stricter write gate (plan-file-
|
|
97
|
-
// only, enforced in permissionManager) whose denial message must surface
|
|
98
|
-
// instead of being masked by a read-state rejection.
|
|
99
|
-
if (
|
|
100
|
-
isExistingFile &&
|
|
101
|
-
context.readFileState &&
|
|
102
|
-
context.permissionMode !== "plan"
|
|
103
|
-
) {
|
|
104
|
-
const state = context.readFileState.get(resolvedPath);
|
|
105
|
-
if (!state) {
|
|
106
|
-
return {
|
|
107
|
-
success: false,
|
|
108
|
-
content: "",
|
|
109
|
-
error:
|
|
110
|
-
"File has not been read yet. Read it first before writing to it.",
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
const currentStats = await stat(resolvedPath);
|
|
114
|
-
if (currentStats.mtime.getTime() > state.mtime) {
|
|
115
|
-
const isFullRead =
|
|
116
|
-
state.offset === undefined && state.limit === undefined;
|
|
117
|
-
const contentUnchanged =
|
|
118
|
-
isFullRead &&
|
|
119
|
-
createHash("sha256").update(originalContent).digest("hex") ===
|
|
120
|
-
state.hash;
|
|
121
|
-
if (!contentUnchanged) {
|
|
122
|
-
return {
|
|
123
|
-
success: false,
|
|
124
|
-
content: "",
|
|
125
|
-
error:
|
|
126
|
-
"File has been unexpectedly modified since last read. Read it again before writing to it.",
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Check if overwriting existing file but content is the same
|
|
133
|
-
if (isExistingFile && originalContent === content) {
|
|
134
|
-
return {
|
|
135
|
-
success: true,
|
|
136
|
-
content: `File ${filePath} already has the same content, no changes needed`,
|
|
137
|
-
shortResult: "No changes needed",
|
|
138
|
-
filePath: resolvedPath,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Ensure directory exists
|
|
143
|
-
const fileDir = dirname(resolvedPath);
|
|
144
|
-
try {
|
|
145
|
-
await mkdir(fileDir, { recursive: true });
|
|
146
|
-
} catch (mkdirError) {
|
|
147
|
-
// Ignore directory already exists error
|
|
148
|
-
if (
|
|
149
|
-
mkdirError instanceof Error &&
|
|
150
|
-
!mkdirError.message.includes("EEXIST")
|
|
151
|
-
) {
|
|
152
|
-
logger.warn(
|
|
153
|
-
`Failed to create directory ${fileDir}: ${mkdirError.message}`,
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Permission check after validation but before real operation
|
|
159
|
-
if (context.permissionManager) {
|
|
160
|
-
try {
|
|
161
|
-
const permissionContext = context.permissionManager.createContext(
|
|
162
|
-
"Write",
|
|
163
|
-
context.permissionMode || "default",
|
|
164
|
-
context.canUseToolCallback,
|
|
165
|
-
{ file_path: filePath, content },
|
|
166
|
-
context.toolCallId,
|
|
167
|
-
);
|
|
168
|
-
const permissionResult =
|
|
169
|
-
await context.permissionManager.checkPermission(permissionContext);
|
|
170
|
-
|
|
171
|
-
if (permissionResult.behavior === "deny") {
|
|
172
|
-
return {
|
|
173
|
-
success: false,
|
|
174
|
-
content: "",
|
|
175
|
-
error: `Write operation denied by user, reason: ${permissionResult.message || "No reason provided"}`,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
} catch {
|
|
179
|
-
return {
|
|
180
|
-
success: false,
|
|
181
|
-
content: "",
|
|
182
|
-
error: "Permission check failed",
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Record snapshot for reversion
|
|
188
|
-
let snapshotId: string | undefined;
|
|
189
|
-
if (context.reversionManager && context.messageId) {
|
|
190
|
-
snapshotId = await context.reversionManager.recordSnapshot(
|
|
191
|
-
context.messageId,
|
|
192
|
-
resolvedPath,
|
|
193
|
-
isExistingFile ? "modify" : "create",
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Write file
|
|
198
|
-
try {
|
|
199
|
-
await writeFile(resolvedPath, content, "utf-8");
|
|
200
|
-
// Commit snapshot on success
|
|
201
|
-
if (context.reversionManager && snapshotId) {
|
|
202
|
-
await context.reversionManager.commitSnapshot(snapshotId);
|
|
203
|
-
}
|
|
204
|
-
} catch (writeError) {
|
|
205
|
-
return {
|
|
206
|
-
success: false,
|
|
207
|
-
content: "",
|
|
208
|
-
error: `Failed to write file: ${writeError instanceof Error ? writeError.message : String(writeError)}`,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Update readFileState so subsequent serialized edits pass staleness check
|
|
213
|
-
if (context.readFileState) {
|
|
214
|
-
const newStats = await stat(resolvedPath);
|
|
215
|
-
const hash = createHash("sha256").update(content).digest("hex");
|
|
216
|
-
context.readFileState.set(resolvedPath, {
|
|
217
|
-
mtime: newStats.mtime.getTime(),
|
|
218
|
-
hash,
|
|
219
|
-
offset: undefined,
|
|
220
|
-
limit: undefined,
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const shortResult = isExistingFile ? "File overwritten" : "File created";
|
|
225
|
-
|
|
226
|
-
const lines = content.split("\n").length;
|
|
227
|
-
const chars = content.length;
|
|
228
|
-
const detailedContent = `${shortResult} (${lines} lines, ${chars} characters)`;
|
|
229
|
-
|
|
230
|
-
logger.debug(`Write tool: ${shortResult}`);
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
success: true,
|
|
234
|
-
content: detailedContent,
|
|
235
|
-
shortResult,
|
|
236
|
-
filePath: resolvedPath,
|
|
237
|
-
startLineNumber: 1,
|
|
238
|
-
};
|
|
239
|
-
} catch (error) {
|
|
240
|
-
const errorMessage =
|
|
241
|
-
error instanceof Error ? error.message : String(error);
|
|
242
|
-
logger.error(`Write tool error: ${errorMessage}`);
|
|
243
|
-
return {
|
|
244
|
-
success: false,
|
|
245
|
-
content: "",
|
|
246
|
-
error: errorMessage,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
},
|
|
250
|
-
formatCompactParams: (
|
|
251
|
-
params: Record<string, unknown>,
|
|
252
|
-
context: ToolContext,
|
|
253
|
-
) => {
|
|
254
|
-
const filePath = params.file_path as string;
|
|
255
|
-
const content = params.content as string;
|
|
256
|
-
|
|
257
|
-
let displayPath = getDisplayPath(filePath || "", context.workdir);
|
|
258
|
-
|
|
259
|
-
if (content) {
|
|
260
|
-
const lines = content.split("\n").length;
|
|
261
|
-
const chars = content.length;
|
|
262
|
-
displayPath += ` ${lines} lines, ${chars} chars`;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return displayPath;
|
|
266
|
-
},
|
|
267
|
-
};
|
package/src/types/agent.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import type { ClientOptions } from "openai";
|
|
2
|
-
import type { QueuedMessage } from "../managers/messageQueue.js";
|
|
3
|
-
import type {
|
|
4
|
-
Message,
|
|
5
|
-
Logger,
|
|
6
|
-
PermissionMode,
|
|
7
|
-
PermissionCallback,
|
|
8
|
-
ILspManager,
|
|
9
|
-
PluginConfig,
|
|
10
|
-
BackgroundTask,
|
|
11
|
-
McpServerConfig,
|
|
12
|
-
} from "./index.js";
|
|
13
|
-
import type { MessageManagerCallbacks } from "../managers/messageManager.js";
|
|
14
|
-
import type { BackgroundTaskManagerCallbacks } from "../managers/backgroundTaskManager.js";
|
|
15
|
-
import type { McpManagerCallbacks } from "../managers/mcpManager.js";
|
|
16
|
-
import type { SubagentManagerCallbacks } from "../managers/subagentManager.js";
|
|
17
|
-
import type { PartialHookConfiguration } from "./configuration.js";
|
|
18
|
-
import type { ToolPlugin } from "../tools/types.js";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Configuration options for Agent instances
|
|
22
|
-
*
|
|
23
|
-
* IMPORTANT: This interface is used by both Agent constructor and Agent.create()
|
|
24
|
-
* Any changes to this interface must be compatible with both methods.
|
|
25
|
-
*/
|
|
26
|
-
export interface AgentOptions {
|
|
27
|
-
// Optional configuration with environment fallbacks
|
|
28
|
-
apiKey?: string;
|
|
29
|
-
baseURL?: string;
|
|
30
|
-
/** Wave server URL for SSO authentication (fallback to WAVE_SERVER_URL env var) */
|
|
31
|
-
serverUrl?: string;
|
|
32
|
-
defaultHeaders?: Record<string, string>;
|
|
33
|
-
/** Per-subagent-type headers, merged into defaultHeaders for matching subagents */
|
|
34
|
-
subagentHeaders?: Record<string, Record<string, string>>;
|
|
35
|
-
fetchOptions?: ClientOptions["fetchOptions"];
|
|
36
|
-
fetch?: ClientOptions["fetch"];
|
|
37
|
-
model?: string;
|
|
38
|
-
fastModel?: string;
|
|
39
|
-
maxInputTokens?: number;
|
|
40
|
-
maxTokens?: number;
|
|
41
|
-
/** Preferred language for agent communication */
|
|
42
|
-
language?: string;
|
|
43
|
-
|
|
44
|
-
// Existing options (preserved)
|
|
45
|
-
callbacks?: AgentCallbacks;
|
|
46
|
-
restoreSessionId?: string;
|
|
47
|
-
continueLastSession?: boolean;
|
|
48
|
-
logger?: Logger;
|
|
49
|
-
/**Add optional initial messages parameter for testing convenience */
|
|
50
|
-
messages?: Message[];
|
|
51
|
-
/**Working directory - if not specified, use process.cwd() */
|
|
52
|
-
workdir?: string;
|
|
53
|
-
/**Optional custom system prompt - if provided, replaces default system prompt */
|
|
54
|
-
systemPrompt?: string;
|
|
55
|
-
/**Permission mode - defaults to "default" */
|
|
56
|
-
permissionMode?: PermissionMode;
|
|
57
|
-
/**Custom permission callback */
|
|
58
|
-
canUseTool?: PermissionCallback;
|
|
59
|
-
/**Whether to use streaming mode for AI responses - defaults to true */
|
|
60
|
-
stream?: boolean;
|
|
61
|
-
/**Optional custom LSP manager - if not provided, a standalone one will be created */
|
|
62
|
-
lspManager?: ILspManager;
|
|
63
|
-
/**Optional local plugins to load */
|
|
64
|
-
plugins?: PluginConfig[];
|
|
65
|
-
/**
|
|
66
|
-
* Optional list of tool names to enable.
|
|
67
|
-
* - undefined: Enable all built-in tools and plugins (default).
|
|
68
|
-
* - []: Disable all tools.
|
|
69
|
-
* - string[]: Enable only the tools with the specified names.
|
|
70
|
-
*/
|
|
71
|
-
tools?: string[];
|
|
72
|
-
/**Optional worktree name */
|
|
73
|
-
worktreeName?: string;
|
|
74
|
-
/**Whether this is a newly created worktree */
|
|
75
|
-
isNewWorktree?: boolean;
|
|
76
|
-
/**Whether to watch for skill changes - defaults to true */
|
|
77
|
-
watchSkills?: boolean;
|
|
78
|
-
/**
|
|
79
|
-
* Optional list of tool names to always allow.
|
|
80
|
-
* These rules follow the standard permission rule syntax: `ToolName` or `ToolName(pattern)`.
|
|
81
|
-
*/
|
|
82
|
-
allowedTools?: string[];
|
|
83
|
-
/**
|
|
84
|
-
* Optional list of tool names to always disallow.
|
|
85
|
-
* These rules follow the standard permission rule syntax: `ToolName` or `ToolName(pattern)`.
|
|
86
|
-
*/
|
|
87
|
-
disallowedTools?: string[];
|
|
88
|
-
/**
|
|
89
|
-
* Optional list of additional directories to include in the Safe Zone
|
|
90
|
-
* for this agent session. These directories are listed in the system
|
|
91
|
-
* prompt and treated as safe for file operations (e.g. in acceptEdits mode).
|
|
92
|
-
*/
|
|
93
|
-
additionalDirectories?: string[];
|
|
94
|
-
/**
|
|
95
|
-
* Optional MCP server configs to connect at startup.
|
|
96
|
-
* Overrides .mcp.json for specified server names.
|
|
97
|
-
*/
|
|
98
|
-
mcpServers?: Record<string, McpServerConfig>;
|
|
99
|
-
/** Custom tools provided by the SDK user, registered alongside built-in tools */
|
|
100
|
-
customTools?: ToolPlugin[];
|
|
101
|
-
/**
|
|
102
|
-
* Optional hook configuration to inject at creation time.
|
|
103
|
-
* File-based hooks (from config.json/.waverc.json) merge on top of these.
|
|
104
|
-
*/
|
|
105
|
-
hooks?: PartialHookConfiguration;
|
|
106
|
-
[key: string]: unknown;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export interface AgentCallbacks
|
|
110
|
-
extends MessageManagerCallbacks,
|
|
111
|
-
BackgroundTaskManagerCallbacks,
|
|
112
|
-
McpManagerCallbacks,
|
|
113
|
-
SubagentManagerCallbacks {
|
|
114
|
-
onBackgroundTasksChange?: (tasks: BackgroundTask[]) => void;
|
|
115
|
-
onTasksChange?: (tasks: import("./tasks.js").Task[]) => void;
|
|
116
|
-
onPermissionModeChange?: (mode: PermissionMode) => void;
|
|
117
|
-
onSubagentLatestTotalTokensChange?: (
|
|
118
|
-
subagentId: string,
|
|
119
|
-
tokens: number,
|
|
120
|
-
) => void;
|
|
121
|
-
onBackgroundCurrentTask?: () => void;
|
|
122
|
-
onModelChange?: (model: string) => void;
|
|
123
|
-
onConfiguredModelsChange?: (models: string[]) => void;
|
|
124
|
-
onLoadingChange?: (loading: boolean) => void;
|
|
125
|
-
onCommandRunningChange?: (running: boolean) => void;
|
|
126
|
-
onWorkdirChange?: (newCwd: string) => void;
|
|
127
|
-
onQueuedMessagesChange?: (messages: QueuedMessage[]) => void;
|
|
128
|
-
}
|
package/src/types/auth.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export interface AuthUser {
|
|
2
|
-
id: string;
|
|
3
|
-
email?: string;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export interface AuthConfig {
|
|
7
|
-
SSO_TOKEN?: string;
|
|
8
|
-
SSO_REFRESH_TOKEN?: string;
|
|
9
|
-
SSO_TOKEN_EXPIRES_AT?: number; // Unix timestamp (ms) when SSO_TOKEN expires
|
|
10
|
-
user?: AuthUser;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** Server response from POST /api/auth/token */
|
|
14
|
-
export interface TokenResponse {
|
|
15
|
-
token: string;
|
|
16
|
-
refreshToken?: string;
|
|
17
|
-
expiresIn?: number; // seconds until token expires
|
|
18
|
-
user: { id: string; email?: string };
|
|
19
|
-
}
|
package/src/types/commands.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Slash command and custom command types
|
|
3
|
-
* Dependencies: None
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export interface SlashCommand {
|
|
7
|
-
id: string;
|
|
8
|
-
name: string;
|
|
9
|
-
description: string;
|
|
10
|
-
handler: (args?: string, signal?: AbortSignal) => Promise<void> | void;
|
|
11
|
-
/** Whether this command should bypass the message queue when AI is busy.
|
|
12
|
-
* - `true`: always immediate
|
|
13
|
-
* - Function: receives args, returns true for immediate variants */
|
|
14
|
-
immediate?: boolean | ((args?: string) => boolean);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface CustomSlashCommandConfig {
|
|
18
|
-
model?: string;
|
|
19
|
-
description?: string;
|
|
20
|
-
allowedTools?: string[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface CustomSlashCommand {
|
|
24
|
-
id: string;
|
|
25
|
-
name: string;
|
|
26
|
-
description?: string; // Add description field
|
|
27
|
-
filePath: string;
|
|
28
|
-
content: string;
|
|
29
|
-
config?: CustomSlashCommandConfig;
|
|
30
|
-
}
|