@humain/terminal 0.0.3-beta.2 → 0.0.4-beta.1

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.
Files changed (136) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/bun/cli.d.ts.map +1 -1
  3. package/dist/bun/cli.js +0 -1
  4. package/dist/bun/cli.js.map +1 -1
  5. package/dist/core/agent-session.d.ts.map +1 -1
  6. package/dist/core/agent-session.js +7 -1
  7. package/dist/core/agent-session.js.map +1 -1
  8. package/dist/core/sdk.d.ts.map +1 -1
  9. package/dist/core/sdk.js +1 -1
  10. package/dist/core/sdk.js.map +1 -1
  11. package/dist/core/session-manager.d.ts +4 -0
  12. package/dist/core/session-manager.d.ts.map +1 -1
  13. package/dist/core/session-manager.js +46 -22
  14. package/dist/core/session-manager.js.map +1 -1
  15. package/dist/core/tools/index.d.ts +2 -1
  16. package/dist/core/tools/index.d.ts.map +1 -1
  17. package/dist/core/tools/index.js +9 -1
  18. package/dist/core/tools/index.js.map +1 -1
  19. package/dist/core/tools/subagent.d.ts +82 -0
  20. package/dist/core/tools/subagent.d.ts.map +1 -0
  21. package/dist/core/tools/subagent.js +980 -0
  22. package/dist/core/tools/subagent.js.map +1 -0
  23. package/dist/humain/bedrock-provider/vendor/index.bundle.d.mts +3 -0
  24. package/dist/humain/bedrock-provider/vendor/index.bundle.mjs +1509 -0
  25. package/dist/humain/index.d.ts.map +1 -1
  26. package/dist/humain/index.js +3 -0
  27. package/dist/humain/index.js.map +1 -1
  28. package/dist/humain/mcp-adapter/vendor/CHANGELOG.md +36 -0
  29. package/dist/humain/mcp-adapter/vendor/README.md +47 -5
  30. package/dist/humain/mcp-adapter/vendor/cli.js +163 -1
  31. package/dist/humain/mcp-adapter/vendor/commands.ts +128 -22
  32. package/dist/humain/mcp-adapter/vendor/config.ts +25 -7
  33. package/dist/humain/mcp-adapter/vendor/direct-tools.ts +5 -4
  34. package/dist/humain/mcp-adapter/vendor/index.bundle.mjs +1663 -458
  35. package/dist/humain/mcp-adapter/vendor/index.ts +219 -32
  36. package/dist/humain/mcp-adapter/vendor/init.ts +36 -4
  37. package/dist/humain/mcp-adapter/vendor/lifecycle.ts +315 -24
  38. package/dist/humain/mcp-adapter/vendor/mcp-auth-flow.ts +39 -6
  39. package/dist/humain/mcp-adapter/vendor/mcp-bearer-store.ts +387 -0
  40. package/dist/humain/mcp-adapter/vendor/mcp-callback-server.ts +27 -3
  41. package/dist/humain/mcp-adapter/vendor/mcp-code.ts +4 -0
  42. package/dist/humain/mcp-adapter/vendor/mcp-panel.ts +42 -13
  43. package/dist/humain/mcp-adapter/vendor/metadata-cache.ts +32 -21
  44. package/dist/humain/mcp-adapter/vendor/package-mcp-loader.ts +168 -0
  45. package/dist/humain/mcp-adapter/vendor/package.json +179 -177
  46. package/dist/humain/mcp-adapter/vendor/proxy-modes.ts +5 -4
  47. package/dist/humain/mcp-adapter/vendor/request-headers-command.ts +31 -37
  48. package/dist/humain/mcp-adapter/vendor/search-ranking.ts +92 -22
  49. package/dist/humain/mcp-adapter/vendor/server-manager.ts +140 -2
  50. package/dist/humain/mcp-adapter/vendor/session-recovery.ts +7 -0
  51. package/dist/humain/mcp-adapter/vendor/tool-approval.ts +15 -2
  52. package/dist/humain/mcp-adapter/vendor/tool-result-renderer.ts +44 -4
  53. package/dist/humain/mcp-adapter/vendor/types.ts +2 -0
  54. package/dist/humain/mcp-adapter/vendor/ui-server.ts +18 -16
  55. package/dist/humain/mcp-adapter/vendor/utils.ts +70 -0
  56. package/dist/humain/web-access/vendor/anysearch.d.ts.map +1 -1
  57. package/dist/humain/web-access/vendor/anysearch.js +3 -2
  58. package/dist/humain/web-access/vendor/anysearch.js.map +1 -1
  59. package/dist/humain/web-access/vendor/brave.d.ts.map +1 -1
  60. package/dist/humain/web-access/vendor/brave.js +14 -4
  61. package/dist/humain/web-access/vendor/brave.js.map +1 -1
  62. package/dist/humain/web-access/vendor/curator-page.d.ts +3 -0
  63. package/dist/humain/web-access/vendor/curator-page.d.ts.map +1 -1
  64. package/dist/humain/web-access/vendor/curator-page.js +4 -1
  65. package/dist/humain/web-access/vendor/curator-page.js.map +1 -1
  66. package/dist/humain/web-access/vendor/curator-server.d.ts +3 -0
  67. package/dist/humain/web-access/vendor/curator-server.d.ts.map +1 -1
  68. package/dist/humain/web-access/vendor/curator-server.js +6 -0
  69. package/dist/humain/web-access/vendor/curator-server.js.map +1 -1
  70. package/dist/humain/web-access/vendor/exa.d.ts.map +1 -1
  71. package/dist/humain/web-access/vendor/exa.js +16 -7
  72. package/dist/humain/web-access/vendor/exa.js.map +1 -1
  73. package/dist/humain/web-access/vendor/extract.d.ts.map +1 -1
  74. package/dist/humain/web-access/vendor/extract.js +31 -3
  75. package/dist/humain/web-access/vendor/extract.js.map +1 -1
  76. package/dist/humain/web-access/vendor/gemini-search.d.ts +2 -2
  77. package/dist/humain/web-access/vendor/gemini-search.d.ts.map +1 -1
  78. package/dist/humain/web-access/vendor/gemini-search.js +25 -4
  79. package/dist/humain/web-access/vendor/gemini-search.js.map +1 -1
  80. package/dist/humain/web-access/vendor/index.d.ts.map +1 -1
  81. package/dist/humain/web-access/vendor/index.js +36 -49
  82. package/dist/humain/web-access/vendor/index.js.map +1 -1
  83. package/dist/humain/web-access/vendor/page-query.d.ts +1 -2
  84. package/dist/humain/web-access/vendor/page-query.d.ts.map +1 -1
  85. package/dist/humain/web-access/vendor/page-query.js +5 -2
  86. package/dist/humain/web-access/vendor/page-query.js.map +1 -1
  87. package/dist/humain/web-access/vendor/parallel-mcp.d.ts +10 -0
  88. package/dist/humain/web-access/vendor/parallel-mcp.d.ts.map +1 -0
  89. package/dist/humain/web-access/vendor/parallel-mcp.js +224 -0
  90. package/dist/humain/web-access/vendor/parallel-mcp.js.map +1 -0
  91. package/dist/humain/web-access/vendor/parallel.d.ts +1 -0
  92. package/dist/humain/web-access/vendor/parallel.d.ts.map +1 -1
  93. package/dist/humain/web-access/vendor/parallel.js +2 -2
  94. package/dist/humain/web-access/vendor/parallel.js.map +1 -1
  95. package/dist/humain/web-access/vendor/query-rewrite.d.ts +3 -0
  96. package/dist/humain/web-access/vendor/query-rewrite.d.ts.map +1 -0
  97. package/dist/humain/web-access/vendor/query-rewrite.js +42 -0
  98. package/dist/humain/web-access/vendor/query-rewrite.js.map +1 -0
  99. package/dist/humain/web-access/vendor/serper.d.ts +4 -0
  100. package/dist/humain/web-access/vendor/serper.d.ts.map +1 -0
  101. package/dist/humain/web-access/vendor/serper.js +206 -0
  102. package/dist/humain/web-access/vendor/serper.js.map +1 -0
  103. package/dist/humain/web-access/vendor/summary-model-scope.d.ts +5 -0
  104. package/dist/humain/web-access/vendor/summary-model-scope.d.ts.map +1 -1
  105. package/dist/humain/web-access/vendor/summary-model-scope.js +10 -5
  106. package/dist/humain/web-access/vendor/summary-model-scope.js.map +1 -1
  107. package/dist/humain/web-access/vendor/summary-review.d.ts.map +1 -1
  108. package/dist/humain/web-access/vendor/summary-review.js +40 -14
  109. package/dist/humain/web-access/vendor/summary-review.js.map +1 -1
  110. package/dist/humain/web-access/vendor/tavily.d.ts.map +1 -1
  111. package/dist/humain/web-access/vendor/tavily.js +14 -4
  112. package/dist/humain/web-access/vendor/tavily.js.map +1 -1
  113. package/dist/humain/web-access/vendor/utils.d.ts +10 -0
  114. package/dist/humain/web-access/vendor/utils.d.ts.map +1 -1
  115. package/dist/humain/web-access/vendor/utils.js +70 -0
  116. package/dist/humain/web-access/vendor/utils.js.map +1 -1
  117. package/dist/humain/web-access/vendor/valyu.d.ts +6 -0
  118. package/dist/humain/web-access/vendor/valyu.d.ts.map +1 -0
  119. package/dist/humain/web-access/vendor/valyu.js +194 -0
  120. package/dist/humain/web-access/vendor/valyu.js.map +1 -0
  121. package/docs/extensions.md +1 -1
  122. package/examples/extensions/README.md +1 -1
  123. package/examples/extensions/subagent/README.md +24 -147
  124. package/npm-shrinkwrap.json +7 -2
  125. package/package.json +7 -2
  126. package/src/humain/mcp-adapter/UPSTREAM.md +1 -1
  127. package/src/humain/mcp-adapter/upstream.json +3 -3
  128. package/src/humain/mcp-adapter/vendor/CHANGELOG.md +36 -0
  129. package/src/humain/web-access/UPSTREAM.md +1 -1
  130. package/src/humain/web-access/upstream.json +4 -4
  131. package/dist/bun/register-bedrock.d.ts +0 -2
  132. package/dist/bun/register-bedrock.d.ts.map +0 -1
  133. package/dist/bun/register-bedrock.js +0 -4
  134. package/dist/bun/register-bedrock.js.map +0 -1
  135. package/examples/extensions/subagent/agents.ts +0 -126
  136. package/examples/extensions/subagent/index.ts +0 -1015
@@ -1 +1 @@
1
- {"version":3,"file":"parallel.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/parallel.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { activityMonitor } from \"./activity.ts\";\nimport type { ExtractedContent, ExtractOptions } from \"./extract.ts\";\nimport type { SearchOptions, SearchResponse } from \"./perplexity.ts\";\nimport { hasCredentialSource, redactCredential, resolveCredential } from \"./credential-source.ts\";\nimport { getWebSearchConfigPath } from \"./utils.ts\";\n\nconst PARALLEL_SEARCH_URL = \"https://api.parallel.ai/v1/search\";\nconst PARALLEL_EXTRACT_URL = \"https://api.parallel.ai/v1/extract\";\nconst CONFIG_PATH = getWebSearchConfigPath();\nconst MIN_PARALLEL_API_KEY_LENGTH = 8;\nconst MIN_USEFUL_CONTENT = 500;\nconst SEARCH_TIMEOUT_MS = 60_000;\n\nconst PLACEHOLDER_API_KEY_DENYLIST = new Set([\n\t\"replace_with_your_parallel_api_key\",\n\t\"parallel_api_key\",\n\t\"your-key\",\n\t\"your-key-here\",\n\t\"your-api-key-here\",\n\t\"dummy\",\n\t\"placeholder\",\n\t\"changeme\",\n\t\"insert-your-key\",\n\t\"insert-your-key-here\",\n\t\"api-key\",\n\t\"xxx\",\n]);\n\ninterface WebSearchConfig {\n\tparallelApiKey?: unknown;\n}\n\ninterface V1WebSearchResult {\n\turl: string;\n\ttitle?: string | null;\n\tpublish_date?: string | null;\n\texcerpts?: string[];\n}\n\ninterface V1ExtractResult {\n\turl: string;\n\ttitle?: string | null;\n\tpublish_date?: string | null;\n\texcerpts?: string[];\n\tfull_content?: string | null;\n}\n\ninterface ParallelSearchOptions extends SearchOptions {\n\tincludeContent?: boolean;\n}\n\nlet cachedConfig: WebSearchConfig | null = null;\n\nfunction loadConfig(): WebSearchConfig {\n\tif (cachedConfig) return cachedConfig;\n\tif (!existsSync(CONFIG_PATH)) {\n\t\tcachedConfig = {};\n\t\treturn cachedConfig;\n\t}\n\n\tconst raw = readFileSync(CONFIG_PATH, \"utf-8\");\n\ttry {\n\t\tcachedConfig = JSON.parse(raw) as WebSearchConfig;\n\t\treturn cachedConfig;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);\n\t}\n}\n\nexport function clearParallelConfigCache(): void {\n\tcachedConfig = null;\n}\n\nfunction normalizeApiKey(value: unknown): string | null {\n\tif (typeof value !== \"string\") return null;\n\tconst normalized = value.trim();\n\treturn normalized.length > 0 ? normalized : null;\n}\n\nfunction isPlaceholderApiKey(key: string): boolean {\n\tconst normalized = key.trim();\n\treturn normalized.length < MIN_PARALLEL_API_KEY_LENGTH || PLACEHOLDER_API_KEY_DENYLIST.has(normalized.toLowerCase());\n}\n\nasync function resolveApiKey(signal?: AbortSignal): Promise<string | null> {\n\tconst configKey = normalizeApiKey(loadConfig().parallelApiKey);\n\tif (configKey?.startsWith(\"$\") || configKey?.startsWith(\"!\")) {\n\t\tconst resolved = await resolveCredential({\n\t\t\tprovider: \"Parallel\",\n\t\t\tconfiguredValue: configKey,\n\t\t\tenvironmentValue: process.env.PARALLEL_API_KEY,\n\t\t\tsignal,\n\t\t});\n\t\treturn resolved && !isPlaceholderApiKey(resolved) ? resolved : null;\n\t}\n\n\tconst envKey = normalizeApiKey(process.env.PARALLEL_API_KEY);\n\tif (envKey && !isPlaceholderApiKey(envKey)) return envKey;\n\tif (configKey && !isPlaceholderApiKey(configKey)) return configKey;\n\treturn null;\n}\n\nfunction hasConfiguredApiKey(): boolean {\n\tconst configKey = normalizeApiKey(loadConfig().parallelApiKey);\n\tif (configKey?.startsWith(\"$\") || configKey?.startsWith(\"!\")) {\n\t\treturn hasCredentialSource({\n\t\t\tprovider: \"Parallel\",\n\t\t\tconfiguredValue: configKey,\n\t\t\tenvironmentValue: process.env.PARALLEL_API_KEY,\n\t\t});\n\t}\n\tconst envKey = normalizeApiKey(process.env.PARALLEL_API_KEY);\n\treturn (envKey !== null && !isPlaceholderApiKey(envKey)) || (configKey !== null && !isPlaceholderApiKey(configKey));\n}\n\nasync function getApiKey(signal?: AbortSignal): Promise<string> {\n\tconst key = await resolveApiKey(signal);\n\tif (!key) {\n\t\tthrow new Error(\n\t\t\t\"Parallel API key not found. Either:\\n\" +\n\t\t\t` 1. Create ${CONFIG_PATH} with { \"parallelApiKey\": \"your-key\" }\\n` +\n\t\t\t\" 2. Set PARALLEL_API_KEY environment variable\\n\" +\n\t\t\t\"Get a key at https://platform.parallel.ai\",\n\t\t);\n\t}\n\treturn key;\n}\n\nexport function hasParallelApiKey(): boolean {\n\treturn hasConfiguredApiKey();\n}\n\nexport function isParallelAvailable(): boolean {\n\treturn hasParallelApiKey();\n}\n\nfunction requestSignal(signal?: AbortSignal): AbortSignal {\n\tconst timeout = AbortSignal.timeout(SEARCH_TIMEOUT_MS);\n\treturn signal ? AbortSignal.any([signal, timeout]) : timeout;\n}\n\nfunction errorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\nfunction activityContext(\n\turl: string,\n\tbody: Record<string, unknown>,\n): { type: \"api\" | \"fetch\"; query?: string; url?: string } {\n\tif (typeof body.objective === \"string\" && body.objective.trim().length > 0) {\n\t\treturn { type: \"api\", query: body.objective };\n\t}\n\n\tconst searchQueries = body.search_queries;\n\tif (Array.isArray(searchQueries) && typeof searchQueries[0] === \"string\") {\n\t\treturn { type: \"api\", query: searchQueries[0] };\n\t}\n\n\tconst urls = body.urls;\n\tif (Array.isArray(urls) && typeof urls[0] === \"string\") {\n\t\treturn { type: \"fetch\", url: urls[0] };\n\t}\n\n\treturn url.includes(\"/search\") ? { type: \"api\", query: \"Parallel search\" } : { type: \"fetch\", url };\n}\n\nfunction recencyToAfterDate(filter: string): string {\n\tconst now = new Date();\n\tconst offsets: Record<string, number> = { day: 1, week: 7, month: 30, year: 365 };\n\tconst days = offsets[filter] ?? 0;\n\treturn new Date(now.getTime() - days * 86_400_000).toISOString().slice(0, 10);\n}\n\nfunction normalizeDomain(value: string): string | null {\n\tlet input = value.trim().toLowerCase();\n\tif (!input) return null;\n\tif (input.startsWith(\"-\")) input = input.slice(1).trim();\n\tif (!input) return null;\n\ttry {\n\t\tconst parsed = input.includes(\"://\") ? new URL(input) : new URL(`https://${input}`);\n\t\tinput = parsed.hostname;\n\t} catch {\n\t\tinput = input.split(\"/\")[0]?.split(\":\")[0] ?? \"\";\n\t}\n\tinput = input.replace(/^\\.+|\\.+$/g, \"\");\n\treturn /^[a-z0-9][a-z0-9.-]*\\.[a-z]{2,}$/i.test(input) ? input : null;\n}\n\nfunction mapDomainFilter(domainFilter: string[] | undefined): { include_domains?: string[]; exclude_domains?: string[] } {\n\tif (!domainFilter?.length) return {};\n\tconst include_domains: string[] = [];\n\tconst exclude_domains: string[] = [];\n\tfor (const raw of domainFilter) {\n\t\tconst domain = normalizeDomain(raw);\n\t\tif (!domain) continue;\n\t\tconst target = raw.trim().startsWith(\"-\") ? exclude_domains : include_domains;\n\t\tif (!target.includes(domain)) target.push(domain);\n\t}\n\treturn {\n\t\t...(include_domains.length ? { include_domains } : {}),\n\t\t...(exclude_domains.length ? { exclude_domains } : {}),\n\t};\n}\n\nfunction buildSearchRequestBody(query: string, options: ParallelSearchOptions = {}): Record<string, unknown> {\n\tconst numResults = Math.max(1, Math.min(Math.floor(options.numResults ?? 5), 20));\n\tconst sourcePolicy = {\n\t\t...mapDomainFilter(options.domainFilter),\n\t\t...(options.recencyFilter ? { after_date: recencyToAfterDate(options.recencyFilter) } : {}),\n\t};\n\treturn {\n\t\tobjective: query,\n\t\tsearch_queries: [query],\n\t\tadvanced_settings: {\n\t\t\tmax_results: numResults,\n\t\t\t...(Object.keys(sourcePolicy).length > 0 ? { source_policy: sourcePolicy } : {}),\n\t\t},\n\t};\n}\n\nfunction normalizeExcerpts(value: unknown): string[] {\n\tif (!Array.isArray(value)) return [];\n\treturn value.filter((item): item is string => typeof item === \"string\" && item.trim().length > 0);\n}\n\nfunction mapSearchResults(results: V1WebSearchResult[] | undefined): SearchResponse[\"results\"] {\n\tif (!Array.isArray(results)) return [];\n\tconst mapped: SearchResponse[\"results\"] = [];\n\tfor (let i = 0; i < results.length; i++) {\n\t\tconst item = results[i];\n\t\tif (!item?.url) continue;\n\t\tconst excerpts = normalizeExcerpts(item.excerpts);\n\t\tmapped.push({\n\t\t\ttitle: item.title || `Source ${i + 1}`,\n\t\t\turl: item.url,\n\t\t\tsnippet: excerpts.length > 0 ? excerpts[0].replace(/\\s+/g, \" \").trim().slice(0, 200) : \"\",\n\t\t});\n\t}\n\treturn mapped;\n}\n\nfunction buildAnswerFromExcerpts(results: V1WebSearchResult[] | undefined): string {\n\tif (!Array.isArray(results)) return \"\";\n\tconst parts: string[] = [];\n\tfor (let i = 0; i < results.length; i++) {\n\t\tconst item = results[i];\n\t\tif (!item?.url) continue;\n\t\tconst excerpts = normalizeExcerpts(item.excerpts);\n\t\tif (excerpts.length === 0) continue;\n\t\tparts.push(`${excerpts.join(\" \")}\\nSource: ${item.title || `Source ${i + 1}`} (${item.url})`);\n\t}\n\treturn parts.join(\"\\n\\n\");\n}\n\nfunction mapInlineContent(results: V1WebSearchResult[] | undefined): ExtractedContent[] {\n\tif (!Array.isArray(results)) return [];\n\treturn results.flatMap((result) => {\n\t\tif (!result?.url) return [];\n\t\tconst excerpts = normalizeExcerpts(result.excerpts);\n\t\tif (excerpts.length === 0) return [];\n\t\treturn [{ url: result.url, title: result.title || \"\", content: excerpts.join(\"\\n\\n\"), error: null }];\n\t});\n}\n\nfunction resolveExtractContent(result: V1ExtractResult): string {\n\tconst fullContent = typeof result.full_content === \"string\" ? result.full_content.trim() : \"\";\n\treturn fullContent.length > 0 ? fullContent : normalizeExcerpts(result.excerpts).join(\"\\n\\n\");\n}\n\nfunction mapExtractResult(result: V1ExtractResult | undefined | null): ExtractedContent | null {\n\tif (!result?.url) return null;\n\tconst content = resolveExtractContent(result);\n\tif (content.length < MIN_USEFUL_CONTENT) return null;\n\treturn {\n\t\turl: result.url,\n\t\ttitle: typeof result.title === \"string\" ? result.title.trim() : \"\",\n\t\tcontent,\n\t\terror: null,\n\t};\n}\n\nfunction buildExtractRequestBody(url: string, options: ExtractOptions = {}, fullContent = false): Record<string, unknown> {\n\tconst body: Record<string, unknown> = { urls: [url] };\n\tconst prompt = options.prompt?.trim();\n\tif (prompt) body.objective = prompt;\n\tif (fullContent) body.advanced_settings = { full_content: true };\n\treturn body;\n}\n\nfunction findExtractResult(results: V1ExtractResult[] | undefined, url: string): V1ExtractResult | undefined {\n\tif (!Array.isArray(results)) return undefined;\n\treturn results.find(item => item?.url === url) ?? results[0];\n}\n\nfunction hasExtractUrlError(errors: unknown, url: string): boolean {\n\tif (!Array.isArray(errors)) return false;\n\treturn errors.some((entry) => {\n\t\tif (typeof entry === \"string\") return entry === url;\n\t\treturn typeof entry === \"object\" && entry !== null && (entry as { url?: unknown }).url === url;\n\t});\n}\n\nasync function fetchAndMapExtractResult(\n\turl: string,\n\tbody: Record<string, unknown>,\n\tsignal?: AbortSignal,\n): Promise<{ mapped: ExtractedContent | null; result: V1ExtractResult | undefined }> {\n\tconst data = await parallelFetch(PARALLEL_EXTRACT_URL, body, signal);\n\tif (hasExtractUrlError(data.errors, url)) return { mapped: null, result: undefined };\n\tconst result = findExtractResult(data.results as V1ExtractResult[] | undefined, url);\n\treturn { mapped: mapExtractResult(result), result };\n}\n\nexport async function searchWithParallel(query: string, options: ParallelSearchOptions = {}): Promise<SearchResponse> {\n\tconst data = await parallelFetch(PARALLEL_SEARCH_URL, buildSearchRequestBody(query, options), options.signal);\n\tconst results = data.results as V1WebSearchResult[] | undefined;\n\tconst response: SearchResponse = {\n\t\tanswer: buildAnswerFromExcerpts(results),\n\t\tresults: mapSearchResults(results),\n\t};\n\tif (options.includeContent) {\n\t\tconst inlineContent = mapInlineContent(results);\n\t\tif (inlineContent.length > 0) response.inlineContent = inlineContent;\n\t}\n\treturn response;\n}\n\nexport async function extractWithParallel(\n\turl: string,\n\tsignal?: AbortSignal,\n\toptions: ExtractOptions = {},\n): Promise<ExtractedContent | null> {\n\tconst initial = await fetchAndMapExtractResult(url, buildExtractRequestBody(url, options), signal);\n\tif (initial.mapped) return initial.mapped;\n\tif (!initial.result || resolveExtractContent(initial.result).length >= MIN_USEFUL_CONTENT) return null;\n\n\tconst retry = await fetchAndMapExtractResult(url, buildExtractRequestBody(url, options, true), signal);\n\treturn retry.mapped;\n}\n\nasync function parallelFetch(\n\turl: string,\n\tbody: Record<string, unknown>,\n\tsignal?: AbortSignal,\n): Promise<Record<string, unknown>> {\n\tconst apiKey = await getApiKey(signal);\n\tconst activityId = activityMonitor.logStart(activityContext(url, body));\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"x-api-key\": apiKey,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify(body),\n\t\t\tsignal: requestSignal(signal),\n\t\t});\n\t} catch (err) {\n\t\tconst message = errorMessage(err);\n\t\tconst redactedMessage = redactCredential(message, apiKey);\n\t\tif (redactedMessage.toLowerCase().includes(\"abort\")) activityMonitor.logComplete(activityId, 0);\n\t\telse activityMonitor.logError(activityId, redactedMessage);\n\t\tif (redactedMessage === message) throw err;\n\t\tconst redactedError = new Error(redactedMessage);\n\t\tif (err instanceof Error) redactedError.name = err.name;\n\t\tthrow redactedError;\n\t}\n\n\tif (!response.ok) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tconst errorText = redactCredential(await response.text(), apiKey);\n\t\tthrow new Error(`Parallel API error ${response.status}: ${errorText.slice(0, 300)}`);\n\t}\n\n\ttry {\n\t\tconst data = await response.json() as Record<string, unknown>;\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\treturn data;\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tthrow new Error(`Parallel API returned invalid JSON: ${errorMessage(err)}`);\n\t}\n}\n"]}
1
+ {"version":3,"file":"parallel.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/parallel.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { activityMonitor } from \"./activity.ts\";\nimport type { ExtractedContent, ExtractOptions } from \"./extract.ts\";\nimport type { SearchOptions, SearchResponse } from \"./perplexity.ts\";\nimport { hasCredentialSource, redactCredential, resolveCredential } from \"./credential-source.ts\";\nimport { getWebSearchConfigPath } from \"./utils.ts\";\n\nconst PARALLEL_SEARCH_URL = \"https://api.parallel.ai/v1/search\";\nconst PARALLEL_EXTRACT_URL = \"https://api.parallel.ai/v1/extract\";\nconst CONFIG_PATH = getWebSearchConfigPath();\nconst MIN_PARALLEL_API_KEY_LENGTH = 8;\nconst MIN_USEFUL_CONTENT = 500;\nconst SEARCH_TIMEOUT_MS = 60_000;\n\nconst PLACEHOLDER_API_KEY_DENYLIST = new Set([\n\t\"replace_with_your_parallel_api_key\",\n\t\"parallel_api_key\",\n\t\"your-key\",\n\t\"your-key-here\",\n\t\"your-api-key-here\",\n\t\"dummy\",\n\t\"placeholder\",\n\t\"changeme\",\n\t\"insert-your-key\",\n\t\"insert-your-key-here\",\n\t\"api-key\",\n\t\"xxx\",\n]);\n\ninterface WebSearchConfig {\n\tparallelApiKey?: unknown;\n}\n\ninterface V1WebSearchResult {\n\turl: string;\n\ttitle?: string | null;\n\tpublish_date?: string | null;\n\texcerpts?: string[];\n}\n\ninterface V1ExtractResult {\n\turl: string;\n\ttitle?: string | null;\n\tpublish_date?: string | null;\n\texcerpts?: string[];\n\tfull_content?: string | null;\n}\n\ninterface ParallelSearchOptions extends SearchOptions {\n\tincludeContent?: boolean;\n}\n\nlet cachedConfig: WebSearchConfig | null = null;\n\nfunction loadConfig(): WebSearchConfig {\n\tif (cachedConfig) return cachedConfig;\n\tif (!existsSync(CONFIG_PATH)) {\n\t\tcachedConfig = {};\n\t\treturn cachedConfig;\n\t}\n\n\tconst raw = readFileSync(CONFIG_PATH, \"utf-8\");\n\ttry {\n\t\tcachedConfig = JSON.parse(raw) as WebSearchConfig;\n\t\treturn cachedConfig;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);\n\t}\n}\n\nexport function clearParallelConfigCache(): void {\n\tcachedConfig = null;\n}\n\nfunction normalizeApiKey(value: unknown): string | null {\n\tif (typeof value !== \"string\") return null;\n\tconst normalized = value.trim();\n\treturn normalized.length > 0 ? normalized : null;\n}\n\nfunction isPlaceholderApiKey(key: string): boolean {\n\tconst normalized = key.trim();\n\treturn normalized.length < MIN_PARALLEL_API_KEY_LENGTH || PLACEHOLDER_API_KEY_DENYLIST.has(normalized.toLowerCase());\n}\n\nexport async function resolveParallelApiKey(signal?: AbortSignal): Promise<string | null> {\n\tconst configKey = normalizeApiKey(loadConfig().parallelApiKey);\n\tif (configKey?.startsWith(\"$\") || configKey?.startsWith(\"!\")) {\n\t\tconst resolved = await resolveCredential({\n\t\t\tprovider: \"Parallel\",\n\t\t\tconfiguredValue: configKey,\n\t\t\tenvironmentValue: process.env.PARALLEL_API_KEY,\n\t\t\tsignal,\n\t\t});\n\t\treturn resolved && !isPlaceholderApiKey(resolved) ? resolved : null;\n\t}\n\n\tconst envKey = normalizeApiKey(process.env.PARALLEL_API_KEY);\n\tif (envKey && !isPlaceholderApiKey(envKey)) return envKey;\n\tif (configKey && !isPlaceholderApiKey(configKey)) return configKey;\n\treturn null;\n}\n\nfunction hasConfiguredApiKey(): boolean {\n\tconst configKey = normalizeApiKey(loadConfig().parallelApiKey);\n\tif (configKey?.startsWith(\"$\") || configKey?.startsWith(\"!\")) {\n\t\treturn hasCredentialSource({\n\t\t\tprovider: \"Parallel\",\n\t\t\tconfiguredValue: configKey,\n\t\t\tenvironmentValue: process.env.PARALLEL_API_KEY,\n\t\t});\n\t}\n\tconst envKey = normalizeApiKey(process.env.PARALLEL_API_KEY);\n\treturn (envKey !== null && !isPlaceholderApiKey(envKey)) || (configKey !== null && !isPlaceholderApiKey(configKey));\n}\n\nasync function getApiKey(signal?: AbortSignal): Promise<string> {\n\tconst key = await resolveParallelApiKey(signal);\n\tif (!key) {\n\t\tthrow new Error(\n\t\t\t\"Parallel API key not found. Either:\\n\" +\n\t\t\t` 1. Create ${CONFIG_PATH} with { \"parallelApiKey\": \"your-key\" }\\n` +\n\t\t\t\" 2. Set PARALLEL_API_KEY environment variable\\n\" +\n\t\t\t\"Get a key at https://platform.parallel.ai\",\n\t\t);\n\t}\n\treturn key;\n}\n\nexport function hasParallelApiKey(): boolean {\n\treturn hasConfiguredApiKey();\n}\n\nexport function isParallelAvailable(): boolean {\n\treturn hasParallelApiKey();\n}\n\nfunction requestSignal(signal?: AbortSignal): AbortSignal {\n\tconst timeout = AbortSignal.timeout(SEARCH_TIMEOUT_MS);\n\treturn signal ? AbortSignal.any([signal, timeout]) : timeout;\n}\n\nfunction errorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\nfunction activityContext(\n\turl: string,\n\tbody: Record<string, unknown>,\n): { type: \"api\" | \"fetch\"; query?: string; url?: string } {\n\tif (typeof body.objective === \"string\" && body.objective.trim().length > 0) {\n\t\treturn { type: \"api\", query: body.objective };\n\t}\n\n\tconst searchQueries = body.search_queries;\n\tif (Array.isArray(searchQueries) && typeof searchQueries[0] === \"string\") {\n\t\treturn { type: \"api\", query: searchQueries[0] };\n\t}\n\n\tconst urls = body.urls;\n\tif (Array.isArray(urls) && typeof urls[0] === \"string\") {\n\t\treturn { type: \"fetch\", url: urls[0] };\n\t}\n\n\treturn url.includes(\"/search\") ? { type: \"api\", query: \"Parallel search\" } : { type: \"fetch\", url };\n}\n\nfunction recencyToAfterDate(filter: string): string {\n\tconst now = new Date();\n\tconst offsets: Record<string, number> = { day: 1, week: 7, month: 30, year: 365 };\n\tconst days = offsets[filter] ?? 0;\n\treturn new Date(now.getTime() - days * 86_400_000).toISOString().slice(0, 10);\n}\n\nfunction normalizeDomain(value: string): string | null {\n\tlet input = value.trim().toLowerCase();\n\tif (!input) return null;\n\tif (input.startsWith(\"-\")) input = input.slice(1).trim();\n\tif (!input) return null;\n\ttry {\n\t\tconst parsed = input.includes(\"://\") ? new URL(input) : new URL(`https://${input}`);\n\t\tinput = parsed.hostname;\n\t} catch {\n\t\tinput = input.split(\"/\")[0]?.split(\":\")[0] ?? \"\";\n\t}\n\tinput = input.replace(/^\\.+|\\.+$/g, \"\");\n\treturn /^[a-z0-9][a-z0-9.-]*\\.[a-z]{2,}$/i.test(input) ? input : null;\n}\n\nfunction mapDomainFilter(domainFilter: string[] | undefined): { include_domains?: string[]; exclude_domains?: string[] } {\n\tif (!domainFilter?.length) return {};\n\tconst include_domains: string[] = [];\n\tconst exclude_domains: string[] = [];\n\tfor (const raw of domainFilter) {\n\t\tconst domain = normalizeDomain(raw);\n\t\tif (!domain) continue;\n\t\tconst target = raw.trim().startsWith(\"-\") ? exclude_domains : include_domains;\n\t\tif (!target.includes(domain)) target.push(domain);\n\t}\n\treturn {\n\t\t...(include_domains.length ? { include_domains } : {}),\n\t\t...(exclude_domains.length ? { exclude_domains } : {}),\n\t};\n}\n\nfunction buildSearchRequestBody(query: string, options: ParallelSearchOptions = {}): Record<string, unknown> {\n\tconst numResults = Math.max(1, Math.min(Math.floor(options.numResults ?? 5), 20));\n\tconst sourcePolicy = {\n\t\t...mapDomainFilter(options.domainFilter),\n\t\t...(options.recencyFilter ? { after_date: recencyToAfterDate(options.recencyFilter) } : {}),\n\t};\n\treturn {\n\t\tobjective: query,\n\t\tsearch_queries: [query],\n\t\tadvanced_settings: {\n\t\t\tmax_results: numResults,\n\t\t\t...(Object.keys(sourcePolicy).length > 0 ? { source_policy: sourcePolicy } : {}),\n\t\t},\n\t};\n}\n\nfunction normalizeExcerpts(value: unknown): string[] {\n\tif (!Array.isArray(value)) return [];\n\treturn value.filter((item): item is string => typeof item === \"string\" && item.trim().length > 0);\n}\n\nfunction mapSearchResults(results: V1WebSearchResult[] | undefined): SearchResponse[\"results\"] {\n\tif (!Array.isArray(results)) return [];\n\tconst mapped: SearchResponse[\"results\"] = [];\n\tfor (let i = 0; i < results.length; i++) {\n\t\tconst item = results[i];\n\t\tif (!item?.url) continue;\n\t\tconst excerpts = normalizeExcerpts(item.excerpts);\n\t\tmapped.push({\n\t\t\ttitle: item.title || `Source ${i + 1}`,\n\t\t\turl: item.url,\n\t\t\tsnippet: excerpts.length > 0 ? excerpts[0].replace(/\\s+/g, \" \").trim().slice(0, 200) : \"\",\n\t\t});\n\t}\n\treturn mapped;\n}\n\nfunction buildAnswerFromExcerpts(results: V1WebSearchResult[] | undefined): string {\n\tif (!Array.isArray(results)) return \"\";\n\tconst parts: string[] = [];\n\tfor (let i = 0; i < results.length; i++) {\n\t\tconst item = results[i];\n\t\tif (!item?.url) continue;\n\t\tconst excerpts = normalizeExcerpts(item.excerpts);\n\t\tif (excerpts.length === 0) continue;\n\t\tparts.push(`${excerpts.join(\" \")}\\nSource: ${item.title || `Source ${i + 1}`} (${item.url})`);\n\t}\n\treturn parts.join(\"\\n\\n\");\n}\n\nfunction mapInlineContent(results: V1WebSearchResult[] | undefined): ExtractedContent[] {\n\tif (!Array.isArray(results)) return [];\n\treturn results.flatMap((result) => {\n\t\tif (!result?.url) return [];\n\t\tconst excerpts = normalizeExcerpts(result.excerpts);\n\t\tif (excerpts.length === 0) return [];\n\t\treturn [{ url: result.url, title: result.title || \"\", content: excerpts.join(\"\\n\\n\"), error: null }];\n\t});\n}\n\nfunction resolveExtractContent(result: V1ExtractResult): string {\n\tconst fullContent = typeof result.full_content === \"string\" ? result.full_content.trim() : \"\";\n\treturn fullContent.length > 0 ? fullContent : normalizeExcerpts(result.excerpts).join(\"\\n\\n\");\n}\n\nfunction mapExtractResult(result: V1ExtractResult | undefined | null): ExtractedContent | null {\n\tif (!result?.url) return null;\n\tconst content = resolveExtractContent(result);\n\tif (content.length < MIN_USEFUL_CONTENT) return null;\n\treturn {\n\t\turl: result.url,\n\t\ttitle: typeof result.title === \"string\" ? result.title.trim() : \"\",\n\t\tcontent,\n\t\terror: null,\n\t};\n}\n\nfunction buildExtractRequestBody(url: string, options: ExtractOptions = {}, fullContent = false): Record<string, unknown> {\n\tconst body: Record<string, unknown> = { urls: [url] };\n\tconst prompt = options.prompt?.trim();\n\tif (prompt) body.objective = prompt;\n\tif (fullContent) body.advanced_settings = { full_content: true };\n\treturn body;\n}\n\nfunction findExtractResult(results: V1ExtractResult[] | undefined, url: string): V1ExtractResult | undefined {\n\tif (!Array.isArray(results)) return undefined;\n\treturn results.find(item => item?.url === url) ?? results[0];\n}\n\nfunction hasExtractUrlError(errors: unknown, url: string): boolean {\n\tif (!Array.isArray(errors)) return false;\n\treturn errors.some((entry) => {\n\t\tif (typeof entry === \"string\") return entry === url;\n\t\treturn typeof entry === \"object\" && entry !== null && (entry as { url?: unknown }).url === url;\n\t});\n}\n\nasync function fetchAndMapExtractResult(\n\turl: string,\n\tbody: Record<string, unknown>,\n\tsignal?: AbortSignal,\n): Promise<{ mapped: ExtractedContent | null; result: V1ExtractResult | undefined }> {\n\tconst data = await parallelFetch(PARALLEL_EXTRACT_URL, body, signal);\n\tif (hasExtractUrlError(data.errors, url)) return { mapped: null, result: undefined };\n\tconst result = findExtractResult(data.results as V1ExtractResult[] | undefined, url);\n\treturn { mapped: mapExtractResult(result), result };\n}\n\nexport async function searchWithParallel(query: string, options: ParallelSearchOptions = {}): Promise<SearchResponse> {\n\tconst data = await parallelFetch(PARALLEL_SEARCH_URL, buildSearchRequestBody(query, options), options.signal);\n\tconst results = data.results as V1WebSearchResult[] | undefined;\n\tconst response: SearchResponse = {\n\t\tanswer: buildAnswerFromExcerpts(results),\n\t\tresults: mapSearchResults(results),\n\t};\n\tif (options.includeContent) {\n\t\tconst inlineContent = mapInlineContent(results);\n\t\tif (inlineContent.length > 0) response.inlineContent = inlineContent;\n\t}\n\treturn response;\n}\n\nexport async function extractWithParallel(\n\turl: string,\n\tsignal?: AbortSignal,\n\toptions: ExtractOptions = {},\n): Promise<ExtractedContent | null> {\n\tconst initial = await fetchAndMapExtractResult(url, buildExtractRequestBody(url, options), signal);\n\tif (initial.mapped) return initial.mapped;\n\tif (!initial.result || resolveExtractContent(initial.result).length >= MIN_USEFUL_CONTENT) return null;\n\n\tconst retry = await fetchAndMapExtractResult(url, buildExtractRequestBody(url, options, true), signal);\n\treturn retry.mapped;\n}\n\nasync function parallelFetch(\n\turl: string,\n\tbody: Record<string, unknown>,\n\tsignal?: AbortSignal,\n): Promise<Record<string, unknown>> {\n\tconst apiKey = await getApiKey(signal);\n\tconst activityId = activityMonitor.logStart(activityContext(url, body));\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"x-api-key\": apiKey,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify(body),\n\t\t\tsignal: requestSignal(signal),\n\t\t});\n\t} catch (err) {\n\t\tconst message = errorMessage(err);\n\t\tconst redactedMessage = redactCredential(message, apiKey);\n\t\tif (redactedMessage.toLowerCase().includes(\"abort\")) activityMonitor.logComplete(activityId, 0);\n\t\telse activityMonitor.logError(activityId, redactedMessage);\n\t\tif (redactedMessage === message) throw err;\n\t\tconst redactedError = new Error(redactedMessage);\n\t\tif (err instanceof Error) redactedError.name = err.name;\n\t\tthrow redactedError;\n\t}\n\n\tif (!response.ok) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tconst errorText = redactCredential(await response.text(), apiKey);\n\t\tthrow new Error(`Parallel API error ${response.status}: ${errorText.slice(0, 300)}`);\n\t}\n\n\ttry {\n\t\tconst data = await response.json() as Record<string, unknown>;\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\treturn data;\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tthrow new Error(`Parallel API returned invalid JSON: ${errorMessage(err)}`);\n\t}\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import type { SummaryGenerationContext } from "./summary-review.ts";
2
+ export declare function rewriteSearchQuery(query: string, ctx: SummaryGenerationContext, signal: AbortSignal): Promise<string>;
3
+ //# sourceMappingURL=query-rewrite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-rewrite.d.ts","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/query-rewrite.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAiBpE,wBAAsB,kBAAkB,CACvC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,wBAAwB,EAC7B,MAAM,EAAE,WAAW,GACjB,OAAO,CAAC,MAAM,CAAC,CA4BjB","sourcesContent":["import { complete, type Api, type Model, type ProviderHeaders } from \"@earendil-works/pi-ai/compat\";\nimport type { SummaryGenerationContext } from \"./summary-review.ts\";\nimport { findModelWithProviderRouting, loadEnabledModelPatterns, modelMatchesEnabledPatterns } from \"./summary-model-scope.ts\";\n\nasync function resolveFirstAvailableModel(\n\tctx: SummaryGenerationContext,\n\tcandidates: Array<{ provider: string; id: string }>,\n): Promise<{ model: Model<Api>; apiKey: string; headers?: ProviderHeaders }> {\n\tconst enabledModelPatterns = loadEnabledModelPatterns(ctx);\n\tfor (const { provider, id } of candidates) {\n\t\tconst model = findModelWithProviderRouting(ctx.modelRegistry, provider, id);\n\t\tif (!model || !modelMatchesEnabledPatterns(model, enabledModelPatterns)) continue;\n\t\tconst auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);\n\t\tif (auth.ok && auth.apiKey) return { model, apiKey: auth.apiKey, headers: auth.headers };\n\t}\n\tthrow new Error(`No enabled model available: ${candidates.map(candidate => `${candidate.provider}/${candidate.id}`).join(\", \")}`);\n}\n\nexport async function rewriteSearchQuery(\n\tquery: string,\n\tctx: SummaryGenerationContext,\n\tsignal: AbortSignal,\n): Promise<string> {\n\tconst { model, apiKey, headers } = await resolveFirstAvailableModel(ctx, [\n\t\t{ provider: \"anthropic\", id: \"claude-haiku-4-5\" },\n\t\t{ provider: \"google\", id: \"gemini-3.6-flash\" },\n\t\t{ provider: \"openai\", id: \"gpt-5-mini\" },\n\t]);\n\tconst registry = ctx.modelRegistry as typeof ctx.modelRegistry & { complete?: typeof complete };\n\tconst usesRegistryComplete = typeof registry.complete === \"function\";\n\tconst completeFn = usesRegistryComplete ? registry.complete!.bind(registry) : complete;\n\tconst response = await completeFn(\n\t\tmodel,\n\t\t{\n\t\t\tmessages: [{\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: [{ type: \"text\", text: `Rewrite this web search query to get better, more specific results. Add relevant year qualifiers, precise technical terms, and specificity. Return ONLY the improved query text, nothing else.\\n\\nQuery: ${query}` }],\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t}],\n\t\t},\n\t\tusesRegistryComplete ? { signal } : { apiKey, headers, signal },\n\t);\n\tif (response.stopReason === \"aborted\") throw new Error(\"Aborted\");\n\tconst contentParts = Array.isArray(response.content) ? response.content : [];\n\tconst text = contentParts\n\t\t.map((part: unknown) => part && typeof part === \"object\" && (part as { type?: unknown }).type === \"text\" && typeof (part as { text?: unknown }).text === \"string\" ? (part as { text: string }).text : \"\")\n\t\t.join(\"\")\n\t\t.trim();\n\tif (!text) throw new Error(\"Rewrite returned empty response\");\n\treturn text;\n}\n"]}
@@ -0,0 +1,42 @@
1
+ import { complete } from "@earendil-works/pi-ai/compat";
2
+ import { findModelWithProviderRouting, loadEnabledModelPatterns, modelMatchesEnabledPatterns } from "./summary-model-scope.js";
3
+ async function resolveFirstAvailableModel(ctx, candidates) {
4
+ const enabledModelPatterns = loadEnabledModelPatterns(ctx);
5
+ for (const { provider, id } of candidates) {
6
+ const model = findModelWithProviderRouting(ctx.modelRegistry, provider, id);
7
+ if (!model || !modelMatchesEnabledPatterns(model, enabledModelPatterns))
8
+ continue;
9
+ const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
10
+ if (auth.ok && auth.apiKey)
11
+ return { model, apiKey: auth.apiKey, headers: auth.headers };
12
+ }
13
+ throw new Error(`No enabled model available: ${candidates.map(candidate => `${candidate.provider}/${candidate.id}`).join(", ")}`);
14
+ }
15
+ export async function rewriteSearchQuery(query, ctx, signal) {
16
+ const { model, apiKey, headers } = await resolveFirstAvailableModel(ctx, [
17
+ { provider: "anthropic", id: "claude-haiku-4-5" },
18
+ { provider: "google", id: "gemini-3.6-flash" },
19
+ { provider: "openai", id: "gpt-5-mini" },
20
+ ]);
21
+ const registry = ctx.modelRegistry;
22
+ const usesRegistryComplete = typeof registry.complete === "function";
23
+ const completeFn = usesRegistryComplete ? registry.complete.bind(registry) : complete;
24
+ const response = await completeFn(model, {
25
+ messages: [{
26
+ role: "user",
27
+ content: [{ type: "text", text: `Rewrite this web search query to get better, more specific results. Add relevant year qualifiers, precise technical terms, and specificity. Return ONLY the improved query text, nothing else.\n\nQuery: ${query}` }],
28
+ timestamp: Date.now(),
29
+ }],
30
+ }, usesRegistryComplete ? { signal } : { apiKey, headers, signal });
31
+ if (response.stopReason === "aborted")
32
+ throw new Error("Aborted");
33
+ const contentParts = Array.isArray(response.content) ? response.content : [];
34
+ const text = contentParts
35
+ .map((part) => part && typeof part === "object" && part.type === "text" && typeof part.text === "string" ? part.text : "")
36
+ .join("")
37
+ .trim();
38
+ if (!text)
39
+ throw new Error("Rewrite returned empty response");
40
+ return text;
41
+ }
42
+ //# sourceMappingURL=query-rewrite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-rewrite.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/query-rewrite.ts"],"names":[],"mappings":"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","sourcesContent":["import { complete, type Api, type Model, type ProviderHeaders } from \"@earendil-works/pi-ai/compat\";\nimport type { SummaryGenerationContext } from \"./summary-review.ts\";\nimport { findModelWithProviderRouting, loadEnabledModelPatterns, modelMatchesEnabledPatterns } from \"./summary-model-scope.ts\";\n\nasync function resolveFirstAvailableModel(\n\tctx: SummaryGenerationContext,\n\tcandidates: Array<{ provider: string; id: string }>,\n): Promise<{ model: Model<Api>; apiKey: string; headers?: ProviderHeaders }> {\n\tconst enabledModelPatterns = loadEnabledModelPatterns(ctx);\n\tfor (const { provider, id } of candidates) {\n\t\tconst model = findModelWithProviderRouting(ctx.modelRegistry, provider, id);\n\t\tif (!model || !modelMatchesEnabledPatterns(model, enabledModelPatterns)) continue;\n\t\tconst auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);\n\t\tif (auth.ok && auth.apiKey) return { model, apiKey: auth.apiKey, headers: auth.headers };\n\t}\n\tthrow new Error(`No enabled model available: ${candidates.map(candidate => `${candidate.provider}/${candidate.id}`).join(\", \")}`);\n}\n\nexport async function rewriteSearchQuery(\n\tquery: string,\n\tctx: SummaryGenerationContext,\n\tsignal: AbortSignal,\n): Promise<string> {\n\tconst { model, apiKey, headers } = await resolveFirstAvailableModel(ctx, [\n\t\t{ provider: \"anthropic\", id: \"claude-haiku-4-5\" },\n\t\t{ provider: \"google\", id: \"gemini-3.6-flash\" },\n\t\t{ provider: \"openai\", id: \"gpt-5-mini\" },\n\t]);\n\tconst registry = ctx.modelRegistry as typeof ctx.modelRegistry & { complete?: typeof complete };\n\tconst usesRegistryComplete = typeof registry.complete === \"function\";\n\tconst completeFn = usesRegistryComplete ? registry.complete!.bind(registry) : complete;\n\tconst response = await completeFn(\n\t\tmodel,\n\t\t{\n\t\t\tmessages: [{\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: [{ type: \"text\", text: `Rewrite this web search query to get better, more specific results. Add relevant year qualifiers, precise technical terms, and specificity. Return ONLY the improved query text, nothing else.\\n\\nQuery: ${query}` }],\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t}],\n\t\t},\n\t\tusesRegistryComplete ? { signal } : { apiKey, headers, signal },\n\t);\n\tif (response.stopReason === \"aborted\") throw new Error(\"Aborted\");\n\tconst contentParts = Array.isArray(response.content) ? response.content : [];\n\tconst text = contentParts\n\t\t.map((part: unknown) => part && typeof part === \"object\" && (part as { type?: unknown }).type === \"text\" && typeof (part as { text?: unknown }).text === \"string\" ? (part as { text: string }).text : \"\")\n\t\t.join(\"\")\n\t\t.trim();\n\tif (!text) throw new Error(\"Rewrite returned empty response\");\n\treturn text;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { SearchOptions, SearchResponse } from "./perplexity.ts";
2
+ export declare function isSerperAvailable(): boolean;
3
+ export declare function searchWithSerper(query: string, options?: SearchOptions): Promise<SearchResponse>;
4
+ //# sourceMappingURL=serper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serper.d.ts","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/serper.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAmJrE,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CAyD1G","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { activityMonitor } from \"./activity.ts\";\nimport type { SearchOptions, SearchResponse } from \"./perplexity.ts\";\nimport { hasCredentialSource, redactCredential, resolveCredential } from \"./credential-source.ts\";\nimport { getWebSearchConfigPath } from \"./utils.ts\";\n\nconst SERPER_SEARCH_URL = \"https://google.serper.dev/search\";\nconst CONFIG_PATH = getWebSearchConfigPath();\nconst SEARCH_TIMEOUT_MS = 60_000;\nconst RECENCY_TBS: Record<NonNullable<SearchOptions[\"recencyFilter\"]>, string> = {\n\tday: \"qdr:d\",\n\tweek: \"qdr:w\",\n\tmonth: \"qdr:m\",\n\tyear: \"qdr:y\",\n};\n\ninterface WebSearchConfig {\n\tserperApiKey?: unknown;\n}\n\ninterface SerperResult {\n\ttitle?: unknown;\n\tlink?: unknown;\n\tsnippet?: unknown;\n}\n\nlet cachedConfig: WebSearchConfig | null = null;\n\nfunction loadConfig(): WebSearchConfig {\n\tif (cachedConfig) return cachedConfig;\n\tif (!existsSync(CONFIG_PATH)) {\n\t\tcachedConfig = {};\n\t\treturn cachedConfig;\n\t}\n\tconst raw = readFileSync(CONFIG_PATH, \"utf-8\");\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);\n\t}\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\tthrow new Error(`Invalid config in ${CONFIG_PATH}: expected a JSON object`);\n\t}\n\tcachedConfig = parsed as WebSearchConfig;\n\treturn cachedConfig;\n}\n\nasync function getApiKey(signal?: AbortSignal): Promise<string | null> {\n\treturn resolveCredential({\n\t\tprovider: \"Serper\",\n\t\tconfiguredValue: loadConfig().serperApiKey,\n\t\tenvironmentValue: process.env.SERPER_API_KEY,\n\t\tsignal,\n\t});\n}\n\nasync function requireApiKey(signal?: AbortSignal): Promise<string> {\n\tconst apiKey = await getApiKey(signal);\n\tif (!apiKey) {\n\t\tthrow new Error(\n\t\t\t\"Serper API key not found. Either:\\n\" +\n\t\t\t` 1. Create ${CONFIG_PATH} with { \"serperApiKey\": \"your-key\" }\\n` +\n\t\t\t\" 2. Set SERPER_API_KEY environment variable\\n\" +\n\t\t\t\"Get a key at https://serper.dev\",\n\t\t);\n\t}\n\treturn apiKey;\n}\n\nfunction normalizeCount(value: number | undefined): number {\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) return 5;\n\treturn Math.max(1, Math.min(Math.floor(value), 20));\n}\n\ninterface DomainFilters {\n\tinclude: string[];\n\texclude: string[];\n}\n\nfunction normalizeDomain(value: string): string | null {\n\tlet input = value.trim().toLowerCase();\n\tif (!input) return null;\n\tif (input.startsWith(\"-\")) input = input.slice(1).trim();\n\tif (!input) return null;\n\ttry {\n\t\tconst parsed = input.includes(\"://\") ? new URL(input) : new URL(`https://${input}`);\n\t\tinput = parsed.hostname;\n\t} catch {\n\t\tinput = input.split(\"/\")[0]?.split(\":\")[0] ?? \"\";\n\t}\n\tinput = input.replace(/^\\.+|\\.+$/g, \"\");\n\treturn /^[a-z0-9][a-z0-9.-]*\\.[a-z]{2,}$/i.test(input) ? input : null;\n}\n\nfunction parseDomainFilter(domainFilter: string[] | undefined): DomainFilters {\n\tconst filters: DomainFilters = { include: [], exclude: [] };\n\tfor (const raw of domainFilter ?? []) {\n\t\tconst domain = normalizeDomain(raw);\n\t\tif (!domain) continue;\n\t\tconst target = raw.trim().startsWith(\"-\") ? filters.exclude : filters.include;\n\t\tif (!target.includes(domain)) target.push(domain);\n\t}\n\treturn filters;\n}\n\nfunction passesDomainFilters(url: string, filters: DomainFilters): boolean {\n\tif (filters.include.length === 0 && filters.exclude.length === 0) return true;\n\tlet hostname: string;\n\ttry {\n\t\thostname = new URL(url).hostname.toLowerCase();\n\t} catch {\n\t\treturn false;\n\t}\n\tconst matches = (domain: string) => hostname === domain || hostname.endsWith(`.${domain}`);\n\tif (filters.exclude.some(matches)) return false;\n\treturn filters.include.length === 0 || filters.include.some(matches);\n}\n\nfunction buildQuery(query: string, filters: DomainFilters): string {\n\tconst parts = [query];\n\tif (filters.include.length === 1) parts.push(`site:${filters.include[0]}`);\n\tif (filters.include.length > 1) parts.push(`(${filters.include.map(domain => `site:${domain}`).join(\" OR \")})`);\n\tfor (const domain of filters.exclude) parts.push(`-site:${domain}`);\n\treturn parts.join(\" \");\n}\n\nfunction errorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\nfunction invalidResponse(message: string): Error {\n\treturn new Error(`Serper API returned invalid response: ${message}`);\n}\n\nfunction parseResponse(value: unknown): SerperResult[] {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) throw invalidResponse(\"expected an object envelope\");\n\tconst envelope = value as Record<string, unknown>;\n\tif (!Array.isArray(envelope.organic)) throw invalidResponse(\"expected organic array\");\n\treturn envelope.organic as SerperResult[];\n}\n\nfunction buildAnswer(results: SearchResponse[\"results\"]): string {\n\treturn results.map((result) => result.snippet\n\t\t? `${result.snippet}\\nSource: ${result.title} (${result.url})`\n\t\t: `Source: ${result.title} (${result.url})`).join(\"\\n\\n\");\n}\n\nexport function isSerperAvailable(): boolean {\n\treturn hasCredentialSource({ provider: \"Serper\", configuredValue: loadConfig().serperApiKey, environmentValue: process.env.SERPER_API_KEY });\n}\n\nexport async function searchWithSerper(query: string, options: SearchOptions = {}): Promise<SearchResponse> {\n\tconst apiKey = await requireApiKey(options.signal);\n\tconst numResults = normalizeCount(options.numResults);\n\tconst filters = parseDomainFilter(options.domainFilter);\n\tconst requestCount = options.domainFilter?.length ? Math.min(20, numResults + 5) : numResults;\n\tconst activityId = activityMonitor.logStart({ type: \"api\", query });\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(SERPER_SEARCH_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"X-API-KEY\": apiKey, \"Content-Type\": \"application/json\", Accept: \"application/json\" },\n\t\t\tbody: JSON.stringify({ q: buildQuery(query, filters), num: requestCount, ...(options.recencyFilter ? { tbs: RECENCY_TBS[options.recencyFilter] } : {}) }),\n\t\t\tsignal: options.signal ? AbortSignal.any([AbortSignal.timeout(SEARCH_TIMEOUT_MS), options.signal]) : AbortSignal.timeout(SEARCH_TIMEOUT_MS),\n\t\t});\n\t} catch (err) {\n\t\tconst message = errorMessage(err);\n\t\tconst redactedMessage = redactCredential(message, apiKey);\n\t\tif (redactedMessage.toLowerCase().includes(\"abort\")) activityMonitor.logComplete(activityId, 0);\n\t\telse activityMonitor.logError(activityId, redactedMessage);\n\t\tif (redactedMessage === message) throw err;\n\t\tconst redactedError = new Error(redactedMessage);\n\t\tif (err instanceof Error) redactedError.name = err.name;\n\t\tthrow redactedError;\n\t}\n\tif (!response.ok) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tconst errorText = redactCredential(await response.text(), apiKey);\n\t\tthrow new Error(`Serper API error ${response.status}: ${errorText.slice(0, 300)}`);\n\t}\n\tlet rawData: unknown;\n\ttry {\n\t\trawData = await response.json();\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tthrow new Error(`Serper API returned invalid JSON: ${errorMessage(err)}`);\n\t}\n\tlet entries: SerperResult[];\n\ttry {\n\t\tentries = parseResponse(rawData);\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tthrow err;\n\t}\n\tactivityMonitor.logComplete(activityId, response.status);\n\tconst results: SearchResponse[\"results\"] = [];\n\tfor (const entry of entries) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tif (typeof entry.link !== \"string\" || !entry.link) continue;\n\t\tif (!passesDomainFilters(entry.link, filters)) continue;\n\t\tresults.push({\n\t\t\ttitle: typeof entry.title === \"string\" && entry.title.trim() ? entry.title.trim() : `Source ${results.length + 1}`,\n\t\t\turl: entry.link,\n\t\t\tsnippet: typeof entry.snippet === \"string\" ? entry.snippet : \"\",\n\t\t});\n\t\tif (results.length >= numResults) break;\n\t}\n\treturn { answer: buildAnswer(results), results };\n}\n"]}
@@ -0,0 +1,206 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { activityMonitor } from "./activity.js";
3
+ import { hasCredentialSource, redactCredential, resolveCredential } from "./credential-source.js";
4
+ import { getWebSearchConfigPath } from "./utils.js";
5
+ const SERPER_SEARCH_URL = "https://google.serper.dev/search";
6
+ const CONFIG_PATH = getWebSearchConfigPath();
7
+ const SEARCH_TIMEOUT_MS = 60_000;
8
+ const RECENCY_TBS = {
9
+ day: "qdr:d",
10
+ week: "qdr:w",
11
+ month: "qdr:m",
12
+ year: "qdr:y",
13
+ };
14
+ let cachedConfig = null;
15
+ function loadConfig() {
16
+ if (cachedConfig)
17
+ return cachedConfig;
18
+ if (!existsSync(CONFIG_PATH)) {
19
+ cachedConfig = {};
20
+ return cachedConfig;
21
+ }
22
+ const raw = readFileSync(CONFIG_PATH, "utf-8");
23
+ let parsed;
24
+ try {
25
+ parsed = JSON.parse(raw);
26
+ }
27
+ catch (err) {
28
+ const message = err instanceof Error ? err.message : String(err);
29
+ throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
30
+ }
31
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
32
+ throw new Error(`Invalid config in ${CONFIG_PATH}: expected a JSON object`);
33
+ }
34
+ cachedConfig = parsed;
35
+ return cachedConfig;
36
+ }
37
+ async function getApiKey(signal) {
38
+ return resolveCredential({
39
+ provider: "Serper",
40
+ configuredValue: loadConfig().serperApiKey,
41
+ environmentValue: process.env.SERPER_API_KEY,
42
+ signal,
43
+ });
44
+ }
45
+ async function requireApiKey(signal) {
46
+ const apiKey = await getApiKey(signal);
47
+ if (!apiKey) {
48
+ throw new Error("Serper API key not found. Either:\n" +
49
+ ` 1. Create ${CONFIG_PATH} with { "serperApiKey": "your-key" }\n` +
50
+ " 2. Set SERPER_API_KEY environment variable\n" +
51
+ "Get a key at https://serper.dev");
52
+ }
53
+ return apiKey;
54
+ }
55
+ function normalizeCount(value) {
56
+ if (typeof value !== "number" || !Number.isFinite(value))
57
+ return 5;
58
+ return Math.max(1, Math.min(Math.floor(value), 20));
59
+ }
60
+ function normalizeDomain(value) {
61
+ let input = value.trim().toLowerCase();
62
+ if (!input)
63
+ return null;
64
+ if (input.startsWith("-"))
65
+ input = input.slice(1).trim();
66
+ if (!input)
67
+ return null;
68
+ try {
69
+ const parsed = input.includes("://") ? new URL(input) : new URL(`https://${input}`);
70
+ input = parsed.hostname;
71
+ }
72
+ catch {
73
+ input = input.split("/")[0]?.split(":")[0] ?? "";
74
+ }
75
+ input = input.replace(/^\.+|\.+$/g, "");
76
+ return /^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/i.test(input) ? input : null;
77
+ }
78
+ function parseDomainFilter(domainFilter) {
79
+ const filters = { include: [], exclude: [] };
80
+ for (const raw of domainFilter ?? []) {
81
+ const domain = normalizeDomain(raw);
82
+ if (!domain)
83
+ continue;
84
+ const target = raw.trim().startsWith("-") ? filters.exclude : filters.include;
85
+ if (!target.includes(domain))
86
+ target.push(domain);
87
+ }
88
+ return filters;
89
+ }
90
+ function passesDomainFilters(url, filters) {
91
+ if (filters.include.length === 0 && filters.exclude.length === 0)
92
+ return true;
93
+ let hostname;
94
+ try {
95
+ hostname = new URL(url).hostname.toLowerCase();
96
+ }
97
+ catch {
98
+ return false;
99
+ }
100
+ const matches = (domain) => hostname === domain || hostname.endsWith(`.${domain}`);
101
+ if (filters.exclude.some(matches))
102
+ return false;
103
+ return filters.include.length === 0 || filters.include.some(matches);
104
+ }
105
+ function buildQuery(query, filters) {
106
+ const parts = [query];
107
+ if (filters.include.length === 1)
108
+ parts.push(`site:${filters.include[0]}`);
109
+ if (filters.include.length > 1)
110
+ parts.push(`(${filters.include.map(domain => `site:${domain}`).join(" OR ")})`);
111
+ for (const domain of filters.exclude)
112
+ parts.push(`-site:${domain}`);
113
+ return parts.join(" ");
114
+ }
115
+ function errorMessage(err) {
116
+ return err instanceof Error ? err.message : String(err);
117
+ }
118
+ function invalidResponse(message) {
119
+ return new Error(`Serper API returned invalid response: ${message}`);
120
+ }
121
+ function parseResponse(value) {
122
+ if (!value || typeof value !== "object" || Array.isArray(value))
123
+ throw invalidResponse("expected an object envelope");
124
+ const envelope = value;
125
+ if (!Array.isArray(envelope.organic))
126
+ throw invalidResponse("expected organic array");
127
+ return envelope.organic;
128
+ }
129
+ function buildAnswer(results) {
130
+ return results.map((result) => result.snippet
131
+ ? `${result.snippet}\nSource: ${result.title} (${result.url})`
132
+ : `Source: ${result.title} (${result.url})`).join("\n\n");
133
+ }
134
+ export function isSerperAvailable() {
135
+ return hasCredentialSource({ provider: "Serper", configuredValue: loadConfig().serperApiKey, environmentValue: process.env.SERPER_API_KEY });
136
+ }
137
+ export async function searchWithSerper(query, options = {}) {
138
+ const apiKey = await requireApiKey(options.signal);
139
+ const numResults = normalizeCount(options.numResults);
140
+ const filters = parseDomainFilter(options.domainFilter);
141
+ const requestCount = options.domainFilter?.length ? Math.min(20, numResults + 5) : numResults;
142
+ const activityId = activityMonitor.logStart({ type: "api", query });
143
+ let response;
144
+ try {
145
+ response = await fetch(SERPER_SEARCH_URL, {
146
+ method: "POST",
147
+ headers: { "X-API-KEY": apiKey, "Content-Type": "application/json", Accept: "application/json" },
148
+ body: JSON.stringify({ q: buildQuery(query, filters), num: requestCount, ...(options.recencyFilter ? { tbs: RECENCY_TBS[options.recencyFilter] } : {}) }),
149
+ signal: options.signal ? AbortSignal.any([AbortSignal.timeout(SEARCH_TIMEOUT_MS), options.signal]) : AbortSignal.timeout(SEARCH_TIMEOUT_MS),
150
+ });
151
+ }
152
+ catch (err) {
153
+ const message = errorMessage(err);
154
+ const redactedMessage = redactCredential(message, apiKey);
155
+ if (redactedMessage.toLowerCase().includes("abort"))
156
+ activityMonitor.logComplete(activityId, 0);
157
+ else
158
+ activityMonitor.logError(activityId, redactedMessage);
159
+ if (redactedMessage === message)
160
+ throw err;
161
+ const redactedError = new Error(redactedMessage);
162
+ if (err instanceof Error)
163
+ redactedError.name = err.name;
164
+ throw redactedError;
165
+ }
166
+ if (!response.ok) {
167
+ activityMonitor.logComplete(activityId, response.status);
168
+ const errorText = redactCredential(await response.text(), apiKey);
169
+ throw new Error(`Serper API error ${response.status}: ${errorText.slice(0, 300)}`);
170
+ }
171
+ let rawData;
172
+ try {
173
+ rawData = await response.json();
174
+ }
175
+ catch (err) {
176
+ activityMonitor.logComplete(activityId, response.status);
177
+ throw new Error(`Serper API returned invalid JSON: ${errorMessage(err)}`);
178
+ }
179
+ let entries;
180
+ try {
181
+ entries = parseResponse(rawData);
182
+ }
183
+ catch (err) {
184
+ activityMonitor.logComplete(activityId, response.status);
185
+ throw err;
186
+ }
187
+ activityMonitor.logComplete(activityId, response.status);
188
+ const results = [];
189
+ for (const entry of entries) {
190
+ if (!entry || typeof entry !== "object")
191
+ continue;
192
+ if (typeof entry.link !== "string" || !entry.link)
193
+ continue;
194
+ if (!passesDomainFilters(entry.link, filters))
195
+ continue;
196
+ results.push({
197
+ title: typeof entry.title === "string" && entry.title.trim() ? entry.title.trim() : `Source ${results.length + 1}`,
198
+ url: entry.link,
199
+ snippet: typeof entry.snippet === "string" ? entry.snippet : "",
200
+ });
201
+ if (results.length >= numResults)
202
+ break;
203
+ }
204
+ return { answer: buildAnswer(results), results };
205
+ }
206
+ //# sourceMappingURL=serper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serper.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/serper.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { activityMonitor } from \"./activity.ts\";\nimport type { SearchOptions, SearchResponse } from \"./perplexity.ts\";\nimport { hasCredentialSource, redactCredential, resolveCredential } from \"./credential-source.ts\";\nimport { getWebSearchConfigPath } from \"./utils.ts\";\n\nconst SERPER_SEARCH_URL = \"https://google.serper.dev/search\";\nconst CONFIG_PATH = getWebSearchConfigPath();\nconst SEARCH_TIMEOUT_MS = 60_000;\nconst RECENCY_TBS: Record<NonNullable<SearchOptions[\"recencyFilter\"]>, string> = {\n\tday: \"qdr:d\",\n\tweek: \"qdr:w\",\n\tmonth: \"qdr:m\",\n\tyear: \"qdr:y\",\n};\n\ninterface WebSearchConfig {\n\tserperApiKey?: unknown;\n}\n\ninterface SerperResult {\n\ttitle?: unknown;\n\tlink?: unknown;\n\tsnippet?: unknown;\n}\n\nlet cachedConfig: WebSearchConfig | null = null;\n\nfunction loadConfig(): WebSearchConfig {\n\tif (cachedConfig) return cachedConfig;\n\tif (!existsSync(CONFIG_PATH)) {\n\t\tcachedConfig = {};\n\t\treturn cachedConfig;\n\t}\n\tconst raw = readFileSync(CONFIG_PATH, \"utf-8\");\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);\n\t}\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\tthrow new Error(`Invalid config in ${CONFIG_PATH}: expected a JSON object`);\n\t}\n\tcachedConfig = parsed as WebSearchConfig;\n\treturn cachedConfig;\n}\n\nasync function getApiKey(signal?: AbortSignal): Promise<string | null> {\n\treturn resolveCredential({\n\t\tprovider: \"Serper\",\n\t\tconfiguredValue: loadConfig().serperApiKey,\n\t\tenvironmentValue: process.env.SERPER_API_KEY,\n\t\tsignal,\n\t});\n}\n\nasync function requireApiKey(signal?: AbortSignal): Promise<string> {\n\tconst apiKey = await getApiKey(signal);\n\tif (!apiKey) {\n\t\tthrow new Error(\n\t\t\t\"Serper API key not found. Either:\\n\" +\n\t\t\t` 1. Create ${CONFIG_PATH} with { \"serperApiKey\": \"your-key\" }\\n` +\n\t\t\t\" 2. Set SERPER_API_KEY environment variable\\n\" +\n\t\t\t\"Get a key at https://serper.dev\",\n\t\t);\n\t}\n\treturn apiKey;\n}\n\nfunction normalizeCount(value: number | undefined): number {\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) return 5;\n\treturn Math.max(1, Math.min(Math.floor(value), 20));\n}\n\ninterface DomainFilters {\n\tinclude: string[];\n\texclude: string[];\n}\n\nfunction normalizeDomain(value: string): string | null {\n\tlet input = value.trim().toLowerCase();\n\tif (!input) return null;\n\tif (input.startsWith(\"-\")) input = input.slice(1).trim();\n\tif (!input) return null;\n\ttry {\n\t\tconst parsed = input.includes(\"://\") ? new URL(input) : new URL(`https://${input}`);\n\t\tinput = parsed.hostname;\n\t} catch {\n\t\tinput = input.split(\"/\")[0]?.split(\":\")[0] ?? \"\";\n\t}\n\tinput = input.replace(/^\\.+|\\.+$/g, \"\");\n\treturn /^[a-z0-9][a-z0-9.-]*\\.[a-z]{2,}$/i.test(input) ? input : null;\n}\n\nfunction parseDomainFilter(domainFilter: string[] | undefined): DomainFilters {\n\tconst filters: DomainFilters = { include: [], exclude: [] };\n\tfor (const raw of domainFilter ?? []) {\n\t\tconst domain = normalizeDomain(raw);\n\t\tif (!domain) continue;\n\t\tconst target = raw.trim().startsWith(\"-\") ? filters.exclude : filters.include;\n\t\tif (!target.includes(domain)) target.push(domain);\n\t}\n\treturn filters;\n}\n\nfunction passesDomainFilters(url: string, filters: DomainFilters): boolean {\n\tif (filters.include.length === 0 && filters.exclude.length === 0) return true;\n\tlet hostname: string;\n\ttry {\n\t\thostname = new URL(url).hostname.toLowerCase();\n\t} catch {\n\t\treturn false;\n\t}\n\tconst matches = (domain: string) => hostname === domain || hostname.endsWith(`.${domain}`);\n\tif (filters.exclude.some(matches)) return false;\n\treturn filters.include.length === 0 || filters.include.some(matches);\n}\n\nfunction buildQuery(query: string, filters: DomainFilters): string {\n\tconst parts = [query];\n\tif (filters.include.length === 1) parts.push(`site:${filters.include[0]}`);\n\tif (filters.include.length > 1) parts.push(`(${filters.include.map(domain => `site:${domain}`).join(\" OR \")})`);\n\tfor (const domain of filters.exclude) parts.push(`-site:${domain}`);\n\treturn parts.join(\" \");\n}\n\nfunction errorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\nfunction invalidResponse(message: string): Error {\n\treturn new Error(`Serper API returned invalid response: ${message}`);\n}\n\nfunction parseResponse(value: unknown): SerperResult[] {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) throw invalidResponse(\"expected an object envelope\");\n\tconst envelope = value as Record<string, unknown>;\n\tif (!Array.isArray(envelope.organic)) throw invalidResponse(\"expected organic array\");\n\treturn envelope.organic as SerperResult[];\n}\n\nfunction buildAnswer(results: SearchResponse[\"results\"]): string {\n\treturn results.map((result) => result.snippet\n\t\t? `${result.snippet}\\nSource: ${result.title} (${result.url})`\n\t\t: `Source: ${result.title} (${result.url})`).join(\"\\n\\n\");\n}\n\nexport function isSerperAvailable(): boolean {\n\treturn hasCredentialSource({ provider: \"Serper\", configuredValue: loadConfig().serperApiKey, environmentValue: process.env.SERPER_API_KEY });\n}\n\nexport async function searchWithSerper(query: string, options: SearchOptions = {}): Promise<SearchResponse> {\n\tconst apiKey = await requireApiKey(options.signal);\n\tconst numResults = normalizeCount(options.numResults);\n\tconst filters = parseDomainFilter(options.domainFilter);\n\tconst requestCount = options.domainFilter?.length ? Math.min(20, numResults + 5) : numResults;\n\tconst activityId = activityMonitor.logStart({ type: \"api\", query });\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(SERPER_SEARCH_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"X-API-KEY\": apiKey, \"Content-Type\": \"application/json\", Accept: \"application/json\" },\n\t\t\tbody: JSON.stringify({ q: buildQuery(query, filters), num: requestCount, ...(options.recencyFilter ? { tbs: RECENCY_TBS[options.recencyFilter] } : {}) }),\n\t\t\tsignal: options.signal ? AbortSignal.any([AbortSignal.timeout(SEARCH_TIMEOUT_MS), options.signal]) : AbortSignal.timeout(SEARCH_TIMEOUT_MS),\n\t\t});\n\t} catch (err) {\n\t\tconst message = errorMessage(err);\n\t\tconst redactedMessage = redactCredential(message, apiKey);\n\t\tif (redactedMessage.toLowerCase().includes(\"abort\")) activityMonitor.logComplete(activityId, 0);\n\t\telse activityMonitor.logError(activityId, redactedMessage);\n\t\tif (redactedMessage === message) throw err;\n\t\tconst redactedError = new Error(redactedMessage);\n\t\tif (err instanceof Error) redactedError.name = err.name;\n\t\tthrow redactedError;\n\t}\n\tif (!response.ok) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tconst errorText = redactCredential(await response.text(), apiKey);\n\t\tthrow new Error(`Serper API error ${response.status}: ${errorText.slice(0, 300)}`);\n\t}\n\tlet rawData: unknown;\n\ttry {\n\t\trawData = await response.json();\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tthrow new Error(`Serper API returned invalid JSON: ${errorMessage(err)}`);\n\t}\n\tlet entries: SerperResult[];\n\ttry {\n\t\tentries = parseResponse(rawData);\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tthrow err;\n\t}\n\tactivityMonitor.logComplete(activityId, response.status);\n\tconst results: SearchResponse[\"results\"] = [];\n\tfor (const entry of entries) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tif (typeof entry.link !== \"string\" || !entry.link) continue;\n\t\tif (!passesDomainFilters(entry.link, filters)) continue;\n\t\tresults.push({\n\t\t\ttitle: typeof entry.title === \"string\" && entry.title.trim() ? entry.title.trim() : `Source ${results.length + 1}`,\n\t\t\turl: entry.link,\n\t\t\tsnippet: typeof entry.snippet === \"string\" ? entry.snippet : \"\",\n\t\t});\n\t\tif (results.length >= numResults) break;\n\t}\n\treturn { answer: buildAnswer(results), results };\n}\n"]}
@@ -1,3 +1,4 @@
1
+ export type SummaryThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
1
2
  interface SummaryModelScopeContext {
2
3
  cwd: string;
3
4
  isProjectTrusted(): boolean;
@@ -20,6 +21,10 @@ export interface ModelRegistryLike<T extends ModelLike = ModelLike> {
20
21
  export declare function findModelWithProviderRouting<T extends ModelLike>(registry: ModelRegistryLike<T>, provider: string, id: string): T | undefined;
21
22
  export declare function loadEnabledModelPatterns(ctx: SummaryModelScopeContext): string[] | null;
22
23
  export declare function summaryModelValue(model: ModelLike): string;
24
+ export declare function splitThinkingSuffix(value: string): {
25
+ value: string;
26
+ thinkingLevel?: SummaryThinkingLevel;
27
+ };
23
28
  export declare function modelMatchesEnabledPatterns(model: ModelLike, patterns: string[] | null): boolean;
24
29
  export {};
25
30
  //# sourceMappingURL=summary-model-scope.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"summary-model-scope.d.ts","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/summary-model-scope.ts"],"names":[],"mappings":"AAMA,UAAU,wBAAwB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,IAAI,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS;IACjE,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;IAClD,YAAY,IAAI,SAAS,CAAC,EAAE,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,CAAC,SAAS,SAAS,EAC/D,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAC9B,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,GACR,CAAC,GAAG,SAAS,CAWf;AAaD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,wBAAwB,GAAG,MAAM,EAAE,GAAG,IAAI,CAcvF;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAE1D;AAuBD,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,OAAO,CAehG","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../host.ts\";\n\nconst THINKING_LEVELS = new Set([\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"]);\n\ninterface SummaryModelScopeContext {\n\tcwd: string;\n\tisProjectTrusted(): boolean;\n}\n\nexport interface ModelLike {\n\tprovider: string;\n\tid: string;\n}\n\nexport interface ModelRegistryLike<T extends ModelLike = ModelLike> {\n\tfind(provider: string, id: string): T | undefined;\n\tgetAvailable(): readonly T[];\n}\n\n/**\n * Resolve a model through its native provider or a provider that routes that model ID.\n *\n * The direct registry fallback preserves explicit/native model resolution when a\n * provider's availability snapshot does not include the configured model. Callers\n * continue to apply their existing enabled-model and authentication checks.\n */\nexport function findModelWithProviderRouting<T extends ModelLike>(\n\tregistry: ModelRegistryLike<T>,\n\tprovider: string,\n\tid: string,\n): T | undefined {\n\tconst available = registry.getAvailable();\n\tconst direct = available.find(model => model.provider === provider && model.id === id);\n\tif (direct) return direct;\n\n\tconst routedId = `${provider}/${id}`;\n\t// If multiple routers expose the same model ID, Pi's available-model ordering\n\t// determines which route is selected. An explicit provider/model selector can\n\t// select a specific route when that distinction matters.\n\tconst routed = available.find(model => model.id === routedId);\n\treturn routed ?? registry.find(provider, id);\n}\n\nfunction readSettings(path: string): Record<string, unknown> {\n\tif (!existsSync(path)) return {};\n\tconst raw = readFileSync(path, \"utf8\");\n\ttry {\n\t\treturn JSON.parse(raw) as Record<string, unknown>;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${path}: ${message}`);\n\t}\n}\n\nexport function loadEnabledModelPatterns(ctx: SummaryModelScopeContext): string[] | null {\n\tconst globalSettings = readSettings(join(getAgentDir(), \"settings.json\"));\n\tconst projectSettings = ctx.isProjectTrusted()\n\t\t? readSettings(join(ctx.cwd, \".humain-terminal\", \"settings.json\"))\n\t\t: {};\n\tconst value = Object.hasOwn(projectSettings, \"enabledModels\")\n\t\t? projectSettings.enabledModels\n\t\t: globalSettings.enabledModels;\n\tif (value === undefined) return null;\n\tif (!Array.isArray(value)) throw new Error(\"enabledModels must be an array\");\n\treturn value\n\t\t.filter((item): item is string => typeof item === \"string\")\n\t\t.map(item => item.trim())\n\t\t.filter(Boolean);\n}\n\nexport function summaryModelValue(model: ModelLike): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction stripThinkingSuffix(pattern: string): string {\n\tconst index = pattern.lastIndexOf(\":\");\n\tif (index < 0) return pattern;\n\tconst suffix = pattern.slice(index + 1);\n\treturn THINKING_LEVELS.has(suffix) ? pattern.slice(0, index) : pattern;\n}\n\nfunction globToRegExp(pattern: string): RegExp {\n\tlet source = \"^\";\n\tfor (const char of pattern) {\n\t\tif (char === \"*\") {\n\t\t\tsource += \".*\";\n\t\t} else if (char === \"?\") {\n\t\t\tsource += \".\";\n\t\t} else {\n\t\t\tsource += char.replace(/[|\\\\{}()[\\]^$+?.]/g, \"\\\\$&\");\n\t\t}\n\t}\n\treturn new RegExp(`${source}$`, \"i\");\n}\n\nexport function modelMatchesEnabledPatterns(model: ModelLike, patterns: string[] | null): boolean {\n\tif (patterns === null) return true;\n\tconst value = summaryModelValue(model).toLowerCase();\n\tconst id = model.id.toLowerCase();\n\tfor (const rawPattern of patterns) {\n\t\tconst pattern = stripThinkingSuffix(rawPattern.trim()).toLowerCase();\n\t\tif (!pattern) continue;\n\t\tif (pattern.includes(\"*\") || pattern.includes(\"?\")) {\n\t\t\tconst regex = globToRegExp(pattern);\n\t\t\tif (regex.test(value) || regex.test(id)) return true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (pattern === value || pattern === id) return true;\n\t}\n\treturn false;\n}\n"]}
1
+ {"version":3,"file":"summary-model-scope.d.ts","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/summary-model-scope.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAEnG,UAAU,wBAAwB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,IAAI,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS;IACjE,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;IAClD,YAAY,IAAI,SAAS,CAAC,EAAE,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,CAAC,SAAS,SAAS,EAC/D,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAC9B,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,GACR,CAAC,GAAG,SAAS,CAWf;AAaD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,wBAAwB,GAAG,MAAM,EAAE,GAAG,IAAI,CAcvF;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAE1D;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,oBAAoB,CAAA;CAAE,CAO1G;AAoBD,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,OAAO,CAehG","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../host.ts\";\n\nconst THINKING_LEVELS = new Set([\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"]);\n\nexport type SummaryThinkingLevel = \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n\ninterface SummaryModelScopeContext {\n\tcwd: string;\n\tisProjectTrusted(): boolean;\n}\n\nexport interface ModelLike {\n\tprovider: string;\n\tid: string;\n}\n\nexport interface ModelRegistryLike<T extends ModelLike = ModelLike> {\n\tfind(provider: string, id: string): T | undefined;\n\tgetAvailable(): readonly T[];\n}\n\n/**\n * Resolve a model through its native provider or a provider that routes that model ID.\n *\n * The direct registry fallback preserves explicit/native model resolution when a\n * provider's availability snapshot does not include the configured model. Callers\n * continue to apply their existing enabled-model and authentication checks.\n */\nexport function findModelWithProviderRouting<T extends ModelLike>(\n\tregistry: ModelRegistryLike<T>,\n\tprovider: string,\n\tid: string,\n): T | undefined {\n\tconst available = registry.getAvailable();\n\tconst direct = available.find(model => model.provider === provider && model.id === id);\n\tif (direct) return direct;\n\n\tconst routedId = `${provider}/${id}`;\n\t// If multiple routers expose the same model ID, Pi's available-model ordering\n\t// determines which route is selected. An explicit provider/model selector can\n\t// select a specific route when that distinction matters.\n\tconst routed = available.find(model => model.id === routedId);\n\treturn routed ?? registry.find(provider, id);\n}\n\nfunction readSettings(path: string): Record<string, unknown> {\n\tif (!existsSync(path)) return {};\n\tconst raw = readFileSync(path, \"utf8\");\n\ttry {\n\t\treturn JSON.parse(raw) as Record<string, unknown>;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${path}: ${message}`);\n\t}\n}\n\nexport function loadEnabledModelPatterns(ctx: SummaryModelScopeContext): string[] | null {\n\tconst globalSettings = readSettings(join(getAgentDir(), \"settings.json\"));\n\tconst projectSettings = ctx.isProjectTrusted()\n\t\t? readSettings(join(ctx.cwd, \".humain-terminal\", \"settings.json\"))\n\t\t: {};\n\tconst value = Object.hasOwn(projectSettings, \"enabledModels\")\n\t\t? projectSettings.enabledModels\n\t\t: globalSettings.enabledModels;\n\tif (value === undefined) return null;\n\tif (!Array.isArray(value)) throw new Error(\"enabledModels must be an array\");\n\treturn value\n\t\t.filter((item): item is string => typeof item === \"string\")\n\t\t.map(item => item.trim())\n\t\t.filter(Boolean);\n}\n\nexport function summaryModelValue(model: ModelLike): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nexport function splitThinkingSuffix(value: string): { value: string; thinkingLevel?: SummaryThinkingLevel } {\n\tconst index = value.lastIndexOf(\":\");\n\tif (index < 0) return { value };\n\tconst suffix = value.slice(index + 1);\n\treturn THINKING_LEVELS.has(suffix)\n\t\t? { value: value.slice(0, index), thinkingLevel: suffix as SummaryThinkingLevel }\n\t\t: { value };\n}\n\nfunction stripThinkingSuffix(pattern: string): string {\n return splitThinkingSuffix(pattern).value;\n}\n\nfunction globToRegExp(pattern: string): RegExp {\n\tlet source = \"^\";\n\tfor (const char of pattern) {\n\t\tif (char === \"*\") {\n\t\t\tsource += \".*\";\n\t\t} else if (char === \"?\") {\n\t\t\tsource += \".\";\n\t\t} else {\n\t\t\tsource += char.replace(/[|\\\\{}()[\\]^$+?.]/g, \"\\\\$&\");\n\t\t}\n\t}\n\treturn new RegExp(`${source}$`, \"i\");\n}\n\nexport function modelMatchesEnabledPatterns(model: ModelLike, patterns: string[] | null): boolean {\n\tif (patterns === null) return true;\n\tconst value = summaryModelValue(model).toLowerCase();\n\tconst id = model.id.toLowerCase();\n\tfor (const rawPattern of patterns) {\n\t\tconst pattern = stripThinkingSuffix(rawPattern.trim()).toLowerCase();\n\t\tif (!pattern) continue;\n\t\tif (pattern.includes(\"*\") || pattern.includes(\"?\")) {\n\t\t\tconst regex = globToRegExp(pattern);\n\t\t\tif (regex.test(value) || regex.test(id)) return true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (pattern === value || pattern === id) return true;\n\t}\n\treturn false;\n}\n"]}
@@ -53,12 +53,17 @@ export function loadEnabledModelPatterns(ctx) {
53
53
  export function summaryModelValue(model) {
54
54
  return `${model.provider}/${model.id}`;
55
55
  }
56
- function stripThinkingSuffix(pattern) {
57
- const index = pattern.lastIndexOf(":");
56
+ export function splitThinkingSuffix(value) {
57
+ const index = value.lastIndexOf(":");
58
58
  if (index < 0)
59
- return pattern;
60
- const suffix = pattern.slice(index + 1);
61
- return THINKING_LEVELS.has(suffix) ? pattern.slice(0, index) : pattern;
59
+ return { value };
60
+ const suffix = value.slice(index + 1);
61
+ return THINKING_LEVELS.has(suffix)
62
+ ? { value: value.slice(0, index), thinkingLevel: suffix }
63
+ : { value };
64
+ }
65
+ function stripThinkingSuffix(pattern) {
66
+ return splitThinkingSuffix(pattern).value;
62
67
  }
63
68
  function globToRegExp(pattern) {
64
69
  let source = "^";
@@ -1 +1 @@
1
- {"version":3,"file":"summary-model-scope.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/summary-model-scope.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../host.ts\";\n\nconst THINKING_LEVELS = new Set([\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"]);\n\ninterface SummaryModelScopeContext {\n\tcwd: string;\n\tisProjectTrusted(): boolean;\n}\n\nexport interface ModelLike {\n\tprovider: string;\n\tid: string;\n}\n\nexport interface ModelRegistryLike<T extends ModelLike = ModelLike> {\n\tfind(provider: string, id: string): T | undefined;\n\tgetAvailable(): readonly T[];\n}\n\n/**\n * Resolve a model through its native provider or a provider that routes that model ID.\n *\n * The direct registry fallback preserves explicit/native model resolution when a\n * provider's availability snapshot does not include the configured model. Callers\n * continue to apply their existing enabled-model and authentication checks.\n */\nexport function findModelWithProviderRouting<T extends ModelLike>(\n\tregistry: ModelRegistryLike<T>,\n\tprovider: string,\n\tid: string,\n): T | undefined {\n\tconst available = registry.getAvailable();\n\tconst direct = available.find(model => model.provider === provider && model.id === id);\n\tif (direct) return direct;\n\n\tconst routedId = `${provider}/${id}`;\n\t// If multiple routers expose the same model ID, Pi's available-model ordering\n\t// determines which route is selected. An explicit provider/model selector can\n\t// select a specific route when that distinction matters.\n\tconst routed = available.find(model => model.id === routedId);\n\treturn routed ?? registry.find(provider, id);\n}\n\nfunction readSettings(path: string): Record<string, unknown> {\n\tif (!existsSync(path)) return {};\n\tconst raw = readFileSync(path, \"utf8\");\n\ttry {\n\t\treturn JSON.parse(raw) as Record<string, unknown>;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${path}: ${message}`);\n\t}\n}\n\nexport function loadEnabledModelPatterns(ctx: SummaryModelScopeContext): string[] | null {\n\tconst globalSettings = readSettings(join(getAgentDir(), \"settings.json\"));\n\tconst projectSettings = ctx.isProjectTrusted()\n\t\t? readSettings(join(ctx.cwd, \".humain-terminal\", \"settings.json\"))\n\t\t: {};\n\tconst value = Object.hasOwn(projectSettings, \"enabledModels\")\n\t\t? projectSettings.enabledModels\n\t\t: globalSettings.enabledModels;\n\tif (value === undefined) return null;\n\tif (!Array.isArray(value)) throw new Error(\"enabledModels must be an array\");\n\treturn value\n\t\t.filter((item): item is string => typeof item === \"string\")\n\t\t.map(item => item.trim())\n\t\t.filter(Boolean);\n}\n\nexport function summaryModelValue(model: ModelLike): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction stripThinkingSuffix(pattern: string): string {\n\tconst index = pattern.lastIndexOf(\":\");\n\tif (index < 0) return pattern;\n\tconst suffix = pattern.slice(index + 1);\n\treturn THINKING_LEVELS.has(suffix) ? pattern.slice(0, index) : pattern;\n}\n\nfunction globToRegExp(pattern: string): RegExp {\n\tlet source = \"^\";\n\tfor (const char of pattern) {\n\t\tif (char === \"*\") {\n\t\t\tsource += \".*\";\n\t\t} else if (char === \"?\") {\n\t\t\tsource += \".\";\n\t\t} else {\n\t\t\tsource += char.replace(/[|\\\\{}()[\\]^$+?.]/g, \"\\\\$&\");\n\t\t}\n\t}\n\treturn new RegExp(`${source}$`, \"i\");\n}\n\nexport function modelMatchesEnabledPatterns(model: ModelLike, patterns: string[] | null): boolean {\n\tif (patterns === null) return true;\n\tconst value = summaryModelValue(model).toLowerCase();\n\tconst id = model.id.toLowerCase();\n\tfor (const rawPattern of patterns) {\n\t\tconst pattern = stripThinkingSuffix(rawPattern.trim()).toLowerCase();\n\t\tif (!pattern) continue;\n\t\tif (pattern.includes(\"*\") || pattern.includes(\"?\")) {\n\t\t\tconst regex = globToRegExp(pattern);\n\t\t\tif (regex.test(value) || regex.test(id)) return true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (pattern === value || pattern === id) return true;\n\t}\n\treturn false;\n}\n"]}
1
+ {"version":3,"file":"summary-model-scope.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/summary-model-scope.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir } from \"../host.ts\";\n\nconst THINKING_LEVELS = new Set([\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"]);\n\nexport type SummaryThinkingLevel = \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n\ninterface SummaryModelScopeContext {\n\tcwd: string;\n\tisProjectTrusted(): boolean;\n}\n\nexport interface ModelLike {\n\tprovider: string;\n\tid: string;\n}\n\nexport interface ModelRegistryLike<T extends ModelLike = ModelLike> {\n\tfind(provider: string, id: string): T | undefined;\n\tgetAvailable(): readonly T[];\n}\n\n/**\n * Resolve a model through its native provider or a provider that routes that model ID.\n *\n * The direct registry fallback preserves explicit/native model resolution when a\n * provider's availability snapshot does not include the configured model. Callers\n * continue to apply their existing enabled-model and authentication checks.\n */\nexport function findModelWithProviderRouting<T extends ModelLike>(\n\tregistry: ModelRegistryLike<T>,\n\tprovider: string,\n\tid: string,\n): T | undefined {\n\tconst available = registry.getAvailable();\n\tconst direct = available.find(model => model.provider === provider && model.id === id);\n\tif (direct) return direct;\n\n\tconst routedId = `${provider}/${id}`;\n\t// If multiple routers expose the same model ID, Pi's available-model ordering\n\t// determines which route is selected. An explicit provider/model selector can\n\t// select a specific route when that distinction matters.\n\tconst routed = available.find(model => model.id === routedId);\n\treturn routed ?? registry.find(provider, id);\n}\n\nfunction readSettings(path: string): Record<string, unknown> {\n\tif (!existsSync(path)) return {};\n\tconst raw = readFileSync(path, \"utf8\");\n\ttry {\n\t\treturn JSON.parse(raw) as Record<string, unknown>;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${path}: ${message}`);\n\t}\n}\n\nexport function loadEnabledModelPatterns(ctx: SummaryModelScopeContext): string[] | null {\n\tconst globalSettings = readSettings(join(getAgentDir(), \"settings.json\"));\n\tconst projectSettings = ctx.isProjectTrusted()\n\t\t? readSettings(join(ctx.cwd, \".humain-terminal\", \"settings.json\"))\n\t\t: {};\n\tconst value = Object.hasOwn(projectSettings, \"enabledModels\")\n\t\t? projectSettings.enabledModels\n\t\t: globalSettings.enabledModels;\n\tif (value === undefined) return null;\n\tif (!Array.isArray(value)) throw new Error(\"enabledModels must be an array\");\n\treturn value\n\t\t.filter((item): item is string => typeof item === \"string\")\n\t\t.map(item => item.trim())\n\t\t.filter(Boolean);\n}\n\nexport function summaryModelValue(model: ModelLike): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nexport function splitThinkingSuffix(value: string): { value: string; thinkingLevel?: SummaryThinkingLevel } {\n\tconst index = value.lastIndexOf(\":\");\n\tif (index < 0) return { value };\n\tconst suffix = value.slice(index + 1);\n\treturn THINKING_LEVELS.has(suffix)\n\t\t? { value: value.slice(0, index), thinkingLevel: suffix as SummaryThinkingLevel }\n\t\t: { value };\n}\n\nfunction stripThinkingSuffix(pattern: string): string {\n return splitThinkingSuffix(pattern).value;\n}\n\nfunction globToRegExp(pattern: string): RegExp {\n\tlet source = \"^\";\n\tfor (const char of pattern) {\n\t\tif (char === \"*\") {\n\t\t\tsource += \".*\";\n\t\t} else if (char === \"?\") {\n\t\t\tsource += \".\";\n\t\t} else {\n\t\t\tsource += char.replace(/[|\\\\{}()[\\]^$+?.]/g, \"\\\\$&\");\n\t\t}\n\t}\n\treturn new RegExp(`${source}$`, \"i\");\n}\n\nexport function modelMatchesEnabledPatterns(model: ModelLike, patterns: string[] | null): boolean {\n\tif (patterns === null) return true;\n\tconst value = summaryModelValue(model).toLowerCase();\n\tconst id = model.id.toLowerCase();\n\tfor (const rawPattern of patterns) {\n\t\tconst pattern = stripThinkingSuffix(rawPattern.trim()).toLowerCase();\n\t\tif (!pattern) continue;\n\t\tif (pattern.includes(\"*\") || pattern.includes(\"?\")) {\n\t\t\tconst regex = globToRegExp(pattern);\n\t\t\tif (regex.test(value) || regex.test(id)) return true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (pattern === value || pattern === id) return true;\n\t}\n\treturn false;\n}\n"]}