@makefinks/daemon 0.8.0 → 0.9.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/package.json +1 -1
- package/src/ai/system-prompt.ts +47 -41
- package/src/ai/tools/fetch-urls.ts +153 -125
- package/src/components/SettingsMenu.tsx +36 -27
- package/src/components/UrlMenu.tsx +2 -7
- package/src/components/tool-layouts/layouts/subagent.tsx +16 -0
- package/src/components/tool-layouts/layouts/url-tools.ts +142 -80
- package/src/hooks/keyboard-handlers.ts +22 -31
- package/src/types/index.ts +0 -1
- package/src/utils/derive-url-menu-items.ts +197 -37
- package/src/utils/preferences.ts +3 -0
- package/src/utils/tool-output-preview.ts +111 -27
|
@@ -8,6 +8,10 @@ function normalizeWhitespace(text: string): string {
|
|
|
8
8
|
return text.replace(/\r\n/g, "\n").replace(/\t/g, " ");
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
function escapeXmlAttribute(value: string): string {
|
|
12
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
function truncateText(text: string, maxChars: number): { text: string; truncated: boolean } {
|
|
12
16
|
if (text.length <= maxChars) return { text, truncated: false };
|
|
13
17
|
if (maxChars <= 1) return { text: "…", truncated: true };
|
|
@@ -105,43 +109,123 @@ function formatExaSearchResult(result: unknown): string | null {
|
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
function formatExaFetchResult(result: unknown): string | null {
|
|
112
|
+
if (typeof result === "string") {
|
|
113
|
+
const trimmed = result.trim();
|
|
114
|
+
if (!trimmed) return null;
|
|
115
|
+
if (trimmed.startsWith("<fetchUrls") && trimmed.includes("error=") && !trimmed.includes("</fetchUrls>")) {
|
|
116
|
+
return trimmed;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const rawLines = trimmed
|
|
120
|
+
.split("\n")
|
|
121
|
+
.map((line) => line.trimEnd())
|
|
122
|
+
.filter((line) => line.trim().length > 0);
|
|
123
|
+
const hasFetchHeader = rawLines.some((line) => line.includes("<fetchUrls"));
|
|
124
|
+
if (!hasFetchHeader) return trimmed;
|
|
125
|
+
|
|
126
|
+
const headerLine = rawLines.find((line) => line.includes("<fetchUrls"));
|
|
127
|
+
const closingLine = rawLines.find((line) => line.includes("</fetchUrls>"));
|
|
128
|
+
const urlLines = rawLines.filter((line) => line.includes("<url"));
|
|
129
|
+
const errorLines = urlLines.filter((line) => line.includes("error="));
|
|
130
|
+
const normalLines = urlLines.filter((line) => !line.includes("error="));
|
|
131
|
+
|
|
132
|
+
let contentLine: string | undefined;
|
|
133
|
+
if (normalLines.length === 1) {
|
|
134
|
+
const firstUrlIdx = rawLines.findIndex((line) => line.includes("<url") && line.includes("href="));
|
|
135
|
+
if (firstUrlIdx >= 0) {
|
|
136
|
+
contentLine = rawLines
|
|
137
|
+
.slice(firstUrlIdx + 1)
|
|
138
|
+
.find(
|
|
139
|
+
(line) => !line.includes("</url>") && !line.includes("<url") && !line.includes("</fetchUrls>")
|
|
140
|
+
)
|
|
141
|
+
?.trim();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const lines: string[] = [];
|
|
146
|
+
if (headerLine) lines.push(headerLine);
|
|
147
|
+
if (normalLines[0]) lines.push(normalLines[0]);
|
|
148
|
+
|
|
149
|
+
if (errorLines.length > 0) {
|
|
150
|
+
lines.push(errorLines[0]);
|
|
151
|
+
} else if (contentLine) {
|
|
152
|
+
lines.push(contentLine);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (contentLine && errorLines.length > 0 && lines.length < 4) {
|
|
156
|
+
lines.push(contentLine);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (closingLine && lines.length < 4) {
|
|
160
|
+
lines.push(closingLine);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return lines.length > 0 ? lines.join("\n") : trimmed;
|
|
164
|
+
}
|
|
108
165
|
if (!isRecord(result)) return null;
|
|
109
166
|
if (result.success === false && typeof result.error === "string") {
|
|
110
167
|
return `error: ${result.error}`;
|
|
111
168
|
}
|
|
112
169
|
if (result.success !== true) return null;
|
|
113
170
|
const data = extractToolDataContainer(result);
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
171
|
+
const container = isRecord(data) ? data : {};
|
|
172
|
+
const results = Array.isArray(container.results) ? container.results : null;
|
|
173
|
+
if (!results) return null;
|
|
174
|
+
|
|
175
|
+
const lines: string[] = ["<fetchUrls>"];
|
|
176
|
+
const maxItems = 3;
|
|
177
|
+
const MAX_LINES = 2;
|
|
178
|
+
const MAX_CHARS = 160;
|
|
179
|
+
|
|
180
|
+
for (const item of results.slice(0, maxItems)) {
|
|
181
|
+
if (!isRecord(item)) continue;
|
|
182
|
+
|
|
183
|
+
const candidate = item as ExaLikeItem & {
|
|
184
|
+
remainingLines?: unknown;
|
|
185
|
+
totalLines?: unknown;
|
|
186
|
+
error?: unknown;
|
|
187
|
+
};
|
|
188
|
+
const url = typeof candidate.url === "string" ? candidate.url : "";
|
|
189
|
+
if (!url) continue;
|
|
190
|
+
|
|
191
|
+
const attributes: string[] = [`href="${escapeXmlAttribute(url)}"`];
|
|
192
|
+
if (typeof candidate.lineOffset === "number") attributes.push(`lineOffset="${candidate.lineOffset}"`);
|
|
193
|
+
if (typeof candidate.lineLimit === "number") attributes.push(`lineLimit="${candidate.lineLimit}"`);
|
|
194
|
+
if (typeof candidate.totalLines === "number") attributes.push(`totalLines="${candidate.totalLines}"`);
|
|
195
|
+
if (typeof candidate.remainingLines === "number") {
|
|
196
|
+
attributes.push(`remainingLines="${candidate.remainingLines}"`);
|
|
197
|
+
} else if (candidate.remainingLines === null) {
|
|
198
|
+
attributes.push(`remainingLines="unknown"`);
|
|
199
|
+
}
|
|
132
200
|
|
|
133
|
-
|
|
134
|
-
|
|
201
|
+
if (candidate.success === false && typeof candidate.error === "string") {
|
|
202
|
+
attributes.push(`error="${escapeXmlAttribute(candidate.error)}"`);
|
|
203
|
+
lines.push(` <url ${attributes.join(" ")} />`);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
135
206
|
|
|
136
|
-
|
|
137
|
-
|
|
207
|
+
const text = typeof candidate.text === "string" ? candidate.text : "";
|
|
208
|
+
if (!text.trim()) {
|
|
209
|
+
lines.push(` <url ${attributes.join(" ")} />`);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
138
212
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
213
|
+
const snippetLines = normalizeWhitespace(text)
|
|
214
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
215
|
+
.trim()
|
|
216
|
+
.split("\n")
|
|
217
|
+
.slice(0, MAX_LINES)
|
|
218
|
+
.map((l) => (l.length > MAX_CHARS ? `${l.slice(0, MAX_CHARS - 1)}…` : l));
|
|
143
219
|
|
|
144
|
-
|
|
220
|
+
lines.push(` <url ${attributes.join(" ")}>`);
|
|
221
|
+
for (const line of snippetLines) {
|
|
222
|
+
lines.push(` ${escapeXmlAttribute(line)}`);
|
|
223
|
+
}
|
|
224
|
+
lines.push(" </url>");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
lines.push("</fetchUrls>");
|
|
228
|
+
return lines.length > 2 ? lines.join("\n") : null;
|
|
145
229
|
}
|
|
146
230
|
|
|
147
231
|
function formatRenderUrlResult(result: unknown): string | null {
|