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
|
@@ -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
|
|
@@ -15,6 +15,15 @@ import { ClientOptions } from "openai";
|
|
|
15
15
|
* session cleanup runs before config is loaded).
|
|
16
16
|
*/
|
|
17
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
|
+
}
|
|
18
27
|
/**
|
|
19
28
|
* Default ConfigurationService implementation
|
|
20
29
|
*
|
|
@@ -26,10 +35,19 @@ export declare class ConfigurationService {
|
|
|
26
35
|
private options;
|
|
27
36
|
private _configuredEnvKeys;
|
|
28
37
|
private envSnapshot;
|
|
38
|
+
private turnSnapshotSource?;
|
|
39
|
+
/**
|
|
40
|
+
* Register the turn-snapshot source (see {@link TurnConfigurationSnapshot}).
|
|
41
|
+
*/
|
|
42
|
+
setTurnSnapshotSource(source: () => TurnConfigurationSnapshot | null): void;
|
|
29
43
|
/**
|
|
30
44
|
* Set agent options for configuration resolution
|
|
31
45
|
*/
|
|
32
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();
|
|
33
51
|
/**
|
|
34
52
|
* Returns a copy of the per-session environment snapshot (settings.json `env`).
|
|
35
53
|
* Priority over OS env; does NOT include OS env. For subprocess spawning use
|
|
@@ -101,17 +119,30 @@ export declare class ConfigurationService {
|
|
|
101
119
|
resolveMaxInputTokens(constructorLimit?: number, model?: string): number;
|
|
102
120
|
/**
|
|
103
121
|
* Resolves preferred language with fallbacks
|
|
104
|
-
* 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
|
+
*
|
|
105
128
|
* @param constructorLanguage - Language override (optional)
|
|
106
|
-
* @returns Resolved language
|
|
129
|
+
* @returns Resolved language (never empty)
|
|
107
130
|
*/
|
|
108
|
-
resolveLanguage(constructorLanguage?: string): string
|
|
131
|
+
resolveLanguage(constructorLanguage?: string): string;
|
|
109
132
|
/**
|
|
110
133
|
* Resolves auto-memory enabled state with fallbacks
|
|
111
|
-
* Resolution priority: session options (
|
|
134
|
+
* Resolution priority: session options (session-level override) > settings.json > WAVE_DISABLE_AUTO_MEMORY > default (true)
|
|
112
135
|
* @returns Resolved auto-memory enabled state
|
|
113
136
|
*/
|
|
114
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;
|
|
115
146
|
/**
|
|
116
147
|
* Resolves worktree base ref with fallbacks
|
|
117
148
|
* Resolution priority: settings.json > default ("fresh")
|
|
@@ -120,7 +151,7 @@ export declare class ConfigurationService {
|
|
|
120
151
|
resolveWorktreeBaseRef(): "fresh" | "head";
|
|
121
152
|
/**
|
|
122
153
|
* Resolves auto-memory extraction frequency with fallbacks
|
|
123
|
-
* Resolution priority: session options (
|
|
154
|
+
* Resolution priority: session options (程序化会话级覆盖) > settings.json > WAVE_AUTO_MEMORY_FREQUENCY > default (1)
|
|
124
155
|
* @returns Resolved auto-memory extraction frequency (turns)
|
|
125
156
|
*/
|
|
126
157
|
resolveAutoMemoryFrequency(): number;
|
|
@@ -132,9 +163,13 @@ export declare class ConfigurationService {
|
|
|
132
163
|
*/
|
|
133
164
|
resolveMaxOutputTokens(constructorLimit?: number): number;
|
|
134
165
|
/**
|
|
135
|
-
* 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.
|
|
136
171
|
*/
|
|
137
|
-
setModel(model: string): void
|
|
172
|
+
setModel(model: string): Promise<void>;
|
|
138
173
|
private persistModelToSettings;
|
|
139
174
|
/**
|
|
140
175
|
* Get all configured models from settings.json and environment
|
|
@@ -184,9 +219,16 @@ export declare class ConfigurationService {
|
|
|
184
219
|
*/
|
|
185
220
|
deleteHook(workdir: string, scope: Scope, hookName: string): Promise<void>;
|
|
186
221
|
/**
|
|
187
|
-
* Get merged marketplaces from all scopes
|
|
222
|
+
* Get merged marketplaces from all scopes, with admin-managed (remote) entries
|
|
223
|
+
* taking precedence per key (spec enterprise server-managed-config「托管配置
|
|
224
|
+
* 下发插件市场与启用列表」场景 5).
|
|
188
225
|
*/
|
|
189
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;
|
|
190
232
|
/**
|
|
191
233
|
* Get marketplaces at a specific scope
|
|
192
234
|
*/
|
|
@@ -204,9 +246,18 @@ export declare class ConfigurationService {
|
|
|
204
246
|
*/
|
|
205
247
|
removeEnabledPlugin(workdir: string, scope: Scope, pluginId: string): Promise<void>;
|
|
206
248
|
/**
|
|
207
|
-
* 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.
|
|
208
253
|
*/
|
|
209
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;
|
|
210
261
|
/**
|
|
211
262
|
* Load Wave configuration from a JSON file
|
|
212
263
|
* Supports both hooks and environment variables with proper validation
|