@ahkohd/yagami 0.1.2
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/.beads/.beads-credential-key +1 -0
- package/.beads/README.md +81 -0
- package/.beads/config.yaml +54 -0
- package/.beads/hooks/post-checkout +24 -0
- package/.beads/hooks/post-merge +24 -0
- package/.beads/hooks/pre-commit +24 -0
- package/.beads/hooks/pre-push +24 -0
- package/.beads/hooks/prepare-commit-msg +24 -0
- package/.beads/metadata.json +7 -0
- package/.github/workflows/ci.yml +43 -0
- package/.github/workflows/release.yml +115 -0
- package/AGENTS.md +150 -0
- package/README.md +210 -0
- package/biome.json +36 -0
- package/config/mcporter.json +8 -0
- package/dist/cli/theme.js +202 -0
- package/dist/cli/theme.js.map +1 -0
- package/dist/cli.js +1883 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +223 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon.js +745 -0
- package/dist/daemon.js.map +1 -0
- package/dist/engine/constants.js +131 -0
- package/dist/engine/constants.js.map +1 -0
- package/dist/engine/deep-research.js +167 -0
- package/dist/engine/deep-research.js.map +1 -0
- package/dist/engine/defuddle-utils.js +57 -0
- package/dist/engine/defuddle-utils.js.map +1 -0
- package/dist/engine/github-fetch.js +232 -0
- package/dist/engine/github-fetch.js.map +1 -0
- package/dist/engine/helpers.js +372 -0
- package/dist/engine/helpers.js.map +1 -0
- package/dist/engine/limiter.js +75 -0
- package/dist/engine/limiter.js.map +1 -0
- package/dist/engine/policy.js +313 -0
- package/dist/engine/policy.js.map +1 -0
- package/dist/engine/runtime-utils.js +65 -0
- package/dist/engine/runtime-utils.js.map +1 -0
- package/dist/engine/search-discovery.js +275 -0
- package/dist/engine/search-discovery.js.map +1 -0
- package/dist/engine/url-utils.js +72 -0
- package/dist/engine/url-utils.js.map +1 -0
- package/dist/engine.js +2030 -0
- package/dist/engine.js.map +1 -0
- package/dist/mcp.js +282 -0
- package/dist/mcp.js.map +1 -0
- package/dist/types/cli.js +2 -0
- package/dist/types/cli.js.map +1 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/daemon.js +2 -0
- package/dist/types/daemon.js.map +1 -0
- package/dist/types/engine.js +2 -0
- package/dist/types/engine.js.map +1 -0
- package/package.json +66 -0
- package/packages/pi-yagami-search/README.md +39 -0
- package/packages/pi-yagami-search/extensions/yagami-search.ts +273 -0
- package/packages/pi-yagami-search/package.json +41 -0
- package/src/cli/theme.ts +260 -0
- package/src/cli.ts +2226 -0
- package/src/config.ts +250 -0
- package/src/daemon.ts +990 -0
- package/src/engine/constants.ts +147 -0
- package/src/engine/deep-research.ts +207 -0
- package/src/engine/defuddle-utils.ts +75 -0
- package/src/engine/github-fetch.ts +265 -0
- package/src/engine/helpers.ts +394 -0
- package/src/engine/limiter.ts +97 -0
- package/src/engine/policy.ts +392 -0
- package/src/engine/runtime-utils.ts +79 -0
- package/src/engine/search-discovery.ts +351 -0
- package/src/engine/url-utils.ts +86 -0
- package/src/engine.ts +2516 -0
- package/src/mcp.ts +337 -0
- package/src/shims-cli.d.ts +3 -0
- package/src/types/cli.ts +7 -0
- package/src/types/config.ts +53 -0
- package/src/types/daemon.ts +22 -0
- package/src/types/engine.ts +194 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import { CODE_PREFERRED_DOMAINS, COMPANY_PREFERRED_DOMAINS } from "./constants.js";
|
|
2
|
+
import {
|
|
3
|
+
clampInteger,
|
|
4
|
+
extractSeedUrls,
|
|
5
|
+
getCompanyCountryProfile,
|
|
6
|
+
getHostname,
|
|
7
|
+
normalizeCountryCode,
|
|
8
|
+
normalizeDomainFilter,
|
|
9
|
+
normalizeEnum,
|
|
10
|
+
parseIsoDate,
|
|
11
|
+
parseStringList,
|
|
12
|
+
parseUrlList,
|
|
13
|
+
} from "./helpers.js";
|
|
14
|
+
import type {
|
|
15
|
+
EngineResearchConfig,
|
|
16
|
+
NormalizedResearchPolicy,
|
|
17
|
+
RawResearchPolicy,
|
|
18
|
+
ResearchMode,
|
|
19
|
+
ResearchPlan,
|
|
20
|
+
} from "../types/engine.js";
|
|
21
|
+
|
|
22
|
+
const RESEARCH_MODES: readonly ResearchMode[] = ["general", "code", "company", "similar", "deep"];
|
|
23
|
+
const RETRIEVAL_TYPES = ["auto", "fast", "neural"] as const;
|
|
24
|
+
const LIVECRAWL_MODES = ["never", "fallback", "always", "preferred"] as const;
|
|
25
|
+
|
|
26
|
+
export function normalizeResearchPolicy(rawPolicy: RawResearchPolicy = {}): NormalizedResearchPolicy {
|
|
27
|
+
const explicitIncludeDomains = parseStringList(rawPolicy.includeDomains)
|
|
28
|
+
.map((value) => normalizeDomainFilter(value))
|
|
29
|
+
.filter(Boolean);
|
|
30
|
+
|
|
31
|
+
const siteDomains = parseStringList(rawPolicy.sites)
|
|
32
|
+
.map((value) => normalizeDomainFilter(value))
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
|
|
35
|
+
const includeDomains = Array.from(new Set([...explicitIncludeDomains, ...siteDomains]));
|
|
36
|
+
|
|
37
|
+
const excludeDomains = parseStringList(rawPolicy.excludeDomains)
|
|
38
|
+
.map((value) => normalizeDomainFilter(value))
|
|
39
|
+
.filter(Boolean);
|
|
40
|
+
|
|
41
|
+
const includeText = parseStringList(rawPolicy.includeText)
|
|
42
|
+
.map((value) => String(value).toLowerCase().trim())
|
|
43
|
+
.filter(Boolean);
|
|
44
|
+
|
|
45
|
+
const excludeText = parseStringList(rawPolicy.excludeText)
|
|
46
|
+
.map((value) => String(value).toLowerCase().trim())
|
|
47
|
+
.filter(Boolean);
|
|
48
|
+
|
|
49
|
+
const categoryRaw = String(rawPolicy.category ?? "")
|
|
50
|
+
.trim()
|
|
51
|
+
.toLowerCase();
|
|
52
|
+
const category = categoryRaw || null;
|
|
53
|
+
|
|
54
|
+
const mode = normalizeEnum(rawPolicy.mode, RESEARCH_MODES, "general");
|
|
55
|
+
|
|
56
|
+
const country = normalizeCountryCode(rawPolicy.country);
|
|
57
|
+
const countryProfile = mode === "company" ? getCompanyCountryProfile(country) : null;
|
|
58
|
+
|
|
59
|
+
const preferredDomainsRaw = parseStringList(rawPolicy.preferredDomains)
|
|
60
|
+
.map((value) => normalizeDomainFilter(value))
|
|
61
|
+
.filter(Boolean);
|
|
62
|
+
|
|
63
|
+
const preferredDomains =
|
|
64
|
+
mode === "code"
|
|
65
|
+
? Array.from(new Set([...preferredDomainsRaw, ...CODE_PREFERRED_DOMAINS]))
|
|
66
|
+
: mode === "company"
|
|
67
|
+
? Array.from(
|
|
68
|
+
new Set([...preferredDomainsRaw, ...COMPANY_PREFERRED_DOMAINS, ...(countryProfile?.domains ?? [])]),
|
|
69
|
+
)
|
|
70
|
+
: preferredDomainsRaw;
|
|
71
|
+
|
|
72
|
+
const explicitSeedUrls = parseUrlList(rawPolicy.seedUrls);
|
|
73
|
+
const countrySeedUrls =
|
|
74
|
+
mode === "company" && countryProfile
|
|
75
|
+
? parseUrlList(countryProfile.seedUrls?.(String(rawPolicy.companyName ?? rawPolicy.query ?? "").trim()) ?? [])
|
|
76
|
+
: [];
|
|
77
|
+
|
|
78
|
+
const seedUrls = Array.from(new Set([...explicitSeedUrls, ...countrySeedUrls]));
|
|
79
|
+
|
|
80
|
+
const customInstruction =
|
|
81
|
+
String(rawPolicy.customInstruction ?? rawPolicy.instructions ?? rawPolicy.instruction ?? "").trim() || null;
|
|
82
|
+
|
|
83
|
+
const type = normalizeEnum(rawPolicy.type, RETRIEVAL_TYPES, "auto");
|
|
84
|
+
const livecrawl = normalizeEnum(rawPolicy.livecrawl, LIVECRAWL_MODES, "fallback");
|
|
85
|
+
|
|
86
|
+
const requestedResults = clampInteger(rawPolicy.numResults, 0, { min: 0, max: 40 });
|
|
87
|
+
const requestedMaxPages = requestedResults > 0 ? requestedResults : null;
|
|
88
|
+
const requestedHops = clampInteger(rawPolicy.maxHops, 0, { min: 0, max: 8 });
|
|
89
|
+
const requestedMaxHops = requestedHops > 0 ? requestedHops : null;
|
|
90
|
+
|
|
91
|
+
const startPublishedDate = String(rawPolicy.startPublishedDate ?? "").trim() || null;
|
|
92
|
+
const endPublishedDate = String(rawPolicy.endPublishedDate ?? "").trim() || null;
|
|
93
|
+
|
|
94
|
+
const startDate = parseIsoDate(startPublishedDate);
|
|
95
|
+
const endDate = parseIsoDate(endPublishedDate);
|
|
96
|
+
|
|
97
|
+
const advanced =
|
|
98
|
+
requestedMaxPages !== null ||
|
|
99
|
+
requestedMaxHops !== null ||
|
|
100
|
+
includeDomains.length > 0 ||
|
|
101
|
+
excludeDomains.length > 0 ||
|
|
102
|
+
includeText.length > 0 ||
|
|
103
|
+
excludeText.length > 0 ||
|
|
104
|
+
Boolean(category) ||
|
|
105
|
+
Boolean(country) ||
|
|
106
|
+
Boolean(startPublishedDate) ||
|
|
107
|
+
Boolean(endPublishedDate) ||
|
|
108
|
+
Boolean(customInstruction) ||
|
|
109
|
+
preferredDomains.length > 0 ||
|
|
110
|
+
seedUrls.length > 0 ||
|
|
111
|
+
mode !== "general" ||
|
|
112
|
+
type !== "auto" ||
|
|
113
|
+
livecrawl !== "fallback";
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
advanced,
|
|
117
|
+
mode,
|
|
118
|
+
type,
|
|
119
|
+
livecrawl,
|
|
120
|
+
category,
|
|
121
|
+
country,
|
|
122
|
+
countryLabel: countryProfile?.label ?? null,
|
|
123
|
+
includeDomains,
|
|
124
|
+
excludeDomains,
|
|
125
|
+
includeText,
|
|
126
|
+
excludeText,
|
|
127
|
+
preferredDomains,
|
|
128
|
+
seedUrls,
|
|
129
|
+
customInstruction,
|
|
130
|
+
requestedResults,
|
|
131
|
+
requestedMaxPages,
|
|
132
|
+
requestedMaxHops,
|
|
133
|
+
startPublishedDate,
|
|
134
|
+
endPublishedDate,
|
|
135
|
+
startDate,
|
|
136
|
+
endDate,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function deriveResearchPlan(
|
|
141
|
+
query: string,
|
|
142
|
+
config: EngineResearchConfig,
|
|
143
|
+
options: { researchPolicy?: RawResearchPolicy } = {},
|
|
144
|
+
): ResearchPlan {
|
|
145
|
+
const policy = normalizeResearchPolicy(options.researchPolicy ?? {});
|
|
146
|
+
|
|
147
|
+
const extractedSeedUrls = extractSeedUrls(query);
|
|
148
|
+
const seedUrls = Array.from(new Set([...extractedSeedUrls, ...(policy.seedUrls ?? [])]));
|
|
149
|
+
const seedHosts = new Set(seedUrls.map((url) => getHostname(url)).filter(Boolean));
|
|
150
|
+
|
|
151
|
+
let defaultMaxPages = config.researchMaxPages;
|
|
152
|
+
if (policy.mode === "code") defaultMaxPages = Math.min(defaultMaxPages, 8);
|
|
153
|
+
if (policy.mode === "company") defaultMaxPages = Math.min(defaultMaxPages, 8);
|
|
154
|
+
if (policy.mode === "similar") defaultMaxPages = Math.min(defaultMaxPages, 6);
|
|
155
|
+
|
|
156
|
+
const maxPages = Math.max(1, policy.requestedMaxPages || defaultMaxPages);
|
|
157
|
+
|
|
158
|
+
const maxHops = Math.max(0, policy.requestedMaxHops ?? config.researchMaxHops);
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
maxPages,
|
|
162
|
+
maxHops,
|
|
163
|
+
sameDomainOnly: config.researchSameDomainOnly && seedHosts.size > 0,
|
|
164
|
+
seedUrls,
|
|
165
|
+
seedHosts,
|
|
166
|
+
policy,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ---- Prompt building blocks (internal) ----
|
|
171
|
+
|
|
172
|
+
function currentDateIso(): string {
|
|
173
|
+
const now = new Date();
|
|
174
|
+
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function toolsBlock(): string {
|
|
178
|
+
return [
|
|
179
|
+
"Tools:",
|
|
180
|
+
"- browse(url) → documentId. Fetches a web page and stores it.",
|
|
181
|
+
"- present(documentId) → title, author, date, text. Extracts readable content. Always call before citing a page.",
|
|
182
|
+
].join("\n");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function groundingBlock(): string {
|
|
186
|
+
return [
|
|
187
|
+
"Grounding:",
|
|
188
|
+
"- Cite only URLs you browsed and presented. Never fabricate URLs or quotes.",
|
|
189
|
+
"- If evidence is missing or conflicting, say so.",
|
|
190
|
+
].join("\n");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function collateOutputBlock(): string {
|
|
194
|
+
return `Browse pages relevant to the query. Call present(documentId) on each page to read it.
|
|
195
|
+
Then list the URLs of the pages that best answer the query.
|
|
196
|
+
|
|
197
|
+
Output format:
|
|
198
|
+
SOURCES
|
|
199
|
+
<url>
|
|
200
|
+
<url>
|
|
201
|
+
SOURCES
|
|
202
|
+
|
|
203
|
+
At most 6 URLs. Only include pages you browsed and presented.
|
|
204
|
+
Drop junk pages (challenge walls, login screens, empty pages).
|
|
205
|
+
If no pages are relevant, output: NONE`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function deepReportOutputBlock(): string {
|
|
209
|
+
return `Browse extensively, then write a structured report from what you find.
|
|
210
|
+
|
|
211
|
+
Structure:
|
|
212
|
+
## Executive Summary
|
|
213
|
+
Brief overview and overall confidence.
|
|
214
|
+
|
|
215
|
+
## Key Findings
|
|
216
|
+
Detailed findings with confidence (high/medium/low). Cite source URLs.
|
|
217
|
+
|
|
218
|
+
## Disagreements & Unknowns
|
|
219
|
+
Conflicting evidence, unresolved questions, gaps.
|
|
220
|
+
|
|
221
|
+
## Sources
|
|
222
|
+
- Title — URL (one line per source)`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function workflowBlock(mode: ResearchMode, seedUrls: string[]): string {
|
|
226
|
+
const hasSeeds = seedUrls.length > 0;
|
|
227
|
+
const startStep = hasSeeds
|
|
228
|
+
? "Start from seed URLs, then use search discovery for additional links."
|
|
229
|
+
: "Browse the search discovery URL to find relevant links.";
|
|
230
|
+
|
|
231
|
+
switch (mode) {
|
|
232
|
+
case "code":
|
|
233
|
+
return [
|
|
234
|
+
"Workflow:",
|
|
235
|
+
`1. ${startStep}`,
|
|
236
|
+
"2. Prioritize official documentation, GitHub repos, and Stack Overflow.",
|
|
237
|
+
"3. Call present(documentId) on each page before citing.",
|
|
238
|
+
"4. Provide source-linked examples. State uncertainty about APIs explicitly.",
|
|
239
|
+
].join("\n");
|
|
240
|
+
|
|
241
|
+
case "company":
|
|
242
|
+
return [
|
|
243
|
+
"Workflow:",
|
|
244
|
+
`1. ${hasSeeds ? "Start from seed/registry URLs before general search." : startStep}`,
|
|
245
|
+
"2. Check company registries and directories before secondary blogs.",
|
|
246
|
+
"3. Call present(documentId) on each page before citing.",
|
|
247
|
+
"4. Include legal entity basics when available: name, jurisdiction, status.",
|
|
248
|
+
"5. Mark missing data explicitly.",
|
|
249
|
+
].join("\n");
|
|
250
|
+
|
|
251
|
+
case "similar":
|
|
252
|
+
return [
|
|
253
|
+
"Workflow:",
|
|
254
|
+
"1. Browse the seed URL to understand the target site's category and purpose.",
|
|
255
|
+
"2. Search for alternatives and competitors via discovery.",
|
|
256
|
+
"3. Call present(documentId) on each candidate page.",
|
|
257
|
+
"4. Return direct alternatives over generic articles or dictionary pages.",
|
|
258
|
+
"5. Skip bot-wall/challenge pages (Cloudflare, access-denied) as evidence.",
|
|
259
|
+
].join("\n");
|
|
260
|
+
|
|
261
|
+
case "deep":
|
|
262
|
+
return [
|
|
263
|
+
"Workflow:",
|
|
264
|
+
`1. ${startStep}`,
|
|
265
|
+
"2. Iterate: discover → browse → present → synthesize → gap-check → follow-up browse → final report.",
|
|
266
|
+
"3. Cross-check major claims against independent sources when available.",
|
|
267
|
+
"4. Surface disagreements between sources and note confidence impact.",
|
|
268
|
+
"5. Mark uncertainty explicitly instead of filling gaps with assumptions.",
|
|
269
|
+
].join("\n");
|
|
270
|
+
|
|
271
|
+
default:
|
|
272
|
+
return [
|
|
273
|
+
"Workflow:",
|
|
274
|
+
`1. ${startStep}`,
|
|
275
|
+
"2. Browse 3+ relevant sources when possible.",
|
|
276
|
+
"3. Call present(documentId) on each page before citing.",
|
|
277
|
+
"4. Prefer primary sources: official docs, repos, papers, first-party pages.",
|
|
278
|
+
].join("\n");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function constraintsBlock(plan: ResearchPlan, searchDiscovery?: { engine: string; template: string }): string {
|
|
283
|
+
const lines: string[] = [];
|
|
284
|
+
|
|
285
|
+
lines.push(`Today: ${currentDateIso()}`);
|
|
286
|
+
lines.push(`Page budget: ${plan.maxPages} unique pages max.`);
|
|
287
|
+
lines.push(`Hop limit: ${plan.maxHops} hops from seed pages.`);
|
|
288
|
+
|
|
289
|
+
if (searchDiscovery?.template) {
|
|
290
|
+
lines.push(`Search discovery: browse ${searchDiscovery.template} to find links.`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (plan.sameDomainOnly && plan.seedHosts.size > 0) {
|
|
294
|
+
lines.push(`Domain lock: only browse ${Array.from(plan.seedHosts).join(", ")}.`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const policy = plan.policy;
|
|
298
|
+
|
|
299
|
+
if (policy.includeDomains.length > 0) {
|
|
300
|
+
lines.push(`Allowed domains: ${policy.includeDomains.join(", ")}. Search engine domains OK for discovery only.`);
|
|
301
|
+
}
|
|
302
|
+
if (policy.excludeDomains.length > 0) {
|
|
303
|
+
lines.push(`Blocked domains: ${policy.excludeDomains.join(", ")}.`);
|
|
304
|
+
}
|
|
305
|
+
if (policy.preferredDomains.length > 0) {
|
|
306
|
+
lines.push(`Preferred sources: ${policy.preferredDomains.join(", ")}.`);
|
|
307
|
+
}
|
|
308
|
+
if (policy.includeText.length > 0) {
|
|
309
|
+
lines.push(`Required terms: evidence must mention ${policy.includeText.join(", ")}.`);
|
|
310
|
+
}
|
|
311
|
+
if (policy.excludeText.length > 0) {
|
|
312
|
+
lines.push(`Excluded terms: skip evidence mentioning ${policy.excludeText.join(", ")}.`);
|
|
313
|
+
}
|
|
314
|
+
if (policy.startPublishedDate) {
|
|
315
|
+
lines.push(`After: ${policy.startPublishedDate}.`);
|
|
316
|
+
}
|
|
317
|
+
if (policy.endPublishedDate) {
|
|
318
|
+
lines.push(`Before: ${policy.endPublishedDate}.`);
|
|
319
|
+
}
|
|
320
|
+
if (policy.category) {
|
|
321
|
+
lines.push(`Category: ${policy.category}.`);
|
|
322
|
+
}
|
|
323
|
+
if (policy.countryLabel) {
|
|
324
|
+
lines.push(`Country: ${policy.countryLabel}. Prioritize country-specific registries.`);
|
|
325
|
+
}
|
|
326
|
+
if (policy.requestedResults > 0) {
|
|
327
|
+
lines.push(`Target: ~${policy.requestedResults} sources.`);
|
|
328
|
+
}
|
|
329
|
+
if (policy.requestedMaxHops !== null) {
|
|
330
|
+
lines.push(`Depth target: ~${policy.requestedMaxHops} hops.`);
|
|
331
|
+
}
|
|
332
|
+
if (policy.customInstruction) {
|
|
333
|
+
lines.push(policy.customInstruction);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return lines.join("\n");
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function seedsBlock(plan: ResearchPlan): string {
|
|
340
|
+
if (plan.seedUrls.length === 0) return "";
|
|
341
|
+
return `Seeds:\n${plan.seedUrls.map((url) => `- ${url}`).join("\n")}`;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function reinforcementLine(mode: ResearchMode): string {
|
|
345
|
+
if (mode === "deep") {
|
|
346
|
+
return "Follow the iterative workflow. Cite only what you browsed.";
|
|
347
|
+
}
|
|
348
|
+
return "Respond with SOURCES list only.";
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ---- Public prompt builder ----
|
|
352
|
+
|
|
353
|
+
export function buildSystemPrompt(plan: ResearchPlan, searchDiscovery?: { engine: string; template: string }): string {
|
|
354
|
+
const mode = plan.policy.mode;
|
|
355
|
+
|
|
356
|
+
const outputBlock = mode === "deep" ? deepReportOutputBlock() : collateOutputBlock();
|
|
357
|
+
|
|
358
|
+
const isDeep = mode === "deep";
|
|
359
|
+
|
|
360
|
+
const sections = isDeep
|
|
361
|
+
? [
|
|
362
|
+
"You are YAGAMI, a web search agent.",
|
|
363
|
+
"",
|
|
364
|
+
outputBlock,
|
|
365
|
+
"",
|
|
366
|
+
toolsBlock(),
|
|
367
|
+
"",
|
|
368
|
+
workflowBlock(mode, plan.seedUrls),
|
|
369
|
+
"",
|
|
370
|
+
groundingBlock(),
|
|
371
|
+
"",
|
|
372
|
+
constraintsBlock(plan, searchDiscovery),
|
|
373
|
+
]
|
|
374
|
+
: [
|
|
375
|
+
"You are YAGAMI, a web search agent.",
|
|
376
|
+
"",
|
|
377
|
+
outputBlock,
|
|
378
|
+
"",
|
|
379
|
+
toolsBlock(),
|
|
380
|
+
"",
|
|
381
|
+
constraintsBlock(plan, searchDiscovery),
|
|
382
|
+
];
|
|
383
|
+
|
|
384
|
+
const seeds = seedsBlock(plan);
|
|
385
|
+
if (seeds) {
|
|
386
|
+
sections.push("", seeds);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
sections.push("", reinforcementLine(mode));
|
|
390
|
+
|
|
391
|
+
return sections.join("\n");
|
|
392
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { LlmApi } from "../types/config.js";
|
|
2
|
+
import type { AgentThinkingLevel } from "../types/engine.js";
|
|
3
|
+
|
|
4
|
+
export async function fetchJson(
|
|
5
|
+
url: string,
|
|
6
|
+
options: RequestInit = {},
|
|
7
|
+
timeoutMs = 8000,
|
|
8
|
+
): Promise<Record<string, unknown>> {
|
|
9
|
+
const controller = new AbortController();
|
|
10
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const response = await fetch(url, {
|
|
14
|
+
...options,
|
|
15
|
+
signal: controller.signal,
|
|
16
|
+
headers: {
|
|
17
|
+
"content-type": "application/json",
|
|
18
|
+
...(options.headers || {}),
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
const text = await response.text();
|
|
24
|
+
throw new Error(`HTTP ${response.status}: ${text.slice(0, 400)}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (await response.json()) as Record<string, unknown>;
|
|
28
|
+
} finally {
|
|
29
|
+
clearTimeout(timer);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function delay(ms: number): Promise<void> {
|
|
34
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function normalizeLlmApi(value: unknown): LlmApi {
|
|
38
|
+
const normalized = String(value ?? "")
|
|
39
|
+
.trim()
|
|
40
|
+
.toLowerCase();
|
|
41
|
+
|
|
42
|
+
if (normalized === "anthropic-messages") return "anthropic-messages";
|
|
43
|
+
return "openai-completions";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function resolveRuntimeApiKey(api: LlmApi, value: unknown): string {
|
|
47
|
+
const key = String(value ?? "").trim();
|
|
48
|
+
if (key) return key;
|
|
49
|
+
return api === "anthropic-messages" ? "local" : "none";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function normalizeThinkingLevel(value: unknown, fallback: AgentThinkingLevel = "off"): AgentThinkingLevel {
|
|
53
|
+
const normalized = String(value ?? "")
|
|
54
|
+
.trim()
|
|
55
|
+
.toLowerCase();
|
|
56
|
+
|
|
57
|
+
if (["off", "minimal", "low", "medium", "high", "xhigh"].includes(normalized)) {
|
|
58
|
+
return normalized as AgentThinkingLevel;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return fallback;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function joinUrl(baseUrl: string, path: string): string {
|
|
65
|
+
const left = String(baseUrl || "").replace(/\/+$/, "");
|
|
66
|
+
const right = String(path || "").replace(/^\/+/, "");
|
|
67
|
+
if (!left) return `/${right}`;
|
|
68
|
+
if (!right) return left;
|
|
69
|
+
return `${left}/${right}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function anthropicModelsUrl(baseUrl: string): string {
|
|
73
|
+
const trimmed = String(baseUrl || "").replace(/\/+$/, "");
|
|
74
|
+
if (/\/v1$/i.test(trimmed)) {
|
|
75
|
+
return joinUrl(trimmed, "models");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return joinUrl(trimmed, "v1/models");
|
|
79
|
+
}
|