wave-agent-sdk 1.2.0 → 1.3.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/dist/agent.d.ts +58 -4
- package/dist/agent.js +91 -19
- package/dist/builtin/index.js +2 -0
- package/dist/builtin/skills/settings.js +1 -12
- package/dist/builtin/skills/wave-daemon.d.ts +1 -0
- package/dist/builtin/skills/wave-daemon.js +194 -0
- package/dist/constants/images.d.ts +26 -0
- package/dist/constants/images.js +26 -0
- package/dist/constants/index.d.ts +16 -0
- package/dist/constants/index.js +16 -0
- package/dist/constants/memory.d.ts +26 -0
- package/dist/constants/memory.js +34 -0
- package/dist/constants/messages.d.ts +11 -0
- package/dist/constants/messages.js +11 -0
- package/dist/constants/plugins.d.ts +8 -0
- package/dist/constants/plugins.js +8 -0
- package/dist/constants/tools.d.ts +1 -0
- package/dist/constants/tools.js +1 -0
- package/dist/core/plugin.d.ts +53 -13
- package/dist/core/plugin.js +134 -26
- package/dist/core/session.d.ts +1 -1
- package/dist/core/session.js +1 -1
- package/dist/exec/catalog.d.ts +140 -0
- package/dist/exec/catalog.js +470 -0
- package/dist/exec/catalogAnnouncement.d.ts +89 -0
- package/dist/exec/catalogAnnouncement.js +293 -0
- package/dist/exec/constants.d.ts +51 -0
- package/dist/exec/constants.js +51 -0
- package/dist/exec/execRuntime.d.ts +55 -0
- package/dist/exec/execRuntime.js +217 -0
- package/dist/exec/workerSource.d.ts +28 -0
- package/dist/exec/workerSource.js +299 -0
- package/dist/host/index.d.ts +23 -0
- package/dist/host/index.js +23 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -0
- package/dist/managers/MemoryRuleManager.d.ts +6 -0
- package/dist/managers/MemoryRuleManager.js +12 -0
- package/dist/managers/aiManager.d.ts +35 -1
- package/dist/managers/aiManager.js +190 -21
- package/dist/managers/backgroundTaskManager.js +14 -0
- package/dist/managers/hookManager.d.ts +13 -0
- package/dist/managers/hookManager.js +31 -4
- package/dist/managers/liveConfigManager.d.ts +33 -0
- package/dist/managers/liveConfigManager.js +103 -8
- package/dist/managers/lspManager.d.ts +9 -0
- package/dist/managers/lspManager.js +47 -18
- package/dist/managers/mcpManager.d.ts +45 -10
- package/dist/managers/mcpManager.js +103 -1
- package/dist/managers/messageManager.d.ts +48 -5
- package/dist/managers/messageManager.js +107 -21
- package/dist/managers/permissionManager.d.ts +40 -0
- package/dist/managers/permissionManager.js +63 -8
- package/dist/managers/pluginManager.d.ts +46 -2
- package/dist/managers/pluginManager.js +117 -11
- package/dist/managers/pluginScopeManager.d.ts +15 -2
- package/dist/managers/pluginScopeManager.js +20 -1
- package/dist/managers/skillManager.d.ts +19 -0
- package/dist/managers/skillManager.js +44 -0
- package/dist/managers/slashCommandManager.d.ts +10 -0
- package/dist/managers/slashCommandManager.js +35 -3
- package/dist/managers/subagentManager.d.ts +8 -0
- package/dist/managers/subagentManager.js +20 -0
- package/dist/managers/toolManager.d.ts +29 -3
- package/dist/managers/toolManager.js +87 -13
- package/dist/prompts/autoMemory.d.ts +9 -0
- package/dist/prompts/autoMemory.js +30 -31
- package/dist/prompts/autoMemoryExtraction.d.ts +4 -0
- package/dist/prompts/autoMemoryExtraction.js +8 -111
- package/dist/prompts/memoryTypes.d.ts +63 -0
- package/dist/prompts/memoryTypes.js +191 -0
- package/dist/services/GitService.d.ts +7 -0
- package/dist/services/GitService.js +23 -0
- package/dist/services/MarketplaceService.d.ts +101 -17
- package/dist/services/MarketplaceService.js +318 -119
- package/dist/services/artifactContent.d.ts +84 -0
- package/dist/services/artifactContent.js +204 -0
- package/dist/services/artifactSession.d.ts +6 -0
- package/dist/services/artifactSession.js +17 -0
- package/dist/services/autoMemoryService.js +5 -13
- package/dist/services/configurationService.d.ts +60 -9
- package/dist/services/configurationService.js +129 -54
- package/dist/services/contentSummarizer.d.ts +15 -0
- package/dist/services/contentSummarizer.js +45 -0
- package/dist/services/execAvailability.d.ts +9 -0
- package/dist/services/execAvailability.js +32 -0
- package/dist/services/fileWatcher.js +61 -6
- package/dist/services/initializationService.js +19 -15
- package/dist/services/interactionService.d.ts +9 -1
- package/dist/services/interactionService.js +28 -8
- package/dist/services/jsonlHandler.d.ts +84 -0
- package/dist/services/jsonlHandler.js +209 -14
- package/dist/services/memory.d.ts +3 -1
- package/dist/services/memory.js +13 -9
- package/dist/services/officialMarketplaceMirror.js +3 -2
- package/dist/services/pluginLoader.d.ts +12 -4
- package/dist/services/pluginLoader.js +38 -7
- package/dist/services/remoteSettingsService.js +16 -2
- package/dist/services/session.d.ts +74 -0
- package/dist/services/session.js +144 -3
- package/dist/services/sessionEntries.d.ts +2 -0
- package/dist/services/sessionEntries.js +20 -0
- package/dist/stdio/index.d.ts +3 -1
- package/dist/stdio/index.js +3 -1
- package/dist/stdio/notificationRouter.js +1 -0
- package/dist/stdio/stdioAgent.d.ts +14 -7
- package/dist/stdio/stdioAgent.js +19 -0
- package/dist/tools/artifactTool.js +406 -273
- package/dist/tools/bashTool.js +8 -6
- package/dist/tools/editTool.js +6 -3
- package/dist/tools/execTool.d.ts +2 -0
- package/dist/tools/execTool.js +165 -0
- package/dist/tools/grepTool.js +7 -1
- package/dist/tools/readTool.js +30 -2
- package/dist/tools/types.d.ts +34 -8
- package/dist/tools/webFetchTool.js +15 -166
- package/dist/tools/workflowTool.js +40 -8
- package/dist/tools/writeTool.js +6 -3
- package/dist/types/agent.d.ts +20 -5
- package/dist/types/configuration.d.ts +39 -1
- package/dist/types/marketplace.d.ts +40 -2
- package/dist/types/mcp.d.ts +39 -0
- package/dist/types/permissions.d.ts +22 -0
- package/dist/types/permissions.js +17 -0
- package/dist/types/plugins.d.ts +26 -2
- package/dist/types/skills.d.ts +15 -0
- package/dist/utils/constants.d.ts +10 -0
- package/dist/utils/constants.js +10 -0
- package/dist/utils/containerSetup.js +43 -0
- package/dist/utils/convertMessagesForAPI.d.ts +7 -1
- package/dist/utils/convertMessagesForAPI.js +64 -14
- package/dist/utils/fileChangeReminder.d.ts +20 -0
- package/dist/utils/fileChangeReminder.js +153 -0
- package/dist/utils/fileSearch.js +4 -3
- package/dist/utils/fileUtils.d.ts +33 -0
- package/dist/utils/fileUtils.js +81 -0
- package/dist/utils/frontmatterYaml.d.ts +33 -0
- package/dist/utils/frontmatterYaml.js +192 -0
- package/dist/utils/imageBudget.d.ts +85 -0
- package/dist/utils/imageBudget.js +109 -0
- package/dist/utils/imageDimensions.d.ts +83 -0
- package/dist/utils/imageDimensions.js +232 -0
- package/dist/utils/imageProcessor.d.ts +66 -0
- package/dist/utils/imageProcessor.js +84 -0
- package/dist/utils/imageRewrite.d.ts +29 -0
- package/dist/utils/imageRewrite.js +251 -0
- package/dist/utils/markdownParser.d.ts +5 -1
- package/dist/utils/markdownParser.js +9 -51
- package/dist/utils/mcpInstructions.d.ts +61 -0
- package/dist/utils/mcpInstructions.js +126 -0
- package/dist/utils/mcpUtils.d.ts +7 -0
- package/dist/utils/mcpUtils.js +11 -2
- package/dist/utils/memoryAge.d.ts +32 -0
- package/dist/utils/memoryAge.js +47 -0
- package/dist/utils/memoryEntrypoint.d.ts +20 -0
- package/dist/utils/memoryEntrypoint.js +49 -0
- package/dist/utils/memoryIndex.d.ts +30 -0
- package/dist/utils/memoryIndex.js +76 -0
- package/dist/utils/messageOperations.d.ts +6 -2
- package/dist/utils/messageOperations.js +40 -29
- package/dist/utils/nestedMemory.d.ts +22 -0
- package/dist/utils/nestedMemory.js +61 -0
- package/dist/utils/npmTarball.d.ts +19 -0
- package/dist/utils/npmTarball.js +92 -0
- package/dist/utils/pluginSource.d.ts +37 -0
- package/dist/utils/pluginSource.js +73 -0
- package/dist/utils/ripgrep.d.ts +18 -4
- package/dist/utils/ripgrep.js +56 -4
- package/dist/utils/runtimeDeps.d.ts +35 -0
- package/dist/utils/runtimeDeps.js +426 -0
- package/dist/utils/skillParser.js +22 -52
- package/dist/utils/subagentParser.js +39 -43
- package/dist/utils/userSettings.d.ts +90 -0
- package/dist/utils/userSettings.js +291 -0
- package/package.json +10 -7
package/dist/agent.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type QueuedMessage } from "./managers/messageQueue.js";
|
|
2
2
|
import { SlashCommand, CustomSlashCommand, AgentOptions } from "./types/index.js";
|
|
3
|
-
import type { Message, McpServerStatus, GatewayConfig, ModelConfig, Usage, PermissionMode, ForegroundTask, SkillMetadata } from "./types/index.js";
|
|
3
|
+
import type { Message, McpServerStatus, GatewayConfig, ModelConfig, Usage, PermissionMode, ForegroundTask, SkillMetadata, PluginReloadResult } from "./types/index.js";
|
|
4
4
|
import type { HookEvent, HookEventConfig } from "./types/hooks.js";
|
|
5
5
|
import type { WorktreeSession } from "./utils/worktreeSession.js";
|
|
6
6
|
export declare class Agent {
|
|
@@ -43,12 +43,39 @@ export declare class Agent {
|
|
|
43
43
|
getGatewayConfig(): GatewayConfig;
|
|
44
44
|
getModelConfig(): ModelConfig;
|
|
45
45
|
getMaxInputTokens(): number;
|
|
46
|
-
getLanguage(): string
|
|
46
|
+
getLanguage(): string;
|
|
47
|
+
/**
|
|
48
|
+
* Re-read the merged configuration (user/project/local settings.json + remote
|
|
49
|
+
* managed settings) and apply it to this session **without rebuilding it**:
|
|
50
|
+
* hooks / permissions / env follow immediately, the settings.json-derived
|
|
51
|
+
* values the running turn reads are re-pinned at the next turn start.
|
|
52
|
+
*
|
|
53
|
+
* THE save path calls this explicitly after writing the user-level
|
|
54
|
+
* settings.json: that file is commonly *created* by that very write, so the
|
|
55
|
+
* file watcher — armed at session start on a path that did not exist yet —
|
|
56
|
+
* must not be the only trigger (docs/specs/core/agent-config.md
|
|
57
|
+
* 「设置实时重载」). Idempotent with the watcher (a concurrent reload is
|
|
58
|
+
* de-duplicated).
|
|
59
|
+
*/
|
|
60
|
+
reloadConfiguration(): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Re-read every plugin from disk and swap its contribution into this running
|
|
63
|
+
* session in place — the `/reload-plugins` command. Commands, skills,
|
|
64
|
+
* agents, hooks, MCP servers and LSP servers all follow; the session itself
|
|
65
|
+
* (sessionId, transcript, in-flight work) is untouched, so this is not a
|
|
66
|
+
* rebuild. Every live session in the process must call this to see the new
|
|
67
|
+
* state; sessions created afterwards pick it up from disk on their own.
|
|
68
|
+
*
|
|
69
|
+
* Busts the prompt cache for the tool-schema prefix (the skill and agent
|
|
70
|
+
* lists are inlined) — deliberately accepted, see
|
|
71
|
+
* docs/specs/ecosystem/plugin.md「插件变更的就地重载」.
|
|
72
|
+
*/
|
|
73
|
+
reloadPlugins(): Promise<PluginReloadResult>;
|
|
47
74
|
/**
|
|
48
75
|
* Set the active model for the agent session
|
|
49
76
|
* @param model - The ID of the model to use
|
|
50
77
|
*/
|
|
51
|
-
setModel(model: string): void
|
|
78
|
+
setModel(model: string): Promise<void>;
|
|
52
79
|
/**
|
|
53
80
|
* Get all configured models from settings.json and defaults
|
|
54
81
|
* @returns Array of model IDs
|
|
@@ -246,8 +273,35 @@ export declare class Agent {
|
|
|
246
273
|
/**
|
|
247
274
|
* Restore a session by ID, switching to the target session without destroying the Agent instance
|
|
248
275
|
* @param sessionId - The ID of the session to restore
|
|
276
|
+
* @param options.workdir - The directory the target session lives in. Pass it
|
|
277
|
+
* when the target was recorded under a different project directory (e.g.
|
|
278
|
+
* another worktree of the same repo); the session transcript, project rules
|
|
279
|
+
* and memory are then re-pointed at it.
|
|
280
|
+
*/
|
|
281
|
+
restoreSession(sessionId: string, options?: {
|
|
282
|
+
workdir?: string;
|
|
283
|
+
}): Promise<void>;
|
|
284
|
+
/**
|
|
285
|
+
* Set this session's user-visible title.
|
|
286
|
+
*
|
|
287
|
+
* The title is appended to the session's own JSONL file as a `custom-title`
|
|
288
|
+
* entry (see `setSessionCustomTitle`), so every host that reads session files
|
|
289
|
+
* — this process, a later run, the IDE plugins, the desktop app — shows the
|
|
290
|
+
* same label. A blank title is a no-op.
|
|
291
|
+
*/
|
|
292
|
+
renameSession(title: string): Promise<void>;
|
|
293
|
+
/** Shared wiring for the InteractionService entry points. */
|
|
294
|
+
private interactionContext;
|
|
295
|
+
/**
|
|
296
|
+
* Re-point every directory-derived piece of session state at `workdir`
|
|
297
|
+
* (used when restoring a session recorded in another directory).
|
|
298
|
+
*
|
|
299
|
+
* The memory cache needs no explicit clearing: moving to a session always
|
|
300
|
+
* changes the session id, and the resulting `onSessionIdChange` already
|
|
301
|
+
* clears it. Project rules do — they are discovered once at startup and
|
|
302
|
+
* resolved relative to the workdir.
|
|
249
303
|
*/
|
|
250
|
-
|
|
304
|
+
private switchSessionWorkdir;
|
|
251
305
|
abortAIMessage(): void;
|
|
252
306
|
/** Execute a bash-mode command (`!command`) */
|
|
253
307
|
bang(command: string): Promise<void>;
|
package/dist/agent.js
CHANGED
|
@@ -3,8 +3,9 @@ import { configValidator } from "./utils/configValidator.js";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { parseTaskNotificationXml } from "./utils/notificationXml.js";
|
|
5
5
|
import { InitializationService } from "./services/initializationService.js";
|
|
6
|
-
import { InteractionService } from "./services/interactionService.js";
|
|
6
|
+
import { InteractionService, } from "./services/interactionService.js";
|
|
7
7
|
import { ConfigurationService } from "./services/configurationService.js";
|
|
8
|
+
import { setSessionCustomTitle } from "./services/session.js";
|
|
8
9
|
import { setupAgentContainer } from "./utils/containerSetup.js";
|
|
9
10
|
import { initializeTelemetry, shutdownTelemetry, } from "./telemetry/instrumentation.js";
|
|
10
11
|
import { logOTelEvent } from "./telemetry/events.js";
|
|
@@ -26,12 +27,47 @@ export class Agent {
|
|
|
26
27
|
getLanguage() {
|
|
27
28
|
return this.configurationService.resolveLanguage();
|
|
28
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Re-read the merged configuration (user/project/local settings.json + remote
|
|
32
|
+
* managed settings) and apply it to this session **without rebuilding it**:
|
|
33
|
+
* hooks / permissions / env follow immediately, the settings.json-derived
|
|
34
|
+
* values the running turn reads are re-pinned at the next turn start.
|
|
35
|
+
*
|
|
36
|
+
* THE save path calls this explicitly after writing the user-level
|
|
37
|
+
* settings.json: that file is commonly *created* by that very write, so the
|
|
38
|
+
* file watcher — armed at session start on a path that did not exist yet —
|
|
39
|
+
* must not be the only trigger (docs/specs/core/agent-config.md
|
|
40
|
+
* 「设置实时重载」). Idempotent with the watcher (a concurrent reload is
|
|
41
|
+
* de-duplicated).
|
|
42
|
+
*/
|
|
43
|
+
async reloadConfiguration() {
|
|
44
|
+
await this.liveConfigManager.reload();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Re-read every plugin from disk and swap its contribution into this running
|
|
48
|
+
* session in place — the `/reload-plugins` command. Commands, skills,
|
|
49
|
+
* agents, hooks, MCP servers and LSP servers all follow; the session itself
|
|
50
|
+
* (sessionId, transcript, in-flight work) is untouched, so this is not a
|
|
51
|
+
* rebuild. Every live session in the process must call this to see the new
|
|
52
|
+
* state; sessions created afterwards pick it up from disk on their own.
|
|
53
|
+
*
|
|
54
|
+
* Busts the prompt cache for the tool-schema prefix (the skill and agent
|
|
55
|
+
* lists are inlined) — deliberately accepted, see
|
|
56
|
+
* docs/specs/ecosystem/plugin.md「插件变更的就地重载」.
|
|
57
|
+
*/
|
|
58
|
+
async reloadPlugins() {
|
|
59
|
+
const result = await this.pluginManager.reloadAllPlugins();
|
|
60
|
+
// Plugin skills arrive after SkillManager.initialize(), so their slash
|
|
61
|
+
// commands have to be re-derived explicitly (same as at startup).
|
|
62
|
+
this.slashCommandManager.registerSkillCommands(this.skillManager.getAvailableSkills());
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
29
65
|
/**
|
|
30
66
|
* Set the active model for the agent session
|
|
31
67
|
* @param model - The ID of the model to use
|
|
32
68
|
*/
|
|
33
|
-
setModel(model) {
|
|
34
|
-
this.configurationService.setModel(model);
|
|
69
|
+
async setModel(model) {
|
|
70
|
+
await this.configurationService.setModel(model);
|
|
35
71
|
this.options.callbacks?.onModelChange?.(model);
|
|
36
72
|
}
|
|
37
73
|
/**
|
|
@@ -503,6 +539,11 @@ export class Agent {
|
|
|
503
539
|
taskManager: this.taskManager,
|
|
504
540
|
resolveAndValidateConfig: () => this.resolveAndValidateConfig(),
|
|
505
541
|
}, options);
|
|
542
|
+
// Freeze the session-level bypass authorization now that settings.json (and
|
|
543
|
+
// thus `permissions.defaultMode`) has been loaded. Later mode switches and
|
|
544
|
+
// config reloads must not change it — it records what the user authorized
|
|
545
|
+
// when the session started.
|
|
546
|
+
this.permissionManager.captureBypassAuthorization();
|
|
506
547
|
// Initialize OpenTelemetry (awaited to ensure ALS context is ready before
|
|
507
548
|
// startInteractionSpan is called, preventing trace context fragmentation)
|
|
508
549
|
const telemetryConfig = this.configurationService.resolveTelemetryConfig();
|
|
@@ -530,9 +571,33 @@ export class Agent {
|
|
|
530
571
|
/**
|
|
531
572
|
* Restore a session by ID, switching to the target session without destroying the Agent instance
|
|
532
573
|
* @param sessionId - The ID of the session to restore
|
|
574
|
+
* @param options.workdir - The directory the target session lives in. Pass it
|
|
575
|
+
* when the target was recorded under a different project directory (e.g.
|
|
576
|
+
* another worktree of the same repo); the session transcript, project rules
|
|
577
|
+
* and memory are then re-pointed at it.
|
|
578
|
+
*/
|
|
579
|
+
async restoreSession(sessionId, options) {
|
|
580
|
+
await InteractionService.restoreSession(this.interactionContext(), sessionId, options);
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Set this session's user-visible title.
|
|
584
|
+
*
|
|
585
|
+
* The title is appended to the session's own JSONL file as a `custom-title`
|
|
586
|
+
* entry (see `setSessionCustomTitle`), so every host that reads session files
|
|
587
|
+
* — this process, a later run, the IDE plugins, the desktop app — shows the
|
|
588
|
+
* same label. A blank title is a no-op.
|
|
533
589
|
*/
|
|
534
|
-
async
|
|
535
|
-
await
|
|
590
|
+
async renameSession(title) {
|
|
591
|
+
const applied = await setSessionCustomTitle(this.sessionId, this.workdir, title);
|
|
592
|
+
// Keep the in-memory value in step: it is what gets written back at the
|
|
593
|
+
// next flush point if the entry has since slid out of the tail window.
|
|
594
|
+
if (applied) {
|
|
595
|
+
this.messageManager.setCustomTitle(applied);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
/** Shared wiring for the InteractionService entry points. */
|
|
599
|
+
interactionContext() {
|
|
600
|
+
return {
|
|
536
601
|
messageManager: this.messageManager,
|
|
537
602
|
slashCommandManager: this.slashCommandManager,
|
|
538
603
|
hookManager: this.hookManager,
|
|
@@ -544,7 +609,22 @@ export class Agent {
|
|
|
544
609
|
taskManager: this.taskManager,
|
|
545
610
|
options: this.options,
|
|
546
611
|
abortMessage: () => this.abortMessage(),
|
|
547
|
-
|
|
612
|
+
switchWorkdir: (workdir) => this.switchSessionWorkdir(workdir),
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Re-point every directory-derived piece of session state at `workdir`
|
|
617
|
+
* (used when restoring a session recorded in another directory).
|
|
618
|
+
*
|
|
619
|
+
* The memory cache needs no explicit clearing: moving to a session always
|
|
620
|
+
* changes the session id, and the resulting `onSessionIdChange` already
|
|
621
|
+
* clears it. Project rules do — they are discovered once at startup and
|
|
622
|
+
* resolved relative to the workdir.
|
|
623
|
+
*/
|
|
624
|
+
async switchSessionWorkdir(workdir) {
|
|
625
|
+
this.setWorkdir(workdir);
|
|
626
|
+
this.messageManager.setWorkdir(workdir);
|
|
627
|
+
await this.memoryRuleManager.setWorkdir(workdir);
|
|
548
628
|
}
|
|
549
629
|
abortAIMessage() {
|
|
550
630
|
this.aiManager.abortAIMessage();
|
|
@@ -711,6 +791,10 @@ export class Agent {
|
|
|
711
791
|
this.logger?.warn(`SessionEnd hooks failed: ${error.message}`);
|
|
712
792
|
}
|
|
713
793
|
await this.messageManager.saveSession();
|
|
794
|
+
// Flush point: the transcript is about to be left behind for other
|
|
795
|
+
// processes to list, so re-append the metadata entries at EOF. Must come
|
|
796
|
+
// after saveSession (which returns early when nothing is unsaved).
|
|
797
|
+
await this.messageManager.reAppendSessionMetadata();
|
|
714
798
|
this.abortAIMessage(); // This will abort tools including Agent tool (subagents)
|
|
715
799
|
this.abortBashCommand();
|
|
716
800
|
this.abortSlashCommand();
|
|
@@ -832,19 +916,7 @@ export class Agent {
|
|
|
832
916
|
}
|
|
833
917
|
// Immediate slash command: fall through to InteractionService
|
|
834
918
|
}
|
|
835
|
-
await InteractionService.sendMessage(
|
|
836
|
-
messageManager: this.messageManager,
|
|
837
|
-
slashCommandManager: this.slashCommandManager,
|
|
838
|
-
hookManager: this.hookManager,
|
|
839
|
-
workdir: this.workdir,
|
|
840
|
-
configurationService: this.configurationService,
|
|
841
|
-
logger: this.logger,
|
|
842
|
-
aiManager: this.aiManager,
|
|
843
|
-
subagentManager: this.subagentManager,
|
|
844
|
-
taskManager: this.taskManager,
|
|
845
|
-
options: this.options,
|
|
846
|
-
abortMessage: () => this.abortMessage(),
|
|
847
|
-
}, content, images);
|
|
919
|
+
await InteractionService.sendMessage(this.interactionContext(), content, images);
|
|
848
920
|
}
|
|
849
921
|
// ========== Additional Directories ==========
|
|
850
922
|
/**
|
package/dist/builtin/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { initSkill } from "./skills/init.js";
|
|
|
5
5
|
import { loopSkill } from "./skills/loop.js";
|
|
6
6
|
import { simplifySkill } from "./skills/simplify.js";
|
|
7
7
|
import { settingsSkills } from "./skills/settings.js";
|
|
8
|
+
import { waveDaemonSkill } from "./skills/wave-daemon.js";
|
|
8
9
|
import { subagents } from "./subagents.js";
|
|
9
10
|
import { sddPlugin } from "./plugins.js";
|
|
10
11
|
export const BUILTIN_CONTENT = {
|
|
@@ -15,6 +16,7 @@ export const BUILTIN_CONTENT = {
|
|
|
15
16
|
...loopSkill,
|
|
16
17
|
...simplifySkill,
|
|
17
18
|
...settingsSkills,
|
|
19
|
+
...waveDaemonSkill,
|
|
18
20
|
...subagents,
|
|
19
21
|
...sddPlugin,
|
|
20
22
|
};
|
|
@@ -924,18 +924,7 @@ Wave clones the marketplace repo, reads the manifest, and copies the plugin to i
|
|
|
924
924
|
|
|
925
925
|
### Updating Plugins
|
|
926
926
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
\`\`\`json
|
|
930
|
-
{
|
|
931
|
-
"marketplaces": {
|
|
932
|
-
"my-plugins": {
|
|
933
|
-
"source": { "source": "github", "repo": "user/my-plugins" },
|
|
934
|
-
"autoUpdate": true
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
\`\`\`
|
|
927
|
+
Every registered marketplace is refreshed when you open a plugin marketplace surface — the settings page's plugin marketplace view, or the CLI plugin manager (\`/plugin\`). Each checkout is pulled (or cloned if missing). Refreshing a marketplace does **not** upgrade installed plugins, and there is no per-marketplace switch for it. Plugin upgrades only happen through an explicit action: updating a single plugin, or updating a whole marketplace (\`wave plugin marketplace update [name]\`, or the batch entry in either UI) to upgrade all of its plugins in batch.
|
|
939
928
|
|
|
940
929
|
### Marketplace Scopes
|
|
941
930
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const waveDaemonSkill: Record<string, string>;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { BASH_TOOL_NAME, READ_TOOL_NAME } from "../../constants/tools.js";
|
|
2
|
+
export const waveDaemonSkill = {
|
|
3
|
+
"skills/wave-daemon/SKILL.md": `---
|
|
4
|
+
name: wave-daemon
|
|
5
|
+
description: Delegate a development or research task to a background wave daemon session — session creation in an isolated worktree, dispatching messages, aborting and re-scoping, monitoring progress, approving permissions, answering AskUserQuestion, and tearing sessions down with destroy.
|
|
6
|
+
allowed-tools: ${BASH_TOOL_NAME}(wave daemon *), ${BASH_TOOL_NAME}(git *), ${READ_TOOL_NAME}
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Wave daemon delegation
|
|
10
|
+
|
|
11
|
+
Delegate development work (code changes, bug fixes, features) and research work (reading code and producing conclusions — which often turns into code changes) to a **wave daemon session** instead of doing the long task inline. A daemon session runs in the background with no UI window, keeps generating while no client is attached, and survives client disconnects.
|
|
12
|
+
|
|
13
|
+
The authoritative command reference is the Wave documentation, CLI → *Daemon client commands* (\`docs/cli.md\` in the Wave repo). This skill is the playbook: what to run, in what order, and the traps to avoid.
|
|
14
|
+
|
|
15
|
+
## 0. Mental model
|
|
16
|
+
|
|
17
|
+
- \`wave --daemon <socket>\` starts the daemon (server side). \`wave daemon <subcommand>\` is the client that talks to it. The two never interfere.
|
|
18
|
+
- Every client subcommand connects to the fixed socket \`~/.wave/daemon.sock\` (\`$HOME/.wave/daemon.sock\`); there is no \`--socket\` override, so you are always talking to this machine's daemon for the current user.
|
|
19
|
+
- The daemon is **resident**: once started it does not exit when idle. It goes away only when stopped (\`wave daemon stop\`), killed, restarted after a CLI upgrade, or the machine restarts. It is not supervised by pm2/systemd, so it does not come back by itself.
|
|
20
|
+
- Every subcommand except \`stop\` starts a daemon on demand when the socket is absent, then retries. "No daemon is running" is therefore never a problem to work around — just run the command.
|
|
21
|
+
- Sessions are persisted as transcripts under \`~/.wave/projects/<project>/<sessionId>.jsonl\`, so a session can be re-hosted from disk after the daemon restarts.
|
|
22
|
+
- All subcommands are non-interactive: results on stdout, diagnostics on stderr.
|
|
23
|
+
|
|
24
|
+
## 1. Daemon lifecycle
|
|
25
|
+
|
|
26
|
+
\`\`\`bash
|
|
27
|
+
wave daemon stop # graceful: every hosted session is destroyed (each saves its transcript), the socket is removed, the process exits; idempotent ("Daemon is not running" when none is up)
|
|
28
|
+
wave daemon restart # stop the old daemon (if any), then start a fresh one from the current CLI
|
|
29
|
+
\`\`\`
|
|
30
|
+
|
|
31
|
+
**After upgrading the CLI, restart the daemon** — it runs the code it was started with and will not pick up a new build on its own. This is the main reason \`restart\` exists.
|
|
32
|
+
|
|
33
|
+
One-off edge: \`stop\` / \`restart\` cannot stop a daemon that predates the \`shutdown\` RPC (i.e. one still running an older build). The wait times out — \`daemon did not exit within 10000ms\` — because the old process ignores the request and the socket stays up. Kill that process yourself, then run any client subcommand (e.g. \`wave daemon list\`) to start a daemon from the current CLI; \`restart\` works normally from then on.
|
|
34
|
+
|
|
35
|
+
## 2. Creating a session
|
|
36
|
+
|
|
37
|
+
\`\`\`bash
|
|
38
|
+
wave daemon create --worktree [name] --workdir <dir> --permission-mode bypassPermissions
|
|
39
|
+
\`\`\`
|
|
40
|
+
|
|
41
|
+
- Prints the new sessionId on the first line (scripts read it from there), plus a second line with the worktree path and branch when \`--worktree\` was used.
|
|
42
|
+
- \`--worktree\` creates the session in a fresh git worktree, so it never touches your main checkout. The name is optional — one is generated when omitted.
|
|
43
|
+
- \`--permission-mode\` defaults to \`bypassPermissions\` for daemon-created sessions, so a session created this way raises no approval prompts at all. Passing the flag explicitly is redundant but fine and self-documenting. Valid modes: \`default\`, \`bypassPermissions\`, \`acceptEdits\`, \`plan\`, \`dontAsk\`.
|
|
44
|
+
- \`--workdir\` defaults to the current directory.
|
|
45
|
+
|
|
46
|
+
## 3. Dispatching work and following up
|
|
47
|
+
|
|
48
|
+
\`\`\`bash
|
|
49
|
+
wave daemon send <sessionId> <message> # async dispatch (the default)
|
|
50
|
+
wave daemon send <sessionId> <message> --wait 600 # wait up to 600s and print the reply
|
|
51
|
+
\`\`\`
|
|
52
|
+
|
|
53
|
+
- The default is fire-and-forget: the command exits 0 (printing \`Sent message to session: <sessionId>\`) as soon as the message is **delivered**. On an idle session it lands in history and the turn starts; on a busy session it is queued and takes effect when the current turn finishes.
|
|
54
|
+
- \`--wait <seconds>\` blocks until the reply to *that* message arrives and prints only the assistant's final reply text. On timeout it exits non-zero; if the session is stuck on a pending approval it says so and points at \`respond\`.
|
|
55
|
+
- A \`send --wait\` failure of \`Message aborted before producing a reply\` means the turn was interrupted mid-generation — the message was delivered, it just never produced text.
|
|
56
|
+
|
|
57
|
+
**An interrupted send is not a lost message.** If the shell or tool running \`wave daemon send\` is interrupted or times out, the message may already have been delivered and be executing in the daemon. Verify with \`wave daemon status <sessionId>\` before resending — a duplicate send duplicates the work.
|
|
58
|
+
|
|
59
|
+
To block on the work instead of checking it, follow the send with \`wave daemon wait <sessionId> --from-busy\` — the flag covers the moment right after an async send where the session still reports idle (§5).
|
|
60
|
+
|
|
61
|
+
## 4. Changing your mind: abort first
|
|
62
|
+
|
|
63
|
+
A \`send\` to a *generating* session is queued, not applied immediately — the current turn runs to completion first. To correct a task or change its scope mid-flight:
|
|
64
|
+
|
|
65
|
+
\`\`\`bash
|
|
66
|
+
wave daemon abort <sessionId> # interrupt in-flight generation (subagents, bash commands and queued messages included)
|
|
67
|
+
wave daemon send <sessionId> <corrected task>
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
\`abort\` is idempotent and a no-op on an idle session, so it is safe to run without checking first. It does not clear completed history, and it does **not** touch the session's permission mode — the session stays live in the daemon's memory.
|
|
71
|
+
|
|
72
|
+
## 5. Monitoring
|
|
73
|
+
|
|
74
|
+
\`\`\`bash
|
|
75
|
+
wave daemon list # sessions currently live in the daemon's in-memory registry
|
|
76
|
+
wave daemon status <sessionId> # one session: status, pending approvals, and the last message
|
|
77
|
+
wave daemon wait <sessionId> # block until it settles, then print that same snapshot
|
|
78
|
+
wave daemon status <id> --lines 0 # status line only — no message text (single-shot snapshot)
|
|
79
|
+
wave daemon status <id> --lines 5 # widen the context window when the last message is not enough
|
|
80
|
+
\`\`\`
|
|
81
|
+
|
|
82
|
+
\`status\` reports one of three states — \`idle\`, \`generating\`, or \`waiting for approval\` (listed with the pending request ids). It is plain text; there is no \`--json\`.
|
|
83
|
+
|
|
84
|
+
\`--lines N\` prints the last N messages. **The default is 1** — the last message alone — because message text is never truncated, so a single long report is already tens of thousands of characters; the default has to stay bounded. N counts messages, not output lines.
|
|
85
|
+
|
|
86
|
+
- \`--lines 0\` prints no message text at all — just the header and the \`Status:\` line. Use it when a snapshot only needs the status (cheaper than pulling a report you will not read).
|
|
87
|
+
- text is whitespace-collapsed, and tool-only messages print nothing (they still count toward N);
|
|
88
|
+
- it is the last N **messages**, so behind a long tail of intermediate narration ("still investigating…") the final report can fall outside the window.
|
|
89
|
+
|
|
90
|
+
So the final report is what the default \`status <id>\` already gives you; raise \`--lines\` only when the last message is not the report you want.
|
|
91
|
+
|
|
92
|
+
**To wait for the report, use \`wave daemon wait <sessionId>\`** — do not build a polling loop. It attaches, blocks until your session settles, then prints exactly what \`status <id> --lines N\` prints, so \`msg=$(wave daemon wait <id>)\` captures the report itself. Your session's own \`loadingChange\` push is only the **wake-up**: the idle verdict is read from the daemon registry when it wakes, and the daemon broadcasts every session's notifications to every connection, so a concurrent session's push is ignored (it can neither end the wait nor count as the busy phase). Its exit code is the contract:
|
|
93
|
+
|
|
94
|
+
| exit | meaning |
|
|
95
|
+
| ---- | ------- |
|
|
96
|
+
| \`0\` | the session went idle — the snapshot on stdout is the final report |
|
|
97
|
+
| \`3\` | the session is hanging on a permission approval — the snapshot lists the pending request ids; answer them with \`respond\` (§6) and \`wait\` again |
|
|
98
|
+
| \`1\` | error — daemon unreachable, unknown sessionId, the session was destroyed while you waited, or \`--timeout\` elapsed |
|
|
99
|
+
|
|
100
|
+
- A session that is already idle when you call it returns \`0\` immediately (it never hangs).
|
|
101
|
+
- **\`--from-busy\` for the send-then-wait race.** An async \`send\` returns as soon as the message is *delivered*, so for a moment the session still reports idle and a plain \`wait\` would return before the turn even started. \`--from-busy\` makes the wait first observe a busy phase **of that session** (its own push, or the registry reporting it as generating), then idle — a concurrent session being busy does not count.
|
|
102
|
+
- \`--timeout <seconds>\` bounds the wait (default: wait forever); on expiry it exits \`1\`.
|
|
103
|
+
- \`--lines N\` (default 1) and \`--lines 0\` behave exactly as in \`status\`; progress lines go to stderr, stdout carries only the snapshot.
|
|
104
|
+
- **Waiting on a session someone else destroys.** If another client runs \`wave daemon destroy <id>\` while you are blocked in \`wait\` (the daemon itself staying up), the wait does not hang: it notices the session left the registry and exits \`1\` with \`Session <id> no longer exists (destroyed while waiting)\` — a gone session is never reported as finished. The daemon drops the session from its registry *before* running the teardown, so this holds for the whole destroy window (the abort that clears the loading flag cannot be mistaken for a finished turn).
|
|
105
|
+
- **A re-keyed or in-place-reconfigured session is not a destroyed one.** A live session can change its id (a cleared chat mints a new one): the daemon announces it and the wait follows the *same* session on the new id instead of exiting \`1\` (\`send --wait\` follows it too, so the reply is not missed). A config reload rebuilds the session's agent in place: while that runs the session stays listed, is reported busy (never "listed + idle"), reads keep answering, and writes are refused with a retryable error — so the wait keeps waiting; only a **failed** rebuild drops the session, and then \`1\` with the same "no longer exists" error is correct.
|
|
106
|
+
|
|
107
|
+
**Unattended monitoring needs no wrapper script.** Those three exits cover every way a session can end: \`0\` = finished (stdout is the report), \`3\` = blocked on an approval (go \`respond\`, then \`wait\` again), \`1\` = error, including the session being destroyed out from under you. So \`msg=$(wave daemon wait <id>)\` plus a branch on the exit code replaces the hand-rolled poller completely — there is no fourth case left to poll for.
|
|
108
|
+
|
|
109
|
+
If the CLI on that host predates \`wave daemon wait\`, fall back to a **background poll** — run it in the background rather than blocking on \`send --wait\`:
|
|
110
|
+
|
|
111
|
+
\`\`\`bash
|
|
112
|
+
while true; do
|
|
113
|
+
out=$(wave daemon status "$SESSION_ID" --lines 0)
|
|
114
|
+
echo "$out"
|
|
115
|
+
case "$out" in
|
|
116
|
+
*"Status: idle"*|*"waiting for approval"*) break ;;
|
|
117
|
+
esac
|
|
118
|
+
sleep 30
|
|
119
|
+
done
|
|
120
|
+
\`\`\`
|
|
121
|
+
|
|
122
|
+
That loop is a pattern to re-create per session with whatever background-execution mechanism your host offers (on Windows, PowerShell's \`Start-Sleep\` in place of \`sleep\`), and one poller per session so they do not interfere. Prefer \`wait\`: one process, no poll latency, and an exit code that tells idle (0) apart from waiting-for-approval (3) and from error (1, a destroyed session included). \`waiting for approval\` is an action signal (go answer it, §6); \`idle\` means the turn settled and is worth a look.
|
|
123
|
+
|
|
124
|
+
Do not hand-parse the transcript jsonl (\`~/.wave/projects/<project>/<sessionId>.jsonl\`) to recover a report — \`status <id>\` / \`wait <id>\` are the supported paths. If you ever do read the raw file: each line is one message (\`{"timestamp":…,"role":…,"blocks":[…]}\`) and text lives in \`blocks[].content\` on the \`{"type":"text"}\` block. There is no \`blocks[].text\` field, so a lookup by \`text\` silently returns nothing and looks like "the session never reported".
|
|
125
|
+
|
|
126
|
+
An \`idle\` reading can also be a transient pause between turns (waiting on a verification run, a CI job, or a pending approval). Re-check \`status\` before concluding the task is finished, and if the session goes back to \`generating\`, block on it again (or \`wait --from-busy\` if the busy phase has not started yet).
|
|
127
|
+
|
|
128
|
+
## 6. Permission approvals
|
|
129
|
+
|
|
130
|
+
\`\`\`bash
|
|
131
|
+
wave daemon respond <sessionId> <requestId> --allow
|
|
132
|
+
wave daemon respond <sessionId> <requestId> --deny --reason "why"
|
|
133
|
+
wave daemon respond <sessionId> <requestId> --allow --rule "Bash(ls)" # persist this allow rule for the session
|
|
134
|
+
wave daemon respond <sessionId> <requestId> --allow --mode bypassPermissions # switch the session's permission mode
|
|
135
|
+
\`\`\`
|
|
136
|
+
|
|
137
|
+
- Exactly one of \`--allow\` / \`--deny\` is required. \`respond\` validates the requestId first (\`Request not found or already handled\`) and refuses to touch another session's request.
|
|
138
|
+
- \`--mode\` also applies a mode switch, so after that one answer the session stops asking. Requests already queued still need their own \`respond\`.
|
|
139
|
+
- An \`acceptEdits\` session asks for approval on every shell command; approving them one at a time cannot keep up with generation. Switch the mode with \`--mode bypassPermissions\` instead of responding in a loop.
|
|
140
|
+
|
|
141
|
+
**The root cause of an approval flood is a restarted daemon process.** The permission mode is not recorded in the session transcript, so when a new daemon process re-hosts a session from disk (after \`stop\` / a kill, a CLI-upgrade restart, or a machine reboot), the mode is re-derived from the current configuration — \`permissions.defaultMode\` from settings, else \`default\`. A session created with \`bypassPermissions\` therefore comes back as \`default\` and starts asking for approvals.
|
|
142
|
+
|
|
143
|
+
- \`abort\` does **not** cause this: the session stays in the daemon's memory with its mode intact. If approvals suddenly flood after a long interruption, look for a daemon restart, not for \`abort\`.
|
|
144
|
+
- Recovery is one command: \`respond <requestId> --allow --mode bypassPermissions\`.
|
|
145
|
+
- The symptom can be masked: if the repository's settings set \`permissions.defaultMode: bypassPermissions\`, the re-derived mode is bypass anyway and you will never see the fallback.
|
|
146
|
+
|
|
147
|
+
## 7. AskUserQuestion requests
|
|
148
|
+
|
|
149
|
+
\`status\` renders a pending \`AskUserQuestion\` in full: every question as \`Q<i> [header] <question>\`, with its options numbered from 0. Those option numbers are what \`--answer\` accepts.
|
|
150
|
+
|
|
151
|
+
\`\`\`bash
|
|
152
|
+
wave daemon respond <sessionId> <requestId> --allow --answer "0" # option 0 of the only question
|
|
153
|
+
wave daemon respond <sessionId> <requestId> --allow --answer "1,0" # one option number per question, in order
|
|
154
|
+
\`\`\`
|
|
155
|
+
|
|
156
|
+
The older form still works: a JSON object keyed by the full question text, with the chosen option label as the value — exactly what the GUI dialog submits:
|
|
157
|
+
|
|
158
|
+
\`\`\`bash
|
|
159
|
+
wave daemon respond <sessionId> <requestId> --allow --answer '{"<full question text>":"<option label>"}'
|
|
160
|
+
\`\`\`
|
|
161
|
+
|
|
162
|
+
Whether to answer at all depends on whether the user is around:
|
|
163
|
+
|
|
164
|
+
- If the user can see the session (they have the desktop app open), do not answer for them — relay the question and let them choose.
|
|
165
|
+
- If the user has explicitly handed the work over and is away ("I'm offline, it's on you"), answer with the recommended option (the first one, or an option the question marks as recommended), then report what you answered so they can override it.
|
|
166
|
+
|
|
167
|
+
Answering resolves the request and the session resumes generating on its own — no extra nudge is needed, though \`status\` may briefly still read \`generating\`.
|
|
168
|
+
|
|
169
|
+
## 8. Finishing: destroy, and the worktree
|
|
170
|
+
|
|
171
|
+
\`\`\`bash
|
|
172
|
+
wave daemon destroy <sessionId> --remove-worktree
|
|
173
|
+
\`\`\`
|
|
174
|
+
|
|
175
|
+
- \`destroy\` is idempotent, and does not require the session to be live in the registry.
|
|
176
|
+
- \`--remove-worktree\` is **two steps**: it resolves the session's worktree from its working directory and removes it (path + branch, through the worktree-removal protocol, which also fires the WorktreeRemove hook) and then destroys the session. It refuses to remove the main working tree, so a session that was not created in a linked worktree just fails that step.
|
|
177
|
+
- Because it is two steps, an interrupted \`destroy --remove-worktree\` can be **half-done**: the worktree directory, its branch and uncommitted changes are already gone while the session is still alive. After such an interruption verify all three: \`wave daemon list\` / \`status\` (is the session still there?), \`git worktree list\` and the repo's worktree directory (folder + branch), and \`~/.wave/projects/\` (transcript). A session that was killed but whose transcript survives is still recoverable from the jsonl.
|
|
178
|
+
- Destroy is destructive and irreversible: confirm with the user before running it.
|
|
179
|
+
|
|
180
|
+
Whose session is it? Only tear down sessions you created with \`wave daemon create\`. Never destroy a session created by the desktop app — the user may still be using it — or one you do not recognize. If you cannot tell who created it, ask.
|
|
181
|
+
|
|
182
|
+
## 9. Checklist
|
|
183
|
+
|
|
184
|
+
- Create with \`--worktree\` and \`--permission-mode bypassPermissions\` (the mode default is already bypass, so no approvals appear).
|
|
185
|
+
- \`send\` is async by default; after any interruption, check \`status\` before resending.
|
|
186
|
+
- \`abort\` before re-scoping a running session.
|
|
187
|
+
- Read the final report by blocking: \`wave daemon wait <id>\` (exit 0 = idle, 3 = stuck on an approval, 1 = error — session unknown or destroyed mid-wait; defaults to the last message, raise \`--lines\` if that one is not it). Use \`status <id>\` when you want a snapshot without blocking.
|
|
188
|
+
- To watch a session unattended, loop on \`wait\` and branch on its exit code — no shell polling script to maintain (§5).
|
|
189
|
+
- An approval flood means the daemon process restarted and the mode fell back — recover with \`--mode bypassPermissions\`.
|
|
190
|
+
- After a CLI upgrade, \`wave daemon restart\` (kill the old process first if \`restart\` times out).
|
|
191
|
+
- \`destroy --remove-worktree\` last, after user confirmation, only for sessions you created.
|
|
192
|
+
- Sessions the desktop app manages churn quickly on their own; a shifting \`wave daemon list\` is normal, not a fault.
|
|
193
|
+
`,
|
|
194
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outbound image dimension budget: the per-side pixel ceiling we deliberately
|
|
3
|
+
* keep every image under, aligned with Claude Code's `IMAGE_MAX_WIDTH` /
|
|
4
|
+
* `IMAGE_MAX_HEIGHT`.
|
|
5
|
+
*
|
|
6
|
+
* Two gates enforce it and they must agree on the number:
|
|
7
|
+
*
|
|
8
|
+
* - the webview paste path (`packages/webview/src/utils/imageValidation.ts`)
|
|
9
|
+
* downsamples an oversized paste in the browser, before it becomes a message;
|
|
10
|
+
* - the SDK's outbound rewrite pass (`utils/imageRewrite.ts`, via sharp) is the
|
|
11
|
+
* catch-all for every other source (Read tool, file paths, hosts that cannot
|
|
12
|
+
* re-encode).
|
|
13
|
+
*
|
|
14
|
+
* The value lives here rather than in `utils/imageBudget.ts` because the
|
|
15
|
+
* webview is a browser bundle: by contract it may only take *values* from
|
|
16
|
+
* `wave-agent-sdk/constants`, and `utils/*` is not a public subpath. Copying
|
|
17
|
+
* `2000` into the webview would let the two gates drift apart silently — the
|
|
18
|
+
* paste would shrink to one number while the SDK judged by another.
|
|
19
|
+
*
|
|
20
|
+
* Not to be confused with the gateway's *hard* bound: `MAX_IMAGE_DIMENSION_PX`
|
|
21
|
+
* in `utils/imageDimensions.ts` (8192px per side) is where the upstream starts
|
|
22
|
+
* rejecting the whole request with `HTTP 400 ... unsupported image`. That one is
|
|
23
|
+
* an immovable external limit; this one is our own conservative budget and can
|
|
24
|
+
* be raised freely when we want more image fidelity.
|
|
25
|
+
*/
|
|
26
|
+
export declare const OUTBOUND_IMAGE_MAX_DIMENSION_PX = 2000;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outbound image dimension budget: the per-side pixel ceiling we deliberately
|
|
3
|
+
* keep every image under, aligned with Claude Code's `IMAGE_MAX_WIDTH` /
|
|
4
|
+
* `IMAGE_MAX_HEIGHT`.
|
|
5
|
+
*
|
|
6
|
+
* Two gates enforce it and they must agree on the number:
|
|
7
|
+
*
|
|
8
|
+
* - the webview paste path (`packages/webview/src/utils/imageValidation.ts`)
|
|
9
|
+
* downsamples an oversized paste in the browser, before it becomes a message;
|
|
10
|
+
* - the SDK's outbound rewrite pass (`utils/imageRewrite.ts`, via sharp) is the
|
|
11
|
+
* catch-all for every other source (Read tool, file paths, hosts that cannot
|
|
12
|
+
* re-encode).
|
|
13
|
+
*
|
|
14
|
+
* The value lives here rather than in `utils/imageBudget.ts` because the
|
|
15
|
+
* webview is a browser bundle: by contract it may only take *values* from
|
|
16
|
+
* `wave-agent-sdk/constants`, and `utils/*` is not a public subpath. Copying
|
|
17
|
+
* `2000` into the webview would let the two gates drift apart silently — the
|
|
18
|
+
* paste would shrink to one number while the SDK judged by another.
|
|
19
|
+
*
|
|
20
|
+
* Not to be confused with the gateway's *hard* bound: `MAX_IMAGE_DIMENSION_PX`
|
|
21
|
+
* in `utils/imageDimensions.ts` (8192px per side) is where the upstream starts
|
|
22
|
+
* rejecting the whole request with `HTTP 400 ... unsupported image`. That one is
|
|
23
|
+
* an immovable external limit; this one is our own conservative budget and can
|
|
24
|
+
* be raised freely when we want more image fidelity.
|
|
25
|
+
*/
|
|
26
|
+
export const OUTBOUND_IMAGE_MAX_DIMENSION_PX = 2000;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants entry (`wave-agent-sdk/constants`).
|
|
3
|
+
*
|
|
4
|
+
* Hosts that drive the agent over JSON-RPC import shared values from here rather
|
|
5
|
+
* than from the SDK barrel: the barrel pulls in the whole agent runtime,
|
|
6
|
+
* including dependencies that do work while their module body evaluates and can
|
|
7
|
+
* therefore take a host down at load time. Every module re-exported below must
|
|
8
|
+
* stay dependency-free so this entry remains cheap to bundle.
|
|
9
|
+
*/
|
|
10
|
+
export * from "./images.js";
|
|
11
|
+
export * from "./memory.js";
|
|
12
|
+
export * from "./messages.js";
|
|
13
|
+
export * from "./plugins.js";
|
|
14
|
+
export * from "./subagents.js";
|
|
15
|
+
export * from "./toolLimits.js";
|
|
16
|
+
export * from "./tools.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants entry (`wave-agent-sdk/constants`).
|
|
3
|
+
*
|
|
4
|
+
* Hosts that drive the agent over JSON-RPC import shared values from here rather
|
|
5
|
+
* than from the SDK barrel: the barrel pulls in the whole agent runtime,
|
|
6
|
+
* including dependencies that do work while their module body evaluates and can
|
|
7
|
+
* therefore take a host down at load time. Every module re-exported below must
|
|
8
|
+
* stay dependency-free so this entry remains cheap to bundle.
|
|
9
|
+
*/
|
|
10
|
+
export * from "./images.js";
|
|
11
|
+
export * from "./memory.js";
|
|
12
|
+
export * from "./messages.js";
|
|
13
|
+
export * from "./plugins.js";
|
|
14
|
+
export * from "./subagents.js";
|
|
15
|
+
export * from "./toolLimits.js";
|
|
16
|
+
export * from "./tools.js";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-memory taxonomy and entrypoint limits.
|
|
3
|
+
*
|
|
4
|
+
* The entrypoint (`MEMORY.md`) is an index that is always loaded into the
|
|
5
|
+
* conversation context, so it needs a hard size bound. A line cap alone does
|
|
6
|
+
* not provide one: a 200-line index of very long lines was observed at close
|
|
7
|
+
* to 200 KB. Both caps apply and the tighter one wins.
|
|
8
|
+
*
|
|
9
|
+
* Aligned with Claude Code's `memdir/memdir.ts` (MAX_ENTRYPOINT_LINES /
|
|
10
|
+
* MAX_ENTRYPOINT_BYTES); the second bound is counted in characters, not bytes
|
|
11
|
+
* — Claude Code's constant carries the BYTES name but its value is derived
|
|
12
|
+
* from "~125 chars/line at 200 lines" and is compared against `String.length`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const MEMORY_ENTRYPOINT_NAME = "MEMORY.md";
|
|
15
|
+
export declare const MAX_MEMORY_ENTRYPOINT_LINES = 200;
|
|
16
|
+
export declare const MAX_MEMORY_ENTRYPOINT_CHARS = 25000;
|
|
17
|
+
/** Recommended per-line length for `MEMORY.md` index entries. */
|
|
18
|
+
export declare const MEMORY_INDEX_LINE_GUIDANCE_CHARS = 150;
|
|
19
|
+
export declare const MEMORY_TYPES: readonly ["user", "feedback", "project", "reference"];
|
|
20
|
+
export type MemoryType = (typeof MEMORY_TYPES)[number];
|
|
21
|
+
/**
|
|
22
|
+
* Parse a raw frontmatter value into a MemoryType. Invalid or missing values
|
|
23
|
+
* return undefined — files written before the taxonomy existed keep working
|
|
24
|
+
* and unknown types degrade gracefully.
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseMemoryType(raw: unknown): MemoryType | undefined;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-memory taxonomy and entrypoint limits.
|
|
3
|
+
*
|
|
4
|
+
* The entrypoint (`MEMORY.md`) is an index that is always loaded into the
|
|
5
|
+
* conversation context, so it needs a hard size bound. A line cap alone does
|
|
6
|
+
* not provide one: a 200-line index of very long lines was observed at close
|
|
7
|
+
* to 200 KB. Both caps apply and the tighter one wins.
|
|
8
|
+
*
|
|
9
|
+
* Aligned with Claude Code's `memdir/memdir.ts` (MAX_ENTRYPOINT_LINES /
|
|
10
|
+
* MAX_ENTRYPOINT_BYTES); the second bound is counted in characters, not bytes
|
|
11
|
+
* — Claude Code's constant carries the BYTES name but its value is derived
|
|
12
|
+
* from "~125 chars/line at 200 lines" and is compared against `String.length`.
|
|
13
|
+
*/
|
|
14
|
+
export const MEMORY_ENTRYPOINT_NAME = "MEMORY.md";
|
|
15
|
+
export const MAX_MEMORY_ENTRYPOINT_LINES = 200;
|
|
16
|
+
export const MAX_MEMORY_ENTRYPOINT_CHARS = 25000;
|
|
17
|
+
/** Recommended per-line length for `MEMORY.md` index entries. */
|
|
18
|
+
export const MEMORY_INDEX_LINE_GUIDANCE_CHARS = 150;
|
|
19
|
+
export const MEMORY_TYPES = [
|
|
20
|
+
"user",
|
|
21
|
+
"feedback",
|
|
22
|
+
"project",
|
|
23
|
+
"reference",
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* Parse a raw frontmatter value into a MemoryType. Invalid or missing values
|
|
27
|
+
* return undefined — files written before the taxonomy existed keep working
|
|
28
|
+
* and unknown types degrade gracefully.
|
|
29
|
+
*/
|
|
30
|
+
export function parseMemoryType(raw) {
|
|
31
|
+
if (typeof raw !== "string")
|
|
32
|
+
return undefined;
|
|
33
|
+
return MEMORY_TYPES.find((t) => t === raw);
|
|
34
|
+
}
|