@99percentpeople/pi-codex-api 0.1.2 → 0.1.3

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/search-display.ts DELETED
@@ -1,374 +0,0 @@
1
- export interface CodexSearchSource {
2
- type?: string;
3
- refId?: string;
4
- title: string;
5
- domain?: string;
6
- url?: string;
7
- snippet?: string;
8
- }
9
-
10
- export interface CodexSearchDocument {
11
- source?: CodexSearchSource;
12
- body: string;
13
- }
14
-
15
- export type CodexSearchDisplay =
16
- | { kind: "sources"; sources: CodexSearchSource[] }
17
- | {
18
- kind: "document";
19
- source?: CodexSearchSource;
20
- body: string;
21
- /** One entry per open/click/find/screenshot result block. */
22
- documents?: CodexSearchDocument[];
23
- }
24
- | { kind: "data"; body: string };
25
-
26
- export type CodexSearchDisplayLineRole = "title" | "url" | "body" | "hint" | "error";
27
-
28
- export interface CodexSearchDisplayLine {
29
- role: CodexSearchDisplayLineRole;
30
- text: string;
31
- /** Styled keyHint kept separate so renderers do not recolor it. */
32
- expandHint?: string;
33
- }
34
-
35
- const SOURCE_PREVIEW_COUNT = 3;
36
- const DOCUMENT_PREVIEW_LINES = 10;
37
- const MULTI_DOCUMENT_PREVIEW_LINES = 5;
38
- const RESULT_SEPARATOR = /\s*-{40,}\s*/;
39
- const CITATION_MARKER = /cite[^]*/g;
40
- const WORD_LIMIT = /\[wordlim:\s*[^\]]+\]/gi;
41
- const SEARCH_METADATA = /^(?:(?:Published|Crawled):\s*[^;]+;\s*)+/i;
42
- const URL_DECODE_PASSES = 12;
43
-
44
- function record(value: unknown): Record<string, unknown> | undefined {
45
- return value && typeof value === "object" && !Array.isArray(value)
46
- ? value as Record<string, unknown>
47
- : undefined;
48
- }
49
-
50
- function stringField(value: Record<string, unknown>, ...names: string[]): string | undefined {
51
- for (const name of names) {
52
- const field = value[name];
53
- if (typeof field === "string" && field.trim()) return field.trim();
54
- }
55
- return undefined;
56
- }
57
-
58
- function cleanInline(value: string): string {
59
- return value
60
- .replace(CITATION_MARKER, "")
61
- .replace(WORD_LIMIT, "")
62
- .trim()
63
- .replace(SEARCH_METADATA, "")
64
- .replace(/^#{1,6}\s+/, "")
65
- .replace(/\s+/g, " ")
66
- .trim();
67
- }
68
-
69
- function decodeRepeatedUrlEncoding(value: string): string {
70
- let decoded = value;
71
- for (let pass = 0; pass < URL_DECODE_PASSES; pass += 1) {
72
- try {
73
- const next = decodeURIComponent(decoded);
74
- if (next === decoded) break;
75
- decoded = next;
76
- } catch {
77
- break;
78
- }
79
- }
80
- return decoded;
81
- }
82
-
83
- function safeUrl(value: string | undefined): string | undefined {
84
- if (!value) return undefined;
85
- try {
86
- // The search service can return duplicate URLs with their percent escapes
87
- // encoded many times. Canonicalize for display and duplicate detection;
88
- // this never changes the raw ToolResult passed to the model.
89
- const url = new URL(decodeRepeatedUrlEncoding(value));
90
- return url.protocol === "https:" || url.protocol === "http:" ? url.toString() : undefined;
91
- } catch {
92
- return undefined;
93
- }
94
- }
95
-
96
- function domainFor(url: string | undefined): string | undefined {
97
- if (!url) return undefined;
98
- try {
99
- return new URL(url).hostname;
100
- } catch {
101
- return undefined;
102
- }
103
- }
104
-
105
- function normalizeSource(value: unknown): CodexSearchSource | undefined {
106
- const item = record(value);
107
- if (!item) return undefined;
108
- const url = safeUrl(stringField(item, "url", "source_url", "sourceUrl", "page_url", "pageUrl"));
109
- const domain = stringField(item, "domain", "source_domain", "sourceDomain") ?? domainFor(url);
110
- const title = cleanInline(stringField(item, "title", "name", "caption") ?? domain ?? url ?? "Search result");
111
- const snippetValue = stringField(item, "snippet", "description", "text", "content");
112
- const cleanedSnippet = snippetValue ? cleanInline(snippetValue) : undefined;
113
- let snippet = cleanedSnippet && !/^Image:/i.test(cleanedSnippet) ? cleanedSnippet : undefined;
114
- if (snippet === title) snippet = undefined;
115
- else if (snippet?.startsWith(title)) {
116
- snippet = snippet.slice(title.length).replace(/^[\s.…:|—-]+/, "").trim() || undefined;
117
- }
118
- const refId = stringField(item, "ref_id", "refId");
119
- const type = stringField(item, "type");
120
- if (!url && !domain && !snippet && !refId) return undefined;
121
- return { type, refId, title, domain, url, snippet };
122
- }
123
-
124
- function rawSourceBlocks(output: string): CodexSearchSource[] {
125
- const sources: CodexSearchSource[] = [];
126
- for (const block of output.split(RESULT_SEPARATOR)) {
127
- const lines = block.split("\n").map((line) => line.trim()).filter(Boolean);
128
- if (lines.length === 0) continue;
129
- const heading = /^(.*?)\s+\((https?:\/\/[^\s)]+)\)\s*$/.exec(lines[0]);
130
- if (!heading) continue;
131
- const title = cleanInline(heading[1]);
132
- const url = safeUrl(heading[2]);
133
- const candidates = lines.slice(1)
134
- .map(cleanInline)
135
- .filter((line) => line && !/^Image:/i.test(line) && !/^\d+$/.test(line));
136
- const snippet = candidates.find((line) => line !== title && line.length >= 20);
137
- sources.push({ title, url, domain: domainFor(url), snippet });
138
- }
139
- return sources;
140
- }
141
-
142
- function removeDocumentLinePrefix(line: string): string {
143
- return line.replace(/^(?:L\d+:\s*)+/, "").trim();
144
- }
145
-
146
- function isDocumentChrome(line: string): boolean {
147
- return /^\*?\s*\[(?:Button|Input)(?::[^\]]*)?\]\s*$/i.test(line)
148
- || /^(?:\*\s*)+$/.test(line)
149
- || /^(?:\*\s*)?(?:L\d+:\s*)+$/.test(line);
150
- }
151
-
152
- function cleanDocumentLine(line: string): string {
153
- const cleaned = cleanInline(removeDocumentLinePrefix(line));
154
- return cleanInline(cleaned.replace(/(?:^|\s)L\d+:\s*/g, " "));
155
- }
156
-
157
- export function cleanCodexSearchOutput(output: string): string {
158
- const lines = output
159
- .split(RESULT_SEPARATOR)
160
- .join("\n\n")
161
- .split("\n")
162
- .map(cleanDocumentLine)
163
- .filter((line) => line && !/^Image:/i.test(line) && !isDocumentChrome(line));
164
- return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
165
- }
166
-
167
- function cleanCodexDocumentOutput(output: string): string {
168
- let lines = output.split(RESULT_SEPARATOR).join("\n\n").split("\n");
169
- const firstHeading = lines.findIndex((line) => /^#{1,6}\s+/.test(removeDocumentLinePrefix(line)));
170
- if (firstHeading >= 0 && firstHeading <= 30) lines = lines.slice(firstHeading);
171
- return cleanCodexSearchOutput(lines.join("\n"));
172
- }
173
-
174
- function uniqueSources(results: unknown[] | undefined, output: string): CodexSearchSource[] {
175
- const candidates = (results ?? []).map(normalizeSource).filter((value): value is CodexSearchSource => value !== undefined);
176
- const sources = candidates.length > 0 ? candidates : rawSourceBlocks(output);
177
- const seen = new Set<string>();
178
- return sources.filter((source) => {
179
- const key = source.url ?? source.refId ?? `${source.title}\n${source.snippet ?? ""}`;
180
- if (seen.has(key)) return false;
181
- seen.add(key);
182
- return true;
183
- });
184
- }
185
-
186
- function hasItems(value: unknown): boolean {
187
- return Array.isArray(value) && value.length > 0;
188
- }
189
-
190
- function documentSourceFromBlock(block: string): CodexSearchSource | undefined {
191
- const first = block.split("\n").map((line) => line.trim()).find(Boolean);
192
- if (!first) return undefined;
193
- const heading = /^(.*?)\s+\((https?:\/\/[^)]*)?\)\s*$/.exec(first);
194
- if (!heading) return undefined;
195
- const title = cleanInline(heading[1]);
196
- if (!title) return undefined;
197
- const url = safeUrl(heading[2]);
198
- return {
199
- ...(/^Internal Error$/i.test(title) ? { type: "error" } : {}),
200
- title,
201
- ...(url ? { domain: domainFor(url), url } : {}),
202
- };
203
- }
204
-
205
- function mergeDocumentSource(
206
- blockSource: CodexSearchSource | undefined,
207
- resultSources: CodexSearchSource[],
208
- index: number,
209
- ): CodexSearchSource | undefined {
210
- if (!blockSource) return resultSources[index];
211
- const matched = resultSources.find((source) =>
212
- (blockSource.url !== undefined && source.url === blockSource.url)
213
- || source.title === blockSource.title
214
- );
215
- if (!matched) return blockSource;
216
- return {
217
- ...blockSource,
218
- ...matched,
219
- type: blockSource.type ?? matched.type,
220
- title: matched.title || blockSource.title,
221
- domain: matched.domain ?? blockSource.domain,
222
- url: matched.url ?? blockSource.url,
223
- };
224
- }
225
-
226
- function documentBody(output: string, source: CodexSearchSource | undefined): string {
227
- const lines = cleanCodexDocumentOutput(output).split("\n");
228
- if (source) {
229
- while (lines.length > 0) {
230
- const first = lines[0];
231
- const headingText = first.replace(/\s+\([^)]*\)\s*$/, "");
232
- const isHeading = first === source.title
233
- || headingText === source.title
234
- || (source.url !== undefined && first.includes(source.url))
235
- || (source.domain !== undefined && first === source.domain);
236
- if (!isHeading) break;
237
- lines.shift();
238
- }
239
- }
240
- return lines.filter((line, index) => line !== lines[index - 1]).join("\n").trim();
241
- }
242
-
243
- function searchDocuments(
244
- output: string,
245
- results: unknown[] | undefined,
246
- ): CodexSearchDocument[] {
247
- const resultSources = (results ?? [])
248
- .map(normalizeSource)
249
- .filter((value): value is CodexSearchSource => value !== undefined);
250
- const blocks = output.split(RESULT_SEPARATOR).map((block) => block.trim()).filter(Boolean);
251
- const effectiveBlocks = blocks.length > 0 ? blocks : [output];
252
- return effectiveBlocks.map((block, index) => {
253
- const source = mergeDocumentSource(documentSourceFromBlock(block), resultSources, index);
254
- return { source, body: documentBody(block, source) };
255
- });
256
- }
257
-
258
- export function createCodexSearchDisplay(
259
- params: Record<string, unknown>,
260
- output: string,
261
- results?: unknown[],
262
- ): CodexSearchDisplay {
263
- const sources = uniqueSources(results, output);
264
- if ((hasItems(params.search_query) || hasItems(params.image_query)) && sources.length > 0) {
265
- return { kind: "sources", sources };
266
- }
267
- if (hasItems(params.open) || hasItems(params.click) || hasItems(params.find) || hasItems(params.screenshot)) {
268
- const documents = searchDocuments(output, results);
269
- const first = documents[0] ?? { source: sources[0], body: documentBody(output, sources[0]) };
270
- return {
271
- kind: "document",
272
- source: first.source,
273
- body: first.body,
274
- documents,
275
- };
276
- }
277
- return { kind: "data", body: cleanCodexSearchOutput(output) };
278
- }
279
-
280
- function sourceLines(source: CodexSearchSource, index: number, expanded: boolean): CodexSearchDisplayLine[] {
281
- const location = expanded ? source.url ?? source.domain : source.domain ?? source.url;
282
- const lines: CodexSearchDisplayLine[] = [{ role: "title", text: `${index + 1}. ${source.title}` }];
283
- if (location) lines.push({ role: "url", text: ` ${location}` });
284
- if (source.snippet) {
285
- const snippet = !expanded && source.snippet.length > 110
286
- ? `${source.snippet.slice(0, 109).trimEnd()}…`
287
- : source.snippet;
288
- lines.push({ role: "body", text: ` ${snippet}` });
289
- }
290
- return lines;
291
- }
292
-
293
- function expandHintLine(text: string, expandHint?: string): CodexSearchDisplayLine {
294
- return {
295
- role: "hint",
296
- text: expandHint ? `${text} (${expandHint})` : text,
297
- ...(expandHint ? { expandHint } : {}),
298
- };
299
- }
300
-
301
- function excerptLines(body: string, expanded: boolean, expandHint?: string): CodexSearchDisplayLine[] {
302
- const all = body.split("\n").filter(Boolean);
303
- const shown = expanded ? all : all.slice(0, DOCUMENT_PREVIEW_LINES);
304
- const lines: CodexSearchDisplayLine[] = shown.map((text) => ({ role: "body", text }));
305
- if (!expanded && shown.length < all.length) {
306
- lines.push(expandHintLine(`… ${all.length - shown.length} more lines`, expandHint));
307
- }
308
- return lines;
309
- }
310
-
311
- function documentLines(
312
- documents: CodexSearchDocument[],
313
- expanded: boolean,
314
- expandHint?: string,
315
- ): CodexSearchDisplayLine[] {
316
- const multiple = documents.length > 1;
317
- const previewLines = multiple ? MULTI_DOCUMENT_PREVIEW_LINES : DOCUMENT_PREVIEW_LINES;
318
- const lines: CodexSearchDisplayLine[] = [];
319
- let hiddenLineCount = 0;
320
-
321
- documents.forEach((document, index) => {
322
- if (document.source) {
323
- const title = multiple ? `${index + 1}. ${document.source.title}` : document.source.title;
324
- lines.push({
325
- role: document.source.type === "error" ? "error" : "title",
326
- text: title,
327
- });
328
- const location = expanded
329
- ? document.source.url ?? document.source.domain
330
- : document.source.domain ?? document.source.url;
331
- if (location) lines.push({ role: "url", text: ` ${location}` });
332
- }
333
-
334
- const allBodyLines = document.body.split("\n").filter(Boolean);
335
- const shownBodyLines = expanded ? allBodyLines : allBodyLines.slice(0, previewLines);
336
- lines.push(...shownBodyLines.map((text) => ({
337
- role: "body" as const,
338
- text: ` ${text}`,
339
- })));
340
- hiddenLineCount += allBodyLines.length - shownBodyLines.length;
341
- });
342
-
343
- if (!expanded && hiddenLineCount > 0) {
344
- const scope = multiple ? ` across ${documents.length} results` : "";
345
- lines.push(expandHintLine(`… ${hiddenLineCount} more lines${scope}`, expandHint));
346
- }
347
- return lines;
348
- }
349
-
350
- export function formatCodexSearchDisplay(
351
- display: CodexSearchDisplay,
352
- expanded: boolean,
353
- expandHint?: string,
354
- ): CodexSearchDisplayLine[] {
355
- if (display.kind === "sources") {
356
- const shown = expanded ? display.sources : display.sources.slice(0, SOURCE_PREVIEW_COUNT);
357
- const lines: CodexSearchDisplayLine[] = [];
358
- shown.forEach((source, index) => lines.push(...sourceLines(source, index, expanded)));
359
- if (!expanded && shown.length < display.sources.length) {
360
- lines.push(expandHintLine(`… ${display.sources.length - shown.length} more results`, expandHint));
361
- }
362
- return lines;
363
- }
364
-
365
- if (display.kind === "document") {
366
- return documentLines(
367
- display.documents ?? [{ source: display.source, body: display.body }],
368
- expanded,
369
- expandHint,
370
- );
371
- }
372
-
373
- return excerptLines(display.body, expanded, expandHint);
374
- }