wave-agent-sdk 1.1.5 → 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 +128 -28
- package/dist/agent.js +201 -49
- package/dist/builtin/index.js +2 -0
- package/dist/builtin/plugins.js +11 -20
- package/dist/builtin/skills/settings.js +7 -20
- 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 +54 -10
- package/dist/core/plugin.js +137 -23
- 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 +7 -1
- package/dist/index.js +8 -1
- package/dist/managers/MemoryRuleManager.d.ts +6 -0
- package/dist/managers/MemoryRuleManager.js +12 -0
- package/dist/managers/aiManager.d.ts +35 -25
- package/dist/managers/aiManager.js +204 -202
- package/dist/managers/backgroundTaskManager.js +14 -0
- package/dist/managers/bashModeManager.d.ts +33 -0
- package/dist/managers/bashModeManager.js +110 -0
- package/dist/managers/hookManager.d.ts +18 -0
- package/dist/managers/hookManager.js +37 -3
- package/dist/managers/liveConfigManager.d.ts +33 -0
- package/dist/managers/liveConfigManager.js +106 -11
- package/dist/managers/lspManager.d.ts +9 -0
- package/dist/managers/lspManager.js +47 -18
- package/dist/managers/mcpManager.d.ts +68 -10
- package/dist/managers/mcpManager.js +265 -15
- package/dist/managers/messageManager.d.ts +60 -18
- package/dist/managers/messageManager.js +170 -81
- package/dist/managers/permissionManager.d.ts +69 -0
- package/dist/managers/permissionManager.js +221 -78
- package/dist/managers/planManager.d.ts +9 -0
- package/dist/managers/planManager.js +19 -1
- 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 +50 -0
- package/dist/managers/skillManager.js +166 -12
- package/dist/managers/slashCommandManager.d.ts +10 -0
- package/dist/managers/slashCommandManager.js +44 -31
- package/dist/managers/subagentManager.d.ts +15 -0
- package/dist/managers/subagentManager.js +81 -9
- package/dist/managers/toolManager.d.ts +29 -3
- package/dist/managers/toolManager.js +87 -13
- package/dist/managers/workflowManager.js +6 -0
- 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/index.d.ts +0 -1
- package/dist/prompts/index.js +0 -4
- 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 +323 -102
- 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 +92 -9
- package/dist/services/configurationService.js +246 -64
- 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 +21 -17
- package/dist/services/interactionService.d.ts +9 -1
- package/dist/services/interactionService.js +28 -8
- package/dist/services/jsonlHandler.d.ts +98 -0
- package/dist/services/jsonlHandler.js +250 -12
- package/dist/services/memory.d.ts +17 -1
- package/dist/services/memory.js +44 -7
- package/dist/services/officialMarketplaceMirror.d.ts +85 -0
- package/dist/services/officialMarketplaceMirror.js +290 -0
- package/dist/services/pluginLoader.d.ts +12 -4
- package/dist/services/pluginLoader.js +38 -7
- package/dist/services/remoteSettingsService.js +20 -6
- package/dist/services/session.d.ts +74 -0
- package/dist/services/session.js +174 -16
- package/dist/services/sessionEntries.d.ts +2 -0
- package/dist/services/sessionEntries.js +20 -0
- package/dist/services/worktreeHooks.js +6 -1
- package/dist/stdio/index.d.ts +12 -0
- package/dist/stdio/index.js +12 -0
- package/dist/stdio/notificationRouter.d.ts +38 -0
- package/dist/stdio/notificationRouter.js +97 -0
- package/dist/stdio/rpcClient.d.ts +18 -0
- package/dist/stdio/rpcClient.js +10 -0
- package/dist/stdio/stdioAgent.d.ts +229 -0
- package/dist/stdio/stdioAgent.js +360 -0
- package/dist/tools/artifactTool.js +406 -273
- package/dist/tools/bashTool.js +10 -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/exitPlanMode.js +10 -2
- 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 +24 -1
- package/dist/types/commands.d.ts +7 -0
- package/dist/types/configuration.d.ts +45 -2
- package/dist/types/hooks.d.ts +1 -0
- package/dist/types/hooks.js +19 -0
- package/dist/types/marketplace.d.ts +40 -2
- package/dist/types/mcp.d.ts +42 -0
- package/dist/types/messaging.d.ts +1 -8
- 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 +26 -0
- package/dist/utils/bashParser.d.ts +17 -0
- package/dist/utils/bashParser.js +72 -0
- package/dist/utils/bashStructure/bashLexer.d.ts +96 -0
- package/dist/utils/bashStructure/bashLexer.js +676 -0
- package/dist/utils/bashStructure/bashParser.d.ts +144 -0
- package/dist/utils/bashStructure/bashParser.js +606 -0
- package/dist/utils/bashStructure/bashSemantics.d.ts +70 -0
- package/dist/utils/bashStructure/bashSemantics.js +477 -0
- package/dist/utils/bashStructure/index.d.ts +26 -0
- package/dist/utils/bashStructure/index.js +27 -0
- package/dist/utils/bashStructure/types.d.ts +62 -0
- package/dist/utils/bashStructure/types.js +47 -0
- package/dist/utils/constants.d.ts +10 -0
- package/dist/utils/constants.js +10 -0
- package/dist/utils/containerSetup.js +48 -6
- 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 +44 -0
- package/dist/utils/fileUtils.js +118 -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 -20
- package/dist/utils/messageOperations.js +40 -91
- 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 +48 -45
- package/dist/utils/tokenCalculation.js +0 -8
- package/dist/utils/userSettings.d.ts +90 -0
- package/dist/utils/userSettings.js +291 -0
- package/dist/utils/worktreeUtils.d.ts +2 -1
- package/dist/utils/worktreeUtils.js +64 -34
- package/package.json +12 -4
- package/dist/managers/bangManager.d.ts +0 -26
- package/dist/managers/bangManager.js +0 -78
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared artifact content layer.
|
|
3
|
+
*
|
|
4
|
+
* The single implementation of artifact content retrieval: slug extraction,
|
|
5
|
+
* metadata probe (`via=model_read`), ownership detection, Bearer-authenticated
|
|
6
|
+
* content fetch, HTML→markdown conversion and large-content persistence.
|
|
7
|
+
* Both the Artifact tool's `read` action and WebFetch's artifact URL
|
|
8
|
+
* interception call into it (spec: 读取实现单一化).
|
|
9
|
+
*/
|
|
10
|
+
import type { ToolContext, ToolResult } from "../tools/types.js";
|
|
11
|
+
/** Artifact content beyond this size is persisted to a temp file (path + preview). */
|
|
12
|
+
export declare const ARTIFACT_PREVIEW_BYTES: number;
|
|
13
|
+
/** Used when the caller reads someone else's artifact without a `prompt`. */
|
|
14
|
+
export declare const DEFAULT_READ_SUMMARY_PROMPT = "Summarize this page: its purpose, structure, and key content.";
|
|
15
|
+
/** Frame metadata returned by `GET /api/frame/{slug}?via=model_read`. */
|
|
16
|
+
export interface FrameMeta {
|
|
17
|
+
slug?: string;
|
|
18
|
+
version?: string;
|
|
19
|
+
title?: string;
|
|
20
|
+
favicon?: string;
|
|
21
|
+
perm?: {
|
|
22
|
+
mode: "owner" | "users" | "org";
|
|
23
|
+
role?: string;
|
|
24
|
+
};
|
|
25
|
+
url?: string;
|
|
26
|
+
contentUrl?: string;
|
|
27
|
+
/** Empty/missing = shared-live (readers see live updates); non-empty = pinned. */
|
|
28
|
+
shared?: string;
|
|
29
|
+
}
|
|
30
|
+
/** The publishing user's relationship to an artifact. */
|
|
31
|
+
export type ArtifactOwnership = "owner" | "reader";
|
|
32
|
+
export interface ArtifactContent {
|
|
33
|
+
slug: string;
|
|
34
|
+
version: string;
|
|
35
|
+
ownership: ArtifactOwnership;
|
|
36
|
+
/** Raw HTML as stored server-side (inline CSS/JS preserved). */
|
|
37
|
+
html: string;
|
|
38
|
+
bytes: number;
|
|
39
|
+
}
|
|
40
|
+
export type FrameMetaResult = {
|
|
41
|
+
kind: "ok";
|
|
42
|
+
meta: FrameMeta;
|
|
43
|
+
} | {
|
|
44
|
+
kind: "error";
|
|
45
|
+
status: number;
|
|
46
|
+
error: string;
|
|
47
|
+
};
|
|
48
|
+
export type ArtifactContentResult = {
|
|
49
|
+
kind: "ok";
|
|
50
|
+
artifact: ArtifactContent;
|
|
51
|
+
} | {
|
|
52
|
+
kind: "error";
|
|
53
|
+
status?: number;
|
|
54
|
+
error: string;
|
|
55
|
+
};
|
|
56
|
+
/** Extract the artifact slug from a `{host}/code/artifact/{slug}` URL. */
|
|
57
|
+
export declare function extractArtifactSlug(url: string): string | null;
|
|
58
|
+
/** Probe artifact metadata; `via=model_read` marks model access. */
|
|
59
|
+
export declare function fetchFrameMeta(slug: string, opts?: {
|
|
60
|
+
abortSignal?: AbortSignal;
|
|
61
|
+
}): Promise<FrameMetaResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Read an artifact's content. Owner reads come back as raw HTML (inline CSS/JS
|
|
64
|
+
* intact); ownership is decided from the server metadata so readers of someone
|
|
65
|
+
* else's page can be routed to the isolated-summary path.
|
|
66
|
+
*/
|
|
67
|
+
export declare function readArtifactContent(slug: string, opts?: {
|
|
68
|
+
url?: string;
|
|
69
|
+
abortSignal?: AbortSignal;
|
|
70
|
+
}): Promise<ArtifactContentResult>;
|
|
71
|
+
/** Convert artifact HTML to markdown (the summary path feeds text, not markup). */
|
|
72
|
+
export declare function htmlToMarkdown(html: string): string;
|
|
73
|
+
/** Whether content is too large to return inline. */
|
|
74
|
+
export declare function isLargeArtifactContent(content: string): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Persist content to a temp file and return the `<persisted-output>` message.
|
|
77
|
+
* Returns null when the content is small enough to inline, or on write failure.
|
|
78
|
+
*/
|
|
79
|
+
export declare function persistArtifactContent(content: string): string | null;
|
|
80
|
+
/**
|
|
81
|
+
* Reader view: run someone else's artifact through the fast model and return
|
|
82
|
+
* only the answer, so the full page never reaches the main conversation.
|
|
83
|
+
*/
|
|
84
|
+
export declare function summarizeArtifactAsReader(url: string, prompt: string, html: string, context: ToolContext): Promise<ToolResult>;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared artifact content layer.
|
|
3
|
+
*
|
|
4
|
+
* The single implementation of artifact content retrieval: slug extraction,
|
|
5
|
+
* metadata probe (`via=model_read`), ownership detection, Bearer-authenticated
|
|
6
|
+
* content fetch, HTML→markdown conversion and large-content persistence.
|
|
7
|
+
* Both the Artifact tool's `read` action and WebFetch's artifact URL
|
|
8
|
+
* interception call into it (spec: 读取实现单一化).
|
|
9
|
+
*/
|
|
10
|
+
import TurndownService from "turndown";
|
|
11
|
+
import { authService, createAuthAwareFetch } from "./authService.js";
|
|
12
|
+
import { logger } from "../utils/globalLogger.js";
|
|
13
|
+
import { buildPersistedOutputMessage, generatePreview, persistToolResult, } from "../utils/toolResultStorage.js";
|
|
14
|
+
import { processContentWithAI } from "./contentSummarizer.js";
|
|
15
|
+
/** Metadata/content request timeout. */
|
|
16
|
+
const ARTIFACT_READ_TIMEOUT_MS = 60000;
|
|
17
|
+
/** Artifact content beyond this size is persisted to a temp file (path + preview). */
|
|
18
|
+
export const ARTIFACT_PREVIEW_BYTES = 2 * 1024;
|
|
19
|
+
/** Inline fallback when persisting large content fails. */
|
|
20
|
+
const MAX_INLINE_ARTIFACT_CHARS = 100000;
|
|
21
|
+
/** Used when the caller reads someone else's artifact without a `prompt`. */
|
|
22
|
+
export const DEFAULT_READ_SUMMARY_PROMPT = "Summarize this page: its purpose, structure, and key content.";
|
|
23
|
+
/** Extract the artifact slug from a `{host}/code/artifact/{slug}` URL. */
|
|
24
|
+
export function extractArtifactSlug(url) {
|
|
25
|
+
try {
|
|
26
|
+
const parsed = new URL(url);
|
|
27
|
+
const match = parsed.pathname.match(/^\/code\/artifact\/([^/]+)\/?$/);
|
|
28
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function readSignal(abortSignal) {
|
|
35
|
+
return abortSignal
|
|
36
|
+
? AbortSignal.any([
|
|
37
|
+
abortSignal,
|
|
38
|
+
AbortSignal.timeout(ARTIFACT_READ_TIMEOUT_MS),
|
|
39
|
+
])
|
|
40
|
+
: AbortSignal.timeout(ARTIFACT_READ_TIMEOUT_MS);
|
|
41
|
+
}
|
|
42
|
+
/** Probe artifact metadata; `via=model_read` marks model access. */
|
|
43
|
+
export async function fetchFrameMeta(slug, opts = {}) {
|
|
44
|
+
const serverUrl = authService.getServerUrl();
|
|
45
|
+
const authFetch = createAuthAwareFetch(globalThis.fetch);
|
|
46
|
+
try {
|
|
47
|
+
const res = await authFetch(`${serverUrl}/api/frame/${encodeURIComponent(slug)}?via=model_read`, { method: "GET", signal: readSignal(opts.abortSignal) });
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
return {
|
|
50
|
+
kind: "error",
|
|
51
|
+
status: res.status,
|
|
52
|
+
error: `Failed to fetch artifact metadata: ${res.status} ${res.statusText}`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return { kind: "ok", meta: (await res.json()) };
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
logger?.warn("Artifact metadata probe failed", {
|
|
59
|
+
slug,
|
|
60
|
+
error: String(error),
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
kind: "error",
|
|
64
|
+
status: 0,
|
|
65
|
+
error: `Failed to fetch artifact metadata: ${error instanceof Error ? error.message : String(error)}`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Read an artifact's content. Owner reads come back as raw HTML (inline CSS/JS
|
|
71
|
+
* intact); ownership is decided from the server metadata so readers of someone
|
|
72
|
+
* else's page can be routed to the isolated-summary path.
|
|
73
|
+
*/
|
|
74
|
+
export async function readArtifactContent(slug, opts = {}) {
|
|
75
|
+
const displayUrl = opts.url || slug;
|
|
76
|
+
const metaResult = await fetchFrameMeta(slug, {
|
|
77
|
+
abortSignal: opts.abortSignal,
|
|
78
|
+
});
|
|
79
|
+
if (metaResult.kind === "error") {
|
|
80
|
+
if (metaResult.status === 404) {
|
|
81
|
+
return {
|
|
82
|
+
kind: "error",
|
|
83
|
+
status: 404,
|
|
84
|
+
error: `Artifact not found: ${displayUrl} (it may have been deleted)`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (metaResult.status === 403) {
|
|
88
|
+
return {
|
|
89
|
+
kind: "error",
|
|
90
|
+
status: 403,
|
|
91
|
+
error: `You do not have permission to read this artifact: ${displayUrl}`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
kind: "error",
|
|
96
|
+
status: metaResult.status,
|
|
97
|
+
error: metaResult.error,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const meta = metaResult.meta;
|
|
101
|
+
const version = typeof meta.version === "string" ? meta.version : "";
|
|
102
|
+
const contentUrl = typeof meta.contentUrl === "string" ? meta.contentUrl : "";
|
|
103
|
+
if (!contentUrl) {
|
|
104
|
+
return {
|
|
105
|
+
kind: "error",
|
|
106
|
+
error: "Artifact metadata did not include a contentUrl",
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
// Unknown ownership is treated as reader-owned: never hand out full text of a
|
|
110
|
+
// page we cannot prove the user owns.
|
|
111
|
+
const ownership = meta.perm?.mode === "owner" || meta.perm?.role === "owner"
|
|
112
|
+
? "owner"
|
|
113
|
+
: "reader";
|
|
114
|
+
const serverUrl = authService.getServerUrl();
|
|
115
|
+
const authFetch = createAuthAwareFetch(globalThis.fetch);
|
|
116
|
+
let html;
|
|
117
|
+
try {
|
|
118
|
+
const res = await authFetch(new URL(contentUrl, serverUrl).toString(), {
|
|
119
|
+
method: "GET",
|
|
120
|
+
signal: readSignal(opts.abortSignal),
|
|
121
|
+
});
|
|
122
|
+
if (res.status === 403) {
|
|
123
|
+
return {
|
|
124
|
+
kind: "error",
|
|
125
|
+
status: 403,
|
|
126
|
+
error: `You do not have permission to read this artifact: ${displayUrl}`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
if (!res.ok) {
|
|
130
|
+
return {
|
|
131
|
+
kind: "error",
|
|
132
|
+
status: res.status,
|
|
133
|
+
error: `Failed to fetch artifact content: ${res.status} ${res.statusText}`,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
html = await res.text();
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
logger?.warn("Artifact content fetch failed", {
|
|
140
|
+
slug,
|
|
141
|
+
error: String(error),
|
|
142
|
+
});
|
|
143
|
+
return {
|
|
144
|
+
kind: "error",
|
|
145
|
+
error: `Failed to fetch artifact content: ${error instanceof Error ? error.message : String(error)}`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
kind: "ok",
|
|
150
|
+
artifact: {
|
|
151
|
+
slug,
|
|
152
|
+
version,
|
|
153
|
+
ownership,
|
|
154
|
+
html,
|
|
155
|
+
bytes: new TextEncoder().encode(html).length,
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/** Convert artifact HTML to markdown (the summary path feeds text, not markup). */
|
|
160
|
+
export function htmlToMarkdown(html) {
|
|
161
|
+
return new TurndownService().turndown(html);
|
|
162
|
+
}
|
|
163
|
+
/** Whether content is too large to return inline. */
|
|
164
|
+
export function isLargeArtifactContent(content) {
|
|
165
|
+
return new TextEncoder().encode(content).length > ARTIFACT_PREVIEW_BYTES;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Persist content to a temp file and return the `<persisted-output>` message.
|
|
169
|
+
* Returns null when the content is small enough to inline, or on write failure.
|
|
170
|
+
*/
|
|
171
|
+
export function persistArtifactContent(content) {
|
|
172
|
+
if (!isLargeArtifactContent(content))
|
|
173
|
+
return null;
|
|
174
|
+
const filePath = persistToolResult(content, "artifact");
|
|
175
|
+
if (!filePath)
|
|
176
|
+
return null;
|
|
177
|
+
return buildPersistedOutputMessage(content.length, filePath, generatePreview(content));
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Reader view: run someone else's artifact through the fast model and return
|
|
181
|
+
* only the answer, so the full page never reaches the main conversation.
|
|
182
|
+
*/
|
|
183
|
+
export async function summarizeArtifactAsReader(url, prompt, html, context) {
|
|
184
|
+
const markdown = htmlToMarkdown(html);
|
|
185
|
+
const bytes = new TextEncoder().encode(markdown).length;
|
|
186
|
+
const persisted = persistArtifactContent(markdown);
|
|
187
|
+
let aiInput = markdown;
|
|
188
|
+
if (persisted) {
|
|
189
|
+
aiInput = persisted;
|
|
190
|
+
}
|
|
191
|
+
else if (bytes > ARTIFACT_PREVIEW_BYTES) {
|
|
192
|
+
aiInput =
|
|
193
|
+
markdown.substring(0, MAX_INLINE_ARTIFACT_CHARS) +
|
|
194
|
+
"\n\n... (content truncated, failed to persist full output)";
|
|
195
|
+
}
|
|
196
|
+
const result = await processContentWithAI(url, prompt || DEFAULT_READ_SUMMARY_PROMPT, aiInput, 200, "OK", context, bytes);
|
|
197
|
+
if (persisted) {
|
|
198
|
+
// Append the persisted-output message so the model can Read the full text.
|
|
199
|
+
result.content = result.content
|
|
200
|
+
? result.content + "\n\n" + persisted
|
|
201
|
+
: persisted;
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* - slug → latest known version: powers the stale-version guard
|
|
8
8
|
* (local knowledge older than the live version blocks a redeploy
|
|
9
9
|
* unless `force` is set) and lets WebFetch record versions it observed.
|
|
10
|
+
* - slug → read approval: reading someone else's artifact is confirmed once
|
|
11
|
+
* per session; later reads of the same page are not re-confirmed.
|
|
10
12
|
*
|
|
11
13
|
* State is keyed by sessionId so parallel sessions never observe each other.
|
|
12
14
|
*/
|
|
@@ -23,5 +25,9 @@ export declare function getArtifactByFilePath(sessionId: string, filePath: strin
|
|
|
23
25
|
export declare function getRecordedVersion(sessionId: string, slug: string): string | undefined;
|
|
24
26
|
/** Record an observed version (from a publish, a 409 conflict, or a WebFetch read). */
|
|
25
27
|
export declare function recordVersion(sessionId: string, slug: string, version: string): void;
|
|
28
|
+
/** Remember that the user approved reading someone else's artifact this session. */
|
|
29
|
+
export declare function markArtifactReadApproved(sessionId: string, slug: string): void;
|
|
30
|
+
/** Whether the user already approved reading this artifact in this session. */
|
|
31
|
+
export declare function isArtifactReadApproved(sessionId: string, slug: string): boolean;
|
|
26
32
|
/** Clear all session-scoped artifact state (used when a session ends). */
|
|
27
33
|
export declare function clearArtifactSession(sessionId: string): void;
|
|
@@ -7,11 +7,14 @@
|
|
|
7
7
|
* - slug → latest known version: powers the stale-version guard
|
|
8
8
|
* (local knowledge older than the live version blocks a redeploy
|
|
9
9
|
* unless `force` is set) and lets WebFetch record versions it observed.
|
|
10
|
+
* - slug → read approval: reading someone else's artifact is confirmed once
|
|
11
|
+
* per session; later reads of the same page are not re-confirmed.
|
|
10
12
|
*
|
|
11
13
|
* State is keyed by sessionId so parallel sessions never observe each other.
|
|
12
14
|
*/
|
|
13
15
|
const sessionArtifacts = new Map();
|
|
14
16
|
const sessionSlugVersions = new Map();
|
|
17
|
+
const sessionReadApprovals = new Map();
|
|
15
18
|
function fileMapFor(sessionId) {
|
|
16
19
|
let map = sessionArtifacts.get(sessionId);
|
|
17
20
|
if (!map) {
|
|
@@ -45,8 +48,22 @@ export function getRecordedVersion(sessionId, slug) {
|
|
|
45
48
|
export function recordVersion(sessionId, slug, version) {
|
|
46
49
|
slugMapFor(sessionId).set(slug, version);
|
|
47
50
|
}
|
|
51
|
+
/** Remember that the user approved reading someone else's artifact this session. */
|
|
52
|
+
export function markArtifactReadApproved(sessionId, slug) {
|
|
53
|
+
let set = sessionReadApprovals.get(sessionId);
|
|
54
|
+
if (!set) {
|
|
55
|
+
set = new Set();
|
|
56
|
+
sessionReadApprovals.set(sessionId, set);
|
|
57
|
+
}
|
|
58
|
+
set.add(slug);
|
|
59
|
+
}
|
|
60
|
+
/** Whether the user already approved reading this artifact in this session. */
|
|
61
|
+
export function isArtifactReadApproved(sessionId, slug) {
|
|
62
|
+
return sessionReadApprovals.get(sessionId)?.has(slug) ?? false;
|
|
63
|
+
}
|
|
48
64
|
/** Clear all session-scoped artifact state (used when a session ends). */
|
|
49
65
|
export function clearArtifactSession(sessionId) {
|
|
50
66
|
sessionArtifacts.delete(sessionId);
|
|
51
67
|
sessionSlugVersions.delete(sessionId);
|
|
68
|
+
sessionReadApprovals.delete(sessionId);
|
|
52
69
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
-
import * as fs from "node:fs/promises";
|
|
3
2
|
import { logger } from "../utils/globalLogger.js";
|
|
4
3
|
import { isPathInside } from "../utils/pathSafety.js";
|
|
4
|
+
import { formatMemoryManifest, scanMemoryFiles } from "../utils/memoryIndex.js";
|
|
5
5
|
import { buildAutoMemoryExtractionPrompt } from "../prompts/autoMemoryExtraction.js";
|
|
6
6
|
import { READ_ONLY_COMMANDS, splitBashCommand, hasWriteRedirections, hasCommandSubstitution, hasProcessSubstitution, hasSedInPlace, isDangerousFind, } from "../utils/bashParser.js";
|
|
7
7
|
/**
|
|
@@ -140,18 +140,10 @@ export class AutoMemoryService {
|
|
|
140
140
|
const memoryDir = this.memoryService.getAutoMemoryDirectory(workdir);
|
|
141
141
|
// Ensure memory directory exists before starting
|
|
142
142
|
await this.memoryService.ensureAutoMemoryDirectory(workdir);
|
|
143
|
-
// Prepare manifest of existing memory files
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
existingMemoriesManifest = files
|
|
148
|
-
.filter((f) => f.endsWith(".md"))
|
|
149
|
-
.map((f) => `- ${f}`)
|
|
150
|
-
.join("\n");
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
// Ignore if directory doesn't exist yet
|
|
154
|
-
}
|
|
143
|
+
// Prepare manifest of existing memory files: type tag, filename, timestamp
|
|
144
|
+
// and description, so the fork can tell whether a memory already exists
|
|
145
|
+
// without spending a turn on `ls`.
|
|
146
|
+
const existingMemoriesManifest = formatMemoryManifest(await scanMemoryFiles(memoryDir));
|
|
155
147
|
const prompt = buildAutoMemoryExtractionPrompt(newMessageCount, existingMemoriesManifest);
|
|
156
148
|
await this.aiManager.runAutoMemoryFork(messages, `${prompt}\n\nThe memory directory for this project is: ${memoryDir}`, {
|
|
157
149
|
maxTurns: 5, // Limit turns to prevent verification rabbit-holes
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Centralized service for loading, validating, and managing Wave configuration files.
|
|
5
5
|
* Replaces distributed configuration logic previously embedded in hook.ts.
|
|
6
6
|
*/
|
|
7
|
+
import type { HookEvent, HookEventConfig } from "../types/hooks.js";
|
|
7
8
|
import type { ConfigurationLoadResult, ValidationResult, ConfigurationPaths, WaveConfiguration, Scope, MarketplaceConfig } from "../types/configuration.js";
|
|
8
9
|
import { type EnvironmentValidationResult, type MergedEnvironmentContext, type EnvironmentMergeOptions } from "../types/environment.js";
|
|
9
10
|
import { GatewayConfig, ModelConfig, PermissionMode, AgentOptions } from "../types/index.js";
|
|
@@ -14,6 +15,15 @@ import { ClientOptions } from "openai";
|
|
|
14
15
|
* session cleanup runs before config is loaded).
|
|
15
16
|
*/
|
|
16
17
|
export declare function validateConfigurationObject(config: WaveConfiguration): ValidationResult;
|
|
18
|
+
/**
|
|
19
|
+
* The settings.json-derived configuration a turn reads from, captured when the
|
|
20
|
+
* turn started (see {@link ConfigurationService.setTurnSnapshotSource}). Held
|
|
21
|
+
* while a turn runs so a live reload can't shift values under it.
|
|
22
|
+
*/
|
|
23
|
+
export interface TurnConfigurationSnapshot {
|
|
24
|
+
configuration: WaveConfiguration | null;
|
|
25
|
+
env: Record<string, string>;
|
|
26
|
+
}
|
|
17
27
|
/**
|
|
18
28
|
* Default ConfigurationService implementation
|
|
19
29
|
*
|
|
@@ -25,10 +35,19 @@ export declare class ConfigurationService {
|
|
|
25
35
|
private options;
|
|
26
36
|
private _configuredEnvKeys;
|
|
27
37
|
private envSnapshot;
|
|
38
|
+
private turnSnapshotSource?;
|
|
39
|
+
/**
|
|
40
|
+
* Register the turn-snapshot source (see {@link TurnConfigurationSnapshot}).
|
|
41
|
+
*/
|
|
42
|
+
setTurnSnapshotSource(source: () => TurnConfigurationSnapshot | null): void;
|
|
28
43
|
/**
|
|
29
44
|
* Set agent options for configuration resolution
|
|
30
45
|
*/
|
|
31
46
|
setOptions(options: AgentOptions): void;
|
|
47
|
+
/** Configuration the current turn reads from (live configuration outside a turn). */
|
|
48
|
+
private get liveConfiguration();
|
|
49
|
+
/** Environment snapshot the current turn reads from. */
|
|
50
|
+
private get liveEnvSnapshot();
|
|
32
51
|
/**
|
|
33
52
|
* Returns a copy of the per-session environment snapshot (settings.json `env`).
|
|
34
53
|
* Priority over OS env; does NOT include OS env. For subprocess spawning use
|
|
@@ -100,17 +119,30 @@ export declare class ConfigurationService {
|
|
|
100
119
|
resolveMaxInputTokens(constructorLimit?: number, model?: string): number;
|
|
101
120
|
/**
|
|
102
121
|
* Resolves preferred language with fallbacks
|
|
103
|
-
* Resolution priority: override > options > settings.json >
|
|
122
|
+
* Resolution priority: override > options > settings.json > default (`zh-CN`)
|
|
123
|
+
*
|
|
124
|
+
* 末尾的默认值保证「未设置」时也有明确生效值,且与设置页下拉的默认项一致
|
|
125
|
+
* (spec agent-config 边界说明「语言默认值」)。调用方不得再假定
|
|
126
|
+
* 「undefined = 不注入语言指令」。
|
|
127
|
+
*
|
|
104
128
|
* @param constructorLanguage - Language override (optional)
|
|
105
|
-
* @returns Resolved language
|
|
129
|
+
* @returns Resolved language (never empty)
|
|
106
130
|
*/
|
|
107
|
-
resolveLanguage(constructorLanguage?: string): string
|
|
131
|
+
resolveLanguage(constructorLanguage?: string): string;
|
|
108
132
|
/**
|
|
109
133
|
* Resolves auto-memory enabled state with fallbacks
|
|
110
|
-
* Resolution priority: settings.json > WAVE_DISABLE_AUTO_MEMORY > default (true)
|
|
134
|
+
* Resolution priority: session options (session-level override) > settings.json > WAVE_DISABLE_AUTO_MEMORY > default (true)
|
|
111
135
|
* @returns Resolved auto-memory enabled state
|
|
112
136
|
*/
|
|
113
137
|
resolveAutoMemoryEnabled(): boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Same resolution chain but read from the live merged configuration, ignoring
|
|
140
|
+
* the turn snapshot. The permission safe zone is a live mechanism (like
|
|
141
|
+
* permission rules, applied by LiveConfigManager on reload), so its sync must
|
|
142
|
+
* not be pinned to the turn-start value.
|
|
143
|
+
*/
|
|
144
|
+
resolveAutoMemoryEnabledNow(): boolean;
|
|
145
|
+
private resolveAutoMemoryEnabledFrom;
|
|
114
146
|
/**
|
|
115
147
|
* Resolves worktree base ref with fallbacks
|
|
116
148
|
* Resolution priority: settings.json > default ("fresh")
|
|
@@ -119,7 +151,7 @@ export declare class ConfigurationService {
|
|
|
119
151
|
resolveWorktreeBaseRef(): "fresh" | "head";
|
|
120
152
|
/**
|
|
121
153
|
* Resolves auto-memory extraction frequency with fallbacks
|
|
122
|
-
* Resolution priority: settings.json > WAVE_AUTO_MEMORY_FREQUENCY > default (1)
|
|
154
|
+
* Resolution priority: session options (程序化会话级覆盖) > settings.json > WAVE_AUTO_MEMORY_FREQUENCY > default (1)
|
|
123
155
|
* @returns Resolved auto-memory extraction frequency (turns)
|
|
124
156
|
*/
|
|
125
157
|
resolveAutoMemoryFrequency(): number;
|
|
@@ -131,9 +163,13 @@ export declare class ConfigurationService {
|
|
|
131
163
|
*/
|
|
132
164
|
resolveMaxOutputTokens(constructorLimit?: number): number;
|
|
133
165
|
/**
|
|
134
|
-
* Set the active model in the session
|
|
166
|
+
* Set the active model in the session.
|
|
167
|
+
*
|
|
168
|
+
* Persisting to `~/.wave/settings.json` is best-effort: the in-memory model
|
|
169
|
+
* applies immediately, and a failed write (e.g. a locked file / rename EPERM
|
|
170
|
+
* on Windows) is logged instead of escaping as an unhandled rejection.
|
|
135
171
|
*/
|
|
136
|
-
setModel(model: string): void
|
|
172
|
+
setModel(model: string): Promise<void>;
|
|
137
173
|
private persistModelToSettings;
|
|
138
174
|
/**
|
|
139
175
|
* Get all configured models from settings.json and environment
|
|
@@ -161,9 +197,38 @@ export declare class ConfigurationService {
|
|
|
161
197
|
*/
|
|
162
198
|
updateEnabledPlugin(workdir: string, scope: Scope, pluginId: string, enabled: boolean): Promise<void>;
|
|
163
199
|
/**
|
|
164
|
-
*
|
|
200
|
+
* Resolve the settings.json path for a hook scope.
|
|
201
|
+
* user → ~/.wave/settings.json; project → <workdir>/.wave/settings.json
|
|
202
|
+
*/
|
|
203
|
+
private resolveHookConfigPath;
|
|
204
|
+
/**
|
|
205
|
+
* Absolute path of the settings.json that holds hooks for `scope`
|
|
206
|
+
* (user → ~/.wave/settings.json, project → <workdir>/.wave/settings.json).
|
|
207
|
+
* Hosts surface it to the GUI so editors / file panels can open the file —
|
|
208
|
+
* they cannot expand a `~` prefix, so the path must already be absolute.
|
|
209
|
+
*/
|
|
210
|
+
getHookConfigPath(workdir: string, scope: "user" | "project"): string;
|
|
211
|
+
/**
|
|
212
|
+
* Get hook configurations at a specific scope (user or project settings.json)
|
|
213
|
+
*/
|
|
214
|
+
getHooksByScope(workdir: string, scope: Scope): Promise<Partial<Record<HookEvent, HookEventConfig[]>>>;
|
|
215
|
+
/**
|
|
216
|
+
* Delete a hook at a specific scope, identified by hookName of the form
|
|
217
|
+
* `Event:Matcher`. Removes the matching entry from the scope's settings.json
|
|
218
|
+
* hooks block.
|
|
219
|
+
*/
|
|
220
|
+
deleteHook(workdir: string, scope: Scope, hookName: string): Promise<void>;
|
|
221
|
+
/**
|
|
222
|
+
* Get merged marketplaces from all scopes, with admin-managed (remote) entries
|
|
223
|
+
* taking precedence per key (spec enterprise server-managed-config「托管配置
|
|
224
|
+
* 下发插件市场与启用列表」场景 5).
|
|
165
225
|
*/
|
|
166
226
|
getMergedMarketplaces(workdir: string): Record<string, MarketplaceConfig>;
|
|
227
|
+
/**
|
|
228
|
+
* Marketplaces declared by the remote (managed) settings. `null` means the
|
|
229
|
+
* managed layer declares none, leaving the local configuration chain in charge.
|
|
230
|
+
*/
|
|
231
|
+
getManagedMarketplaces(): Record<string, MarketplaceConfig> | null;
|
|
167
232
|
/**
|
|
168
233
|
* Get marketplaces at a specific scope
|
|
169
234
|
*/
|
|
@@ -181,9 +246,18 @@ export declare class ConfigurationService {
|
|
|
181
246
|
*/
|
|
182
247
|
removeEnabledPlugin(workdir: string, scope: Scope, pluginId: string): Promise<void>;
|
|
183
248
|
/**
|
|
184
|
-
* Get merged enabled plugins from all scopes
|
|
249
|
+
* Get merged enabled plugins from all scopes, with admin-managed (remote)
|
|
250
|
+
* entries taking precedence per key (spec enterprise server-managed-config
|
|
251
|
+
* 「托管配置下发插件市场与启用列表」场景 5). A managed `false` therefore
|
|
252
|
+
* force-disables a plugin no matter what the local files say.
|
|
185
253
|
*/
|
|
186
254
|
getMergedEnabledPlugins(workdir: string): Record<string, boolean>;
|
|
255
|
+
/**
|
|
256
|
+
* Enabled-plugin entries declared by the remote (managed) settings. `null`
|
|
257
|
+
* means the managed layer declares none. Doubles as the authoritative
|
|
258
|
+
* "is this plugin managed by the organization" lookup (spec plugin A-024).
|
|
259
|
+
*/
|
|
260
|
+
getManagedEnabledPlugins(): Record<string, boolean> | null;
|
|
187
261
|
/**
|
|
188
262
|
* Load Wave configuration from a JSON file
|
|
189
263
|
* Supports both hooks and environment variables with proper validation
|
|
@@ -216,3 +290,12 @@ export declare function loadUserConfigEnv(): Record<string, string>;
|
|
|
216
290
|
* Checks .local.json files first, then falls back to .json files
|
|
217
291
|
*/
|
|
218
292
|
export declare function loadMergedWaveConfig(workdir: string): WaveConfiguration | null;
|
|
293
|
+
/**
|
|
294
|
+
* Parse a hookName of the form `Event:Matcher` (e.g. `PreToolUse:Write`)
|
|
295
|
+
* into its event and matcher parts. Hooks without a matcher use the event
|
|
296
|
+
* name alone (e.g. `SessionStart`).
|
|
297
|
+
*/
|
|
298
|
+
export declare function parseHookName(hookName: string): {
|
|
299
|
+
event: HookEvent;
|
|
300
|
+
matcher: string;
|
|
301
|
+
};
|