@herbertgao/pi-extensions 2026.9.20 → 2026.10.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.
- package/README.md +18 -13
- package/THIRD_PARTY_NOTICES.md +31 -5
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +1 -1
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +1 -1
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +1 -1
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +1 -1
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +1 -1
- package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/index.js +4 -0
- package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail-frontmatter.cjs +43 -3
- package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail.mjs +142 -59
- package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +1 -1
- package/node_modules/@dietrichgebert/ponytail/README.es.md +87 -204
- package/node_modules/@dietrichgebert/ponytail/README.ja.md +212 -0
- package/node_modules/@dietrichgebert/ponytail/README.ko.md +92 -226
- package/node_modules/@dietrichgebert/ponytail/README.md +58 -244
- package/node_modules/@dietrichgebert/ponytail/README.zh-CN.md +212 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/claude-codex-hooks.json +3 -3
- package/node_modules/@dietrichgebert/ponytail/hooks/copilot-hooks.json +4 -4
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +63 -16
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-config.js +8 -7
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-mode-tracker.js +46 -23
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-runtime.js +39 -7
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-subagent.js +20 -3
- package/node_modules/@dietrichgebert/ponytail/hooks/qoder-hooks.json +3 -3
- package/node_modules/@dietrichgebert/ponytail/package.json +2 -2
- package/node_modules/@dietrichgebert/ponytail/pi-extension/index.js +47 -17
- package/node_modules/@dietrichgebert/ponytail/scripts/uninstall.js +21 -7
- package/node_modules/@dietrichgebert/ponytail/skills/plugin.json +5 -0
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +7 -2
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +1 -1
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +16 -14
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +3 -3
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +12 -7
- package/node_modules/@herbertgao/pi-bark/README.md +39 -3
- package/node_modules/@herbertgao/pi-bark/package.json +1 -1
- package/node_modules/@herbertgao/pi-bark/src/index.ts +275 -15
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +50 -50
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +7 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +25 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +24 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -4
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer.ts +47 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/mcp-chip.ts +136 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +56 -11
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +36 -4
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +11 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/codemode.ts +303 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-header.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-highlight.ts +36 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-presentation.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-write-render.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +88 -34
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts +6 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +31 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/message-display.ts +3 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/names.ts +3 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +173 -36
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-text.ts +1 -0
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +6 -4
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +1 -1
- package/node_modules/@herbertgao/pi-subagents/src/mention-clone.ts +3 -2
- package/node_modules/@herbertgao/sol-pi/CONTRIBUTING.md +22 -0
- package/node_modules/@herbertgao/sol-pi/README.md +36 -17
- package/node_modules/@herbertgao/sol-pi/THIRD_PARTY_NOTICES.md +10 -6
- package/node_modules/@herbertgao/sol-pi/agents-install.md +15 -12
- package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +24 -8
- package/node_modules/@herbertgao/sol-pi/docs/configuration.md +2 -2
- package/node_modules/@herbertgao/sol-pi/docs/star-history.md +18 -0
- package/node_modules/@herbertgao/sol-pi/package.json +4 -2
- package/node_modules/@herbertgao/sol-pi/scripts/check-sol-pi-config.mjs +1 -1
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/config.ts +1 -1
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/file-queue.ts +23 -4
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/index.ts +32 -1
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/then-run.ts +3 -2
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/observation.ts +1 -1
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/economics.ts +27 -7
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +137 -41
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/index.ts +1 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/plan.ts +2 -1
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/projection.ts +58 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/state.ts +28 -6
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/tools.ts +2 -1
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/runtime-paths.ts +15 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@narumitw/pi-caffeinate/package.json +4 -4
- package/node_modules/@pi-plugins/fast-mode/README.md +7 -1
- package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +7 -1
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/package.json +5 -5
- package/node_modules/pi-antigravity/README.md +3 -0
- package/node_modules/pi-antigravity/package.json +8 -8
- package/node_modules/pi-antigravity/src/auth/oauth.ts +6 -3
- package/node_modules/pi-antigravity/src/index.ts +114 -110
- package/node_modules/pi-antigravity/src/stream/stream.ts +1 -1
- package/node_modules/pi-antigravity/src/utils/util.ts +18 -1
- package/node_modules/pi-multi-account/CHANGELOG.md +20 -0
- package/node_modules/pi-multi-account/README.md +46 -8
- package/node_modules/pi-multi-account/config-file-transaction.ts +42 -0
- package/node_modules/pi-multi-account/index.ts +255 -269
- package/node_modules/pi-multi-account/model-pins.ts +36 -0
- package/node_modules/pi-multi-account/package.json +9 -7
- package/node_modules/pi-multi-account/slot-proxy-auth.ts +12 -2
- package/node_modules/pi-multi-account/slot-proxy.ts +27 -5
- package/node_modules/pi-typesafe/README.md +1 -1
- package/node_modules/pi-typesafe/dist/backends.d.ts +15 -5
- package/node_modules/pi-typesafe/dist/backends.js +18 -0
- package/node_modules/pi-typesafe/dist/client.d.ts +4 -3
- package/node_modules/pi-typesafe/dist/client.js +22 -7
- package/node_modules/pi-typesafe/dist/index.d.ts +2 -2
- package/node_modules/pi-typesafe/dist/index.js +1 -1
- package/node_modules/pi-typesafe/dist/usage.d.ts +14 -4
- package/node_modules/pi-typesafe/dist/usage.js +147 -42
- package/node_modules/pi-typesafe/package.json +1 -1
- package/node_modules/pi-web-access/CHANGELOG.md +65 -0
- package/node_modules/pi-web-access/README.md +63 -14
- package/node_modules/pi-web-access/brave-rate-limit.ts +176 -0
- package/node_modules/pi-web-access/brave.ts +28 -11
- package/node_modules/pi-web-access/curator-page.ts +5 -1
- package/node_modules/pi-web-access/curator-server.ts +2 -0
- package/node_modules/pi-web-access/declared-web-links.ts +2 -2
- package/node_modules/pi-web-access/degoog.ts +323 -0
- package/node_modules/pi-web-access/dist/index.js +3647 -2740
- package/node_modules/pi-web-access/dist/mcp-cli.js +27443 -0
- package/node_modules/pi-web-access/exa.ts +5 -1
- package/node_modules/pi-web-access/extract.ts +41 -3
- package/node_modules/pi-web-access/firecrawl.ts +35 -4
- package/node_modules/pi-web-access/gemini-search.ts +36 -16
- package/node_modules/pi-web-access/gemini-web.ts +14 -9
- package/node_modules/pi-web-access/index.ts +164 -1207
- package/node_modules/pi-web-access/keenable.ts +221 -0
- package/node_modules/pi-web-access/mcp-cli.ts +40 -0
- package/node_modules/pi-web-access/mcp-server.ts +81 -0
- package/node_modules/pi-web-access/mcp-tool-definitions.ts +117 -0
- package/node_modules/pi-web-access/openai-search.ts +58 -11
- package/node_modules/pi-web-access/package.json +23 -5
- package/node_modules/pi-web-access/perplexity.ts +64 -56
- package/node_modules/pi-web-access/summary-review.ts +8 -2
- package/node_modules/pi-web-access/tool-arguments.ts +20 -0
- package/node_modules/pi-web-access/video-extract.ts +1 -1
- package/node_modules/pi-web-access/web-tool-contract.ts +86 -0
- package/node_modules/pi-web-access/web-tool-core.ts +1502 -0
- package/node_modules/pi-web-access/youtube-extract.ts +2 -5
- package/node_modules/{@herbertgao/resume-from → resume-from}/README.md +16 -12
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/adapter.d.ts +0 -5
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/adapter.js +2 -3
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/contract.d.ts +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/reader.d.ts +3 -0
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/reader.js +27 -15
- package/node_modules/{@herbertgao/resume-from/dist/adapters/codex → resume-from/dist/adapters/claude-code}/redaction.js +16 -3
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/writer.d.ts +2 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/writer.js +1 -1
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/contract.d.ts +2 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/index.js +1 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/read.d.ts +16 -0
- package/node_modules/resume-from/dist/adapters/codex/read.js +723 -0
- package/node_modules/{@herbertgao/resume-from/dist/adapters/pi → resume-from/dist/adapters/codex}/redaction.js +16 -3
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/rollout.d.ts +44 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/rollout.js +88 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/write.d.ts +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/write.js +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/contract.d.ts +5 -1
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/adapter.js +10 -6
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/contract.d.ts +2 -2
- package/node_modules/{@herbertgao/resume-from/dist/adapters/claude-code → resume-from/dist/adapters/pi}/redaction.js +16 -3
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/serialize.d.ts +2 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/serialize.js +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/render.d.ts +1 -1
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/render.js +4 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/runner.js +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/contract.d.ts +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/command.js +31 -8
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/wiring.d.ts +1 -1
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/wiring.js +1 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/confirmation.d.ts +4 -1
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/confirmation.js +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/contract.d.ts +8 -6
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/contract.d.ts +10 -5
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/finder.d.ts +2 -1
- package/node_modules/resume-from/dist/import/discovery/finder.js +269 -0
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/contract.d.ts +3 -3
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/lander.js +3 -3
- package/node_modules/resume-from/dist/import/pipeline.d.ts +28 -0
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/pipeline.js +31 -11
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/builder.d.ts +2 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/builder.js +9 -5
- package/node_modules/resume-from/dist/import/preview/warnings.d.ts +14 -0
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/warnings.js +26 -20
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/wiring.js +12 -2
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/contract.d.ts +9 -3
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/git.d.ts +4 -4
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/git.js +7 -4
- package/node_modules/resume-from/dist/platform/repo/reader.d.ts +3 -0
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/reader.js +60 -20
- package/node_modules/{@herbertgao/resume-from → resume-from}/dist/session/contract.d.ts +8 -1
- package/node_modules/{@herbertgao/resume-from → resume-from}/package.json +19 -32
- package/node_modules/{@herbertgao/resume-from → resume-from}/shims/pi/extensions/resume-from.js +2 -2
- package/package.json +21 -21
- package/node_modules/@dietrichgebert/ponytail/assets/benchmark-3model.svg +0 -21
- package/node_modules/@dietrichgebert/ponytail/assets/benchmark-agentic.svg +0 -62
- package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.png +0 -0
- package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.svg +0 -115
- package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt-dark.svg +0 -27
- package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt.svg +0 -27
- package/node_modules/@dietrichgebert/ponytail/assets/logo.png +0 -0
- package/node_modules/@dietrichgebert/ponytail/assets/retriever-icon.png +0 -0
- package/node_modules/@dietrichgebert/ponytail/assets/retriever-logo-dark.svg +0 -4
- package/node_modules/@dietrichgebert/ponytail/assets/retriever-logo-light.svg +0 -4
- package/node_modules/@dietrichgebert/ponytail/assets/social-preview.png +0 -0
- package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-es.png +0 -0
- package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-ko.png +0 -0
- package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner.png +0 -0
- package/node_modules/@herbertgao/resume-from/dist/adapters/codex/read.js +0 -270
- package/node_modules/@herbertgao/resume-from/dist/import/discovery/finder.js +0 -129
- package/node_modules/@herbertgao/resume-from/dist/import/pipeline.d.ts +0 -17
- package/node_modules/@herbertgao/resume-from/dist/import/preview/warnings.d.ts +0 -11
- package/node_modules/@herbertgao/resume-from/dist/platform/repo/reader.d.ts +0 -8
- /package/node_modules/{@herbertgao/resume-from → resume-from}/LICENSE +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/entries.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/entries.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/layout.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/layout.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/readback.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/readback.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/redaction.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/validation.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/claude-code/validation.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/readback.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/readback.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/redaction.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/validation.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/codex/validation.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/adapter.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/format.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/format.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/parse.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/parse.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/redaction.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/validate.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/adapters/pi/validate.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/bin.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/bin.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/agents.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/agents.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/args.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/args.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/presentation.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/presentation.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/cli/runner.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/entry.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/entry.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/command.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/picker.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/picker.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/presentation.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/presentation.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/register.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/register.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/ui.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/pi-extension/ui.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/profile.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/profile.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/registry.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/host/registry.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/errors.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/errors.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/homes.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/homes.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/ordering.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/discovery/ordering.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/errors.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/errors.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/errors.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/errors.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/handover.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/handover.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/lander.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/marker.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/landing/marker.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/format.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/format.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/preview/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/rules.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/transfer/rules.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/import/wiring.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/defaults.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/defaults.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/errors.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/errors.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/loader.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/loader.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/paths.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/paths.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/validate.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/config/validate.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/repo/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/file-committer.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/file-committer.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/store/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/contract.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/contract.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/estimator.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/estimator.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/index.d.ts +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/platform/tokens/index.js +0 -0
- /package/node_modules/{@herbertgao/resume-from → resume-from}/dist/session/contract.js +0 -0
|
@@ -1,30 +1,22 @@
|
|
|
1
1
|
import type { AgentToolResult, ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { Box, Text, truncateToWidth, type KeyId } from "@earendil-works/pi-tui";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
|
-
import
|
|
5
|
-
import type { ImageContent, TextContent } from "@earendil-works/pi-ai/compat";
|
|
6
|
-
import type { ExtractedContent, ExtractOptions } from "./extract.ts";
|
|
4
|
+
import type { ExtractedContent } from "./extract.ts";
|
|
7
5
|
import { normalizeFetchContentParams } from "./fetch-params.ts";
|
|
8
|
-
import { resolveAuthFetchProfile, type AuthFetchProfile } from "./auth-fetch.ts";
|
|
9
|
-
import { findContent, type FindMode } from "./content-find.ts";
|
|
10
6
|
import { answerFromPage } from "./page-query.ts";
|
|
11
7
|
import { rewriteSearchQuery } from "./query-rewrite.ts";
|
|
12
8
|
import { clearCloneCache } from "./github-extract.ts";
|
|
13
|
-
import { ALL_SEARCH_PROVIDERS,
|
|
9
|
+
import { ALL_SEARCH_PROVIDERS, getAllowedSearchProviders, getConfiguredSearchRouting, providerLabel, RESOLVED_SEARCH_PROVIDERS, search, type AttributedSearchResponse, type ProviderAvailability, type SearchProvider, type SearchProviderSelection, type ResolvedSearchProvider } from "./gemini-search.ts";
|
|
14
10
|
export type { ProviderAvailability } from "./gemini-search.ts";
|
|
15
|
-
import
|
|
16
|
-
import { formatSeconds, getWebSearchConfigDir, getWebSearchConfigPath, resolveCuratorNetworkConfig, runWithProxy } from "./utils.ts";
|
|
11
|
+
import { formatSeconds, getWebSearchConfigDir, resolveCuratorNetworkConfig, runWithProxy } from "./utils.ts";
|
|
17
12
|
import {
|
|
18
13
|
clearResults,
|
|
19
14
|
deleteResult,
|
|
20
15
|
generateId,
|
|
21
16
|
getAllResults,
|
|
22
|
-
getResult,
|
|
23
17
|
restoreFromSession,
|
|
24
18
|
storeFetchedContentResult,
|
|
25
|
-
storeResult,
|
|
26
19
|
type QueryResultData,
|
|
27
|
-
type StoredSearchData,
|
|
28
20
|
} from "./storage.ts";
|
|
29
21
|
import { activityMonitor, type ActivityEntry } from "./activity.ts";
|
|
30
22
|
import { startCuratorServer, type CuratorSearchEntry, type CuratorServerHandle, type IndexedCuratorSearchEntry } from "./curator-server.ts";
|
|
@@ -47,9 +39,10 @@ import { isGeminiApiAvailable } from "./gemini-api.ts";
|
|
|
47
39
|
import { getActiveGoogleEmail, getGeminiWebAvailabilityDiagnostic, getGeminiWebAvailabilityDiagnosticDetails, isGeminiWebAvailable } from "./gemini-web.ts";
|
|
48
40
|
import { isBrowserCookieAccessAllowed } from "./gemini-web-config.ts";
|
|
49
41
|
import { isBraveAvailable } from "./brave.ts";
|
|
50
|
-
import { isCurrentModelHostedSearchEligible, isOpenAISearchAvailable } from "./openai-search.ts";
|
|
42
|
+
import { isCurrentModelHostedSearchEligible, isOpenAISearchAvailable, isOpenAISubscriptionModelSelected } from "./openai-search.ts";
|
|
51
43
|
import { isParallelAvailable } from "./parallel.ts";
|
|
52
44
|
import { isParallelMcpAvailable } from "./parallel-mcp.ts";
|
|
45
|
+
import { parseStringifiedArrays } from "./tool-arguments.ts";
|
|
53
46
|
import { isTinyFishAvailable } from "./tinyfish.ts";
|
|
54
47
|
import { isSearch1APIAvailable } from "./search1api.ts";
|
|
55
48
|
import { isSearchinfinityAvailable } from "./searchinfinity.ts";
|
|
@@ -63,6 +56,7 @@ import { isKagiAvailable } from "./kagi.ts";
|
|
|
63
56
|
import { isBochaAvailable } from "./bocha.ts";
|
|
64
57
|
import { isOllamaAvailable } from "./ollama.ts";
|
|
65
58
|
import { isSearXNGAvailable } from "./searxng.ts";
|
|
59
|
+
import { isDegoogAvailable } from "./degoog.ts";
|
|
66
60
|
import { isDuckDuckGoAvailable } from "./duckduckgo.ts";
|
|
67
61
|
import { isAnySearchAvailable } from "./anysearch.ts";
|
|
68
62
|
import { isXaiSearchAvailable } from "./xai-search.ts";
|
|
@@ -73,6 +67,7 @@ import { isSerpBaseAvailable } from "./serpbase.ts";
|
|
|
73
67
|
import { isSerpApiAvailable } from "./serpapi.ts";
|
|
74
68
|
import { isSerperAvailable } from "./serper.ts";
|
|
75
69
|
import { isSerplyAvailable } from "./serply.ts";
|
|
70
|
+
import { isKeenableAvailable } from "./keenable.ts";
|
|
76
71
|
import { isBaizhiAvailable } from "./baizhi.ts";
|
|
77
72
|
import { isZaiAvailable } from "./zai.ts";
|
|
78
73
|
import { isValyuAvailable } from "./valyu.ts";
|
|
@@ -80,15 +75,38 @@ import { isXcrawlAvailable } from "./xcrawl.ts";
|
|
|
80
75
|
import { buildSearchErrorPlan, type SearchErrorDetails, type SearchErrorPlan } from "./render-search-error.ts";
|
|
81
76
|
import { findModelWithProviderRouting, isModelInScope, splitThinkingSuffix } from "./summary-model-scope.ts";
|
|
82
77
|
import { registerCuratorRunLifecycle, resolveWebSearchWorkflow, type WebSearchWorkflow } from "./curator-run.ts";
|
|
83
|
-
import {
|
|
84
|
-
buildResearchArtifact,
|
|
85
|
-
withClaimAssessment,
|
|
86
|
-
storeResearchArtifact,
|
|
87
|
-
getResearchArtifact,
|
|
88
|
-
type RecencyFilter,
|
|
89
|
-
type ResearchArtifact,
|
|
90
|
-
} from "./source-check.ts";
|
|
91
78
|
import { registerWebToolActivation } from "./tool-activation.ts";
|
|
79
|
+
import {
|
|
80
|
+
createWebToolCore,
|
|
81
|
+
DEFAULT_TOOL_NAMES,
|
|
82
|
+
expandQueryString,
|
|
83
|
+
fetchAllContent,
|
|
84
|
+
fetchUncoveredContent,
|
|
85
|
+
getMaxInlineContentChars,
|
|
86
|
+
isToolEnabled,
|
|
87
|
+
loadConfig,
|
|
88
|
+
markToolError,
|
|
89
|
+
normalizeProviderInput,
|
|
90
|
+
normalizeQueryList,
|
|
91
|
+
normalizeRecencyFilter,
|
|
92
|
+
parseConfigRoot,
|
|
93
|
+
registeredFetchToolNames,
|
|
94
|
+
resolveFetchModeConfig,
|
|
95
|
+
resolveRequestedProvider,
|
|
96
|
+
runSearchQueries,
|
|
97
|
+
storedContentSourceNames,
|
|
98
|
+
stripThumbnails,
|
|
99
|
+
toCuratorProvider,
|
|
100
|
+
WEB_SEARCH_CONFIG_PATH,
|
|
101
|
+
withRegisteredFetchOptions,
|
|
102
|
+
type FetchedContentData,
|
|
103
|
+
type FetchMode,
|
|
104
|
+
type SearchReturnOptions,
|
|
105
|
+
type SummaryWorkflow,
|
|
106
|
+
type ToolNames,
|
|
107
|
+
type WebSearchCallOptions,
|
|
108
|
+
type WebSearchConfig,
|
|
109
|
+
} from "./web-tool-core.ts";
|
|
92
110
|
|
|
93
111
|
// Match pi-ai's StringEnum without loading its compat barrel during registration.
|
|
94
112
|
function StringEnum<T extends string[]>(values: T, options?: { description?: string; default?: T[number] }) {
|
|
@@ -102,33 +120,33 @@ function StringEnum<T extends string[]>(values: T, options?: { description?: str
|
|
|
102
120
|
|
|
103
121
|
type ExtensionTheme = ExtensionContext["ui"]["theme"];
|
|
104
122
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
123
|
+
// structuredContent shapes that codemode scripts receive (web-tool-contract.ts),
|
|
124
|
+
// on success and on isError results.
|
|
125
|
+
const nullableString = (description: string) => Type.Union([Type.String(), Type.Null()], { description });
|
|
126
|
+
const webSearchOutputSchema = Type.Object({
|
|
127
|
+
responseId: Type.String({ description: "Id of the stored search results" }),
|
|
128
|
+
fetchId: nullableString("Stored page content id when includeContent fetched or started fetching content, else null"),
|
|
129
|
+
queries: Type.Array(Type.Object({
|
|
130
|
+
query: Type.String(),
|
|
131
|
+
answer: Type.String({ description: "Provider answer text, empty when the provider returned only results" }),
|
|
132
|
+
error: nullableString("Why this query failed, null on success"),
|
|
133
|
+
provider: Type.Optional(Type.String()),
|
|
134
|
+
providers: Type.Optional(Type.Array(Type.String())),
|
|
135
|
+
results: Type.Array(Type.Object({ title: Type.String(), url: Type.String(), snippet: Type.String() })),
|
|
136
|
+
})),
|
|
137
|
+
});
|
|
138
|
+
const fetchContentOutputSchema = Type.Object({
|
|
139
|
+
responseId: nullableString("Id of the stored content, null when nothing was stored"),
|
|
140
|
+
urls: Type.Array(Type.Object({
|
|
141
|
+
url: Type.String(),
|
|
142
|
+
title: Type.String(),
|
|
143
|
+
content: Type.String({ description: "Full extracted content, not limited by the inline character cap" }),
|
|
144
|
+
error: nullableString("Why this URL failed, null on success"),
|
|
145
|
+
mimeType: Type.Optional(Type.String()),
|
|
146
|
+
status: Type.Optional(Type.Number()),
|
|
147
|
+
duration: Type.Optional(Type.Number()),
|
|
148
|
+
})),
|
|
149
|
+
});
|
|
132
150
|
|
|
133
151
|
/** Shared collapsed/expanded renderer for an error/cancel plan produced by
|
|
134
152
|
* buildSearchErrorPlan(). Used by every tool renderResult's error branch so
|
|
@@ -148,59 +166,8 @@ function renderSearchErrorPlan(plan: SearchErrorPlan, expanded: boolean, theme:
|
|
|
148
166
|
return box;
|
|
149
167
|
}
|
|
150
168
|
|
|
151
|
-
interface WebSearchConfig {
|
|
152
|
-
anysearchApiKey?: unknown;
|
|
153
|
-
xcrawlApiKey?: unknown;
|
|
154
|
-
brightdataApiKey?: unknown;
|
|
155
|
-
brightdataSerpZone?: unknown;
|
|
156
|
-
kagiApiKey?: unknown;
|
|
157
|
-
ollamaApiKey?: unknown;
|
|
158
|
-
serpbaseApiKey?: unknown;
|
|
159
|
-
serpapiApiKey?: unknown;
|
|
160
|
-
serperApiKey?: unknown;
|
|
161
|
-
serplyApiKey?: unknown;
|
|
162
|
-
youApiKey?: unknown;
|
|
163
|
-
baizhiApiKey?: unknown;
|
|
164
|
-
zaiApiKey?: unknown;
|
|
165
|
-
tinyfishApiKey?: unknown;
|
|
166
|
-
valyuApiKey?: unknown;
|
|
167
|
-
xaiApiKey?: unknown;
|
|
168
|
-
provider?: unknown;
|
|
169
|
-
searchProvider?: unknown;
|
|
170
|
-
workflow?: string;
|
|
171
|
-
curatorTimeoutSeconds?: unknown;
|
|
172
|
-
autoOpenBrowser?: unknown;
|
|
173
|
-
curatorRemote?: unknown;
|
|
174
|
-
summaryModel?: string;
|
|
175
|
-
summaryGenerationDeadlineMs?: unknown;
|
|
176
|
-
maxInlineContentChars?: unknown;
|
|
177
|
-
fetch?: {
|
|
178
|
-
defaultMode?: unknown;
|
|
179
|
-
allowedModes?: unknown;
|
|
180
|
-
};
|
|
181
|
-
webSearch?: {
|
|
182
|
-
enabled?: boolean;
|
|
183
|
-
allowedProviders?: unknown;
|
|
184
|
-
};
|
|
185
|
-
tools?: Partial<Record<keyof ToolNames, { enabled?: boolean }>>;
|
|
186
|
-
toolActivation?: unknown;
|
|
187
|
-
commands?: Partial<Record<"websearch" | "curator" | "search" | "google-account", { enabled?: boolean }>>;
|
|
188
|
-
toolNames?: Partial<ToolNames>;
|
|
189
|
-
shortcuts?: {
|
|
190
|
-
curate?: KeyId;
|
|
191
|
-
activity?: KeyId;
|
|
192
|
-
};
|
|
193
|
-
ssrf?: {
|
|
194
|
-
/** CIDR ranges exempted from the SSRF guard (e.g. fake-IP proxy ranges). */
|
|
195
|
-
allowRanges?: string[];
|
|
196
|
-
/** Skip local hostname DNS preflight when an HTTP(S)_PROXY env var applies. */
|
|
197
|
-
trustEnvProxy?: boolean;
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
169
|
type CuratorWorkflow = "summary-review";
|
|
202
170
|
export type CuratorProvider = SearchProvider;
|
|
203
|
-
type SummaryWorkflow = "summary-review" | "auto-summary";
|
|
204
171
|
|
|
205
172
|
interface CuratorBootstrap {
|
|
206
173
|
availableProviders: ProviderAvailability;
|
|
@@ -208,25 +175,6 @@ interface CuratorBootstrap {
|
|
|
208
175
|
timeoutSeconds: number;
|
|
209
176
|
}
|
|
210
177
|
|
|
211
|
-
function parseConfigRoot(raw: string): Record<string, unknown> {
|
|
212
|
-
let parsed: unknown;
|
|
213
|
-
try {
|
|
214
|
-
parsed = JSON.parse(raw);
|
|
215
|
-
} catch (err) {
|
|
216
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
217
|
-
throw new Error(`Failed to parse ${WEB_SEARCH_CONFIG_PATH}: ${message}`);
|
|
218
|
-
}
|
|
219
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
220
|
-
throw new Error(`Invalid config in ${WEB_SEARCH_CONFIG_PATH}: expected a JSON object`);
|
|
221
|
-
}
|
|
222
|
-
return parsed as Record<string, unknown>;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function loadConfig(): WebSearchConfig {
|
|
226
|
-
if (!existsSync(WEB_SEARCH_CONFIG_PATH)) return {};
|
|
227
|
-
return parseConfigRoot(readFileSync(WEB_SEARCH_CONFIG_PATH, "utf-8")) as WebSearchConfig;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
178
|
function saveConfig(updates: Partial<WebSearchConfig>): void {
|
|
231
179
|
let config: Record<string, unknown> = {};
|
|
232
180
|
if (existsSync(WEB_SEARCH_CONFIG_PATH)) {
|
|
@@ -239,57 +187,18 @@ function saveConfig(updates: Partial<WebSearchConfig>): void {
|
|
|
239
187
|
writeFileSync(WEB_SEARCH_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
|
|
240
188
|
}
|
|
241
189
|
|
|
242
|
-
type ToolNames = {
|
|
243
|
-
webSearch: string;
|
|
244
|
-
sourceCheck: string;
|
|
245
|
-
fetchContent: string;
|
|
246
|
-
getSearchContent: string;
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
const DEFAULT_TOOL_NAMES: ToolNames = {
|
|
250
|
-
webSearch: "web_search",
|
|
251
|
-
sourceCheck: "source_check",
|
|
252
|
-
fetchContent: "fetch_content",
|
|
253
|
-
getSearchContent: "get_search_content",
|
|
254
|
-
};
|
|
255
190
|
const TOOL_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
|
|
256
191
|
const DEFAULT_SHORTCUTS = { curate: "ctrl+shift+s", activity: "ctrl+shift+w" } satisfies Record<string, KeyId>;
|
|
257
192
|
const DEFAULT_CURATOR_TIMEOUT_SECONDS = 20;
|
|
258
193
|
const DEFAULT_REMOTE_CURATOR_TIMEOUT_SECONDS = 60;
|
|
259
194
|
const MAX_CURATOR_TIMEOUT_SECONDS = 600;
|
|
260
195
|
const MAX_SUMMARY_GENERATION_DEADLINE_MS = 600_000;
|
|
261
|
-
const SEARCH_QUERY_CONCURRENCY = 3;
|
|
262
|
-
const FETCH_MODES = ["readable", "raw", "answer"] as const;
|
|
263
|
-
type FetchMode = typeof FETCH_MODES[number];
|
|
264
196
|
const FETCH_MODE_DESCRIPTIONS: Record<FetchMode, string> = {
|
|
265
197
|
readable: "extract readable content as markdown",
|
|
266
198
|
raw: "return the exact textual body using direct HTTP only",
|
|
267
199
|
answer: "answer a prompt using only fetched content",
|
|
268
200
|
};
|
|
269
201
|
|
|
270
|
-
function resolveFetchModeConfig(config: WebSearchConfig): { defaultMode: FetchMode; allowedModes: FetchMode[] } {
|
|
271
|
-
const configuredModes = config.fetch?.allowedModes ?? FETCH_MODES;
|
|
272
|
-
if (!Array.isArray(configuredModes) || configuredModes.length === 0 || configuredModes.some(mode => !FETCH_MODES.includes(mode as FetchMode))) {
|
|
273
|
-
throw new Error(`fetch.allowedModes in ${WEB_SEARCH_CONFIG_PATH} must be a non-empty array containing only "readable", "raw", or "answer"`);
|
|
274
|
-
}
|
|
275
|
-
const allowedModes = configuredModes as FetchMode[];
|
|
276
|
-
const duplicateMode = allowedModes.find((mode, index) => allowedModes.indexOf(mode) !== index);
|
|
277
|
-
if (duplicateMode) {
|
|
278
|
-
throw new Error(`fetch.allowedModes in ${WEB_SEARCH_CONFIG_PATH} must not contain duplicates: "${duplicateMode}"`);
|
|
279
|
-
}
|
|
280
|
-
const defaultMode = config.fetch?.defaultMode ?? "readable";
|
|
281
|
-
if (!allowedModes.includes(defaultMode as FetchMode)) {
|
|
282
|
-
throw new Error(`fetch.defaultMode in ${WEB_SEARCH_CONFIG_PATH} must be one of fetch.allowedModes`);
|
|
283
|
-
}
|
|
284
|
-
return { defaultMode: defaultMode as FetchMode, allowedModes };
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Limit each batch independently so separate Pi tool calls can still run in parallel.
|
|
288
|
-
function runSearchQueries<T>(queries: string[], run: (query: string, index: number) => Promise<T>): Promise<T[]> {
|
|
289
|
-
const limit = pLimit(SEARCH_QUERY_CONCURRENCY);
|
|
290
|
-
return Promise.all(queries.map((query, index) => limit(() => run(query, index))));
|
|
291
|
-
}
|
|
292
|
-
|
|
293
202
|
// Keep primary query indexes stable for curator slots, then interleave additional
|
|
294
203
|
// provider entries deterministically instead of assigning by completion order.
|
|
295
204
|
function curatorResultIndex(queryIndex: number, entryIndex: number, queryCount: number): number {
|
|
@@ -307,23 +216,10 @@ function searchProviderSchema(description: string, allowedProviders: readonly Re
|
|
|
307
216
|
], { description });
|
|
308
217
|
}
|
|
309
218
|
|
|
310
|
-
function isToolEnabled(config: WebSearchConfig, key: keyof ToolNames): boolean {
|
|
311
|
-
const override = config.tools?.[key]?.enabled;
|
|
312
|
-
if (typeof override === "boolean") return override;
|
|
313
|
-
return key !== "webSearch" && key !== "sourceCheck" || config.webSearch?.enabled !== false;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
219
|
function isCommandEnabled(config: WebSearchConfig, name: "websearch" | "curator" | "search" | "google-account"): boolean {
|
|
317
220
|
return config.commands?.[name]?.enabled !== false;
|
|
318
221
|
}
|
|
319
222
|
|
|
320
|
-
function joinToolNames(names: string[]): string {
|
|
321
|
-
if (names.length === 0) return "stored content";
|
|
322
|
-
if (names.length === 1) return names[0];
|
|
323
|
-
if (names.length === 2) return `${names[0]} or ${names[1]}`;
|
|
324
|
-
return `${names.slice(0, -1).join(", ")}, or ${names[names.length - 1]}`;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
223
|
function resolveToolNames(config: WebSearchConfig): ToolNames {
|
|
328
224
|
if (config.toolNames !== undefined && (!config.toolNames || typeof config.toolNames !== "object" || Array.isArray(config.toolNames))) {
|
|
329
225
|
throw new Error(`toolNames in ${WEB_SEARCH_CONFIG_PATH} must be an object`);
|
|
@@ -362,40 +258,12 @@ function loadConfigForExtensionInit(): WebSearchConfig {
|
|
|
362
258
|
}
|
|
363
259
|
}
|
|
364
260
|
|
|
365
|
-
function normalizeProviderInput(value: unknown, label = "provider"): SearchProviderSelection | undefined {
|
|
366
|
-
if (value === undefined) return undefined;
|
|
367
|
-
return normalizeSearchProviderSelection(value, label);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
function resolveRequestedProvider(requested: unknown): SearchProviderSelection {
|
|
371
|
-
const normalizedRequested = normalizeProviderInput(requested);
|
|
372
|
-
if (normalizedRequested && normalizedRequested !== "auto") {
|
|
373
|
-
assertSearchProviderSelectionAllowed(normalizedRequested, "Requested provider");
|
|
374
|
-
return normalizedRequested;
|
|
375
|
-
}
|
|
376
|
-
const config = loadConfig();
|
|
377
|
-
const provider = normalizeProviderInput(config.searchProvider ?? config.provider, `provider in ${WEB_SEARCH_CONFIG_PATH}`) ?? "auto";
|
|
378
|
-
assertSearchProviderSelectionAllowed(provider, `configured provider in ${WEB_SEARCH_CONFIG_PATH}`);
|
|
379
|
-
return provider;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function toCuratorProvider(provider: SearchProviderSelection): SearchProvider | undefined {
|
|
383
|
-
if (Array.isArray(provider)) return "all";
|
|
384
|
-
return provider === "auto" ? undefined : provider;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
261
|
function resolveCuratorSearchProvider(requested: unknown, current: SearchProviderSelection): SearchProviderSelection {
|
|
388
262
|
const normalized = normalizeProviderInput(requested);
|
|
389
263
|
if (!normalized || normalized === "auto") return current;
|
|
390
264
|
return normalized === "all" && Array.isArray(current) ? current : normalized;
|
|
391
265
|
}
|
|
392
266
|
|
|
393
|
-
function normalizeRecencyFilter(value: unknown): RecencyFilter | undefined {
|
|
394
|
-
return value === "day" || value === "week" || value === "month" || value === "year"
|
|
395
|
-
? value
|
|
396
|
-
: undefined;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
267
|
function normalizeCuratorTimeoutSeconds(value: unknown): number | undefined {
|
|
400
268
|
if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
|
|
401
269
|
const normalized = Math.floor(value);
|
|
@@ -403,43 +271,6 @@ function normalizeCuratorTimeoutSeconds(value: unknown): number | undefined {
|
|
|
403
271
|
return Math.min(normalized, MAX_CURATOR_TIMEOUT_SECONDS);
|
|
404
272
|
}
|
|
405
273
|
|
|
406
|
-
function normalizeQueryList(queryList: unknown[]): string[] {
|
|
407
|
-
const normalized: string[] = [];
|
|
408
|
-
for (const query of queryList) {
|
|
409
|
-
if (typeof query !== "string") continue;
|
|
410
|
-
const trimmed = query.trim();
|
|
411
|
-
if (trimmed.length > 0) normalized.push(trimmed);
|
|
412
|
-
}
|
|
413
|
-
return normalized;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Some local models serialize a multi-query list into the single-string `query`
|
|
417
|
-
// field as a JSON array (query: "[\"a\", \"b\"]") instead of using the
|
|
418
|
-
// `queries` parameter. Forwarding that raw string verbatim makes every backend
|
|
419
|
-
// search for the literal array text and return zero results, with no signal to
|
|
420
|
-
// the model that its argument shape was wrong. Expand a string that parses as a
|
|
421
|
-
// JSON array of strings so each element is searched independently.
|
|
422
|
-
function expandQueryString(query: unknown): string[] {
|
|
423
|
-
if (typeof query !== "string") return [];
|
|
424
|
-
const trimmed = query.trim();
|
|
425
|
-
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
426
|
-
try {
|
|
427
|
-
const parsed: unknown = JSON.parse(trimmed);
|
|
428
|
-
// Only expand an unambiguously string-only array. Mixed or non-string
|
|
429
|
-
// arrays are kept as the literal query so we never silently drop
|
|
430
|
-
// members or collapse them into an empty search.
|
|
431
|
-
if (Array.isArray(parsed) && parsed.every((entry): entry is string => typeof entry === "string")) {
|
|
432
|
-
return parsed
|
|
433
|
-
.map((entry) => entry.trim())
|
|
434
|
-
.filter((entry) => entry.length > 0);
|
|
435
|
-
}
|
|
436
|
-
} catch {
|
|
437
|
-
// Not JSON — treat as a literal query string.
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
return [query];
|
|
441
|
-
}
|
|
442
|
-
|
|
443
274
|
function getCuratorTimeoutSeconds(): number {
|
|
444
275
|
const source = loadConfig();
|
|
445
276
|
const explicit = normalizeCuratorTimeoutSeconds(source.curatorTimeoutSeconds);
|
|
@@ -456,6 +287,13 @@ export function getSummaryGenerationDeadlineMs(): number {
|
|
|
456
287
|
return Math.min(value, MAX_SUMMARY_GENERATION_DEADLINE_MS);
|
|
457
288
|
}
|
|
458
289
|
|
|
290
|
+
export function getSummaryInstructions(): string | undefined {
|
|
291
|
+
const value = loadConfig().summaryInstructions;
|
|
292
|
+
if (typeof value !== "string") return undefined;
|
|
293
|
+
const trimmed = value.trim();
|
|
294
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
295
|
+
}
|
|
296
|
+
|
|
459
297
|
function shouldAutoOpenCuratorBrowser(config: WebSearchConfig): boolean {
|
|
460
298
|
if (config.autoOpenBrowser === false) return false;
|
|
461
299
|
if (resolveCuratorNetworkConfig().enabled && config.autoOpenBrowser !== true) return false;
|
|
@@ -484,6 +322,7 @@ async function getProviderAvailability(ctx: ExtensionContext): Promise<ProviderA
|
|
|
484
322
|
bocha: allowedProviders.has("bocha") && isBochaAvailable(),
|
|
485
323
|
ollama: allowedProviders.has("ollama") && isOllamaAvailable(),
|
|
486
324
|
searxng: allowedProviders.has("searxng") && isSearXNGAvailable(),
|
|
325
|
+
degoog: allowedProviders.has("degoog") && isDegoogAvailable(),
|
|
487
326
|
duckduckgo: allowedProviders.has("duckduckgo") && isDuckDuckGoAvailable(),
|
|
488
327
|
perplexity: allowedProviders.has("perplexity") && isPerplexityAvailable(),
|
|
489
328
|
exa: allowedProviders.has("exa") && isExaAvailable(),
|
|
@@ -501,6 +340,7 @@ async function getProviderAvailability(ctx: ExtensionContext): Promise<ProviderA
|
|
|
501
340
|
baizhi: allowedProviders.has("baizhi") && isBaizhiAvailable(),
|
|
502
341
|
zai: allowedProviders.has("zai") && isZaiAvailable(),
|
|
503
342
|
valyu: allowedProviders.has("valyu") && isValyuAvailable(),
|
|
343
|
+
keenable: allowedProviders.has("keenable") && isKeenableAvailable(),
|
|
504
344
|
};
|
|
505
345
|
return {
|
|
506
346
|
all: ALL_SEARCH_PROVIDERS.some(provider => provider === "gemini" ? geminiApiAvail : providers[provider]),
|
|
@@ -516,10 +356,6 @@ async function getOptionalGeminiWebAvailability() {
|
|
|
516
356
|
}
|
|
517
357
|
}
|
|
518
358
|
|
|
519
|
-
function shouldUseOpenAICodexDefault(ctx?: Pick<ExtensionContext, "model">): boolean {
|
|
520
|
-
return ctx?.model?.provider === "openai-codex";
|
|
521
|
-
}
|
|
522
|
-
|
|
523
359
|
function shouldPreferOpenAI(options: Pick<PendingCurate, "numResults" | "recencyFilter"> | undefined, preferOpenAICodexDefault: boolean): boolean {
|
|
524
360
|
if (options?.recencyFilter) return false;
|
|
525
361
|
if (typeof options?.numResults === "number" && Number.isFinite(options.numResults) && Math.floor(options.numResults) !== 5) {
|
|
@@ -546,10 +382,10 @@ async function loadCuratorBootstrap(
|
|
|
546
382
|
export function resolveCuratorDefaultProvider(
|
|
547
383
|
provider: SearchProviderSelection,
|
|
548
384
|
available: ProviderAvailability,
|
|
549
|
-
ctx?:
|
|
385
|
+
ctx?: Parameters<typeof isOpenAISubscriptionModelSelected>[0],
|
|
550
386
|
options?: Pick<PendingCurate, "numResults" | "recencyFilter">,
|
|
551
387
|
): SearchProvider {
|
|
552
|
-
return resolveProvider(provider, available, options,
|
|
388
|
+
return resolveProvider(provider, available, options, isOpenAISubscriptionModelSelected(ctx), ctx);
|
|
553
389
|
}
|
|
554
390
|
|
|
555
391
|
function firstAvailableProvider(available: ProviderAvailability, preferOpenAI: boolean, fallback: ResolvedSearchProvider): ResolvedSearchProvider | "auto" {
|
|
@@ -614,6 +450,7 @@ interface PendingCurate {
|
|
|
614
450
|
numResults?: number;
|
|
615
451
|
recencyFilter?: "day" | "week" | "month" | "year";
|
|
616
452
|
domainFilter?: string[];
|
|
453
|
+
category?: string;
|
|
617
454
|
availableProviders: ProviderAvailability;
|
|
618
455
|
defaultProvider: SearchProvider;
|
|
619
456
|
searchProvider: SearchProviderSelection;
|
|
@@ -631,194 +468,6 @@ interface PendingCurate {
|
|
|
631
468
|
browserOpenError?: string;
|
|
632
469
|
}
|
|
633
470
|
|
|
634
|
-
|
|
635
|
-
const DEFAULT_MAX_INLINE_CONTENT_CHARS = 30_000;
|
|
636
|
-
const MIN_INLINE_CONTENT_CHARS = 1_000;
|
|
637
|
-
const MAX_INLINE_CONTENT_CHARS = 200_000;
|
|
638
|
-
|
|
639
|
-
function getMaxInlineContentChars(config = loadConfig()): number {
|
|
640
|
-
const value = config.maxInlineContentChars;
|
|
641
|
-
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value < MIN_INLINE_CONTENT_CHARS) {
|
|
642
|
-
return DEFAULT_MAX_INLINE_CONTENT_CHARS;
|
|
643
|
-
}
|
|
644
|
-
return Math.min(value, MAX_INLINE_CONTENT_CHARS);
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
function stripThumbnails(results: ExtractedContent[]): ExtractedContent[] {
|
|
648
|
-
return results.map(({ thumbnail, frames, ...rest }) => rest);
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
function storeFetchResult(pi: { appendEntry(type: string, data: unknown): void }, responseId: string, data: StoredSearchData & { type: "fetch"; urls: ExtractedContent[] }, authProfile?: AuthFetchProfile): boolean {
|
|
652
|
-
if (authProfile?.cache === "off") return false;
|
|
653
|
-
pi.appendEntry("web-search-results", storeFetchedContentResult(responseId, data));
|
|
654
|
-
return true;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
function initialContentSlice(content: string, maxChars: number): {
|
|
658
|
-
text: string;
|
|
659
|
-
endOffset: number;
|
|
660
|
-
totalBytes: number;
|
|
661
|
-
totalLines: number;
|
|
662
|
-
shownBytes: number;
|
|
663
|
-
shownLines: number;
|
|
664
|
-
} {
|
|
665
|
-
let endOffset = Math.min(content.length, maxChars);
|
|
666
|
-
if (endOffset < content.length) {
|
|
667
|
-
const lineBreak = content.lastIndexOf("\n", endOffset);
|
|
668
|
-
if (lineBreak >= Math.floor(maxChars * 0.8)) endOffset = lineBreak + 1;
|
|
669
|
-
}
|
|
670
|
-
const text = content.slice(0, endOffset);
|
|
671
|
-
return {
|
|
672
|
-
text,
|
|
673
|
-
endOffset,
|
|
674
|
-
totalBytes: Buffer.byteLength(content),
|
|
675
|
-
totalLines: content.length === 0 ? 0 : content.split("\n").length,
|
|
676
|
-
shownBytes: Buffer.byteLength(text),
|
|
677
|
-
shownLines: text.length === 0 ? 0 : text.split("\n").length,
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
function normalizeFindQueries(value: string | string[]): string[] {
|
|
682
|
-
const queries = (Array.isArray(value) ? value : [value]).map(query => query.trim()).filter(Boolean);
|
|
683
|
-
if (queries.length === 0) throw new Error("findText must contain at least one non-empty string");
|
|
684
|
-
return queries;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
interface GetSearchContentParams {
|
|
688
|
-
responseId: string;
|
|
689
|
-
query?: string;
|
|
690
|
-
queryIndex?: number;
|
|
691
|
-
url?: string;
|
|
692
|
-
urlIndex?: number;
|
|
693
|
-
offset?: number;
|
|
694
|
-
limit?: number;
|
|
695
|
-
findText?: string | string[];
|
|
696
|
-
findMode?: FindMode;
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
type RawGetSearchContentParams = Omit<GetSearchContentParams, "findMode"> & { findMode?: unknown };
|
|
700
|
-
|
|
701
|
-
function normalizeFindMode(value: unknown): FindMode | undefined {
|
|
702
|
-
if (value === undefined) return undefined;
|
|
703
|
-
if (value === "exact" || value === "case-insensitive" || value === "fuzzy") return value;
|
|
704
|
-
throw new Error('findMode must be "exact", "case-insensitive", or "fuzzy"');
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
function normalizeGetSearchContentParams(params: RawGetSearchContentParams): GetSearchContentParams {
|
|
708
|
-
const normalized: GetSearchContentParams = { ...params, findMode: normalizeFindMode(params.findMode) };
|
|
709
|
-
|
|
710
|
-
if (normalized.query?.trim() === "") delete normalized.query;
|
|
711
|
-
if (normalized.url?.trim() === "") delete normalized.url;
|
|
712
|
-
|
|
713
|
-
if (normalized.findText !== undefined) {
|
|
714
|
-
delete normalized.offset;
|
|
715
|
-
delete normalized.limit;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
return normalized;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
function formatInputValue(value: unknown): string {
|
|
722
|
-
if (typeof value === "string") return JSON.stringify(value);
|
|
723
|
-
if (typeof value === "number") return Number.isNaN(value) ? "NaN" : String(value);
|
|
724
|
-
try {
|
|
725
|
-
const serialized = JSON.stringify(value);
|
|
726
|
-
return serialized === undefined ? String(value) : serialized;
|
|
727
|
-
} catch {
|
|
728
|
-
return String(value);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
function formatSearchSummary(results: SearchResult[], answer: string): string {
|
|
733
|
-
if (results.length === 0) {
|
|
734
|
-
return answer ? `${answer}\n\n---\n\n**Sources:**\nNo sources returned.` : "No results found.";
|
|
735
|
-
}
|
|
736
|
-
let output = answer ? `${answer}\n\n---\n\n**Sources:**\n` : "";
|
|
737
|
-
output += results.map((r, i) => `${i + 1}. ${r.title}\n ${r.url}`).join("\n\n");
|
|
738
|
-
return output;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
function formatSourceCheckResult(artifact: ResearchArtifact, getSearchContentTool: string | null = DEFAULT_TOOL_NAMES.getSearchContent): string {
|
|
742
|
-
const assessment = artifact.claims?.[0];
|
|
743
|
-
const lines = [`# Source check: ${artifact.query}`, ""];
|
|
744
|
-
if (assessment) {
|
|
745
|
-
lines.push(`**Status:** ${assessment.status} (confidence ${assessment.confidence.toFixed(2)})`);
|
|
746
|
-
lines.push(`**Rationale:** ${assessment.rationale}`);
|
|
747
|
-
if (assessment.supporting_passages.length > 0) lines.push(`**Supporting passages:** ${assessment.supporting_passages.join(", ")}`);
|
|
748
|
-
if (assessment.contradicting_passages.length > 0) lines.push(`**Contradicting passages:** ${assessment.contradicting_passages.join(", ")}`);
|
|
749
|
-
lines.push("");
|
|
750
|
-
}
|
|
751
|
-
if (artifact.sources.length > 0) {
|
|
752
|
-
lines.push("## Sources");
|
|
753
|
-
for (const source of artifact.sources) lines.push(`${source.rank}. [${source.quality}] ${source.title}\n ${source.url}`);
|
|
754
|
-
lines.push("");
|
|
755
|
-
}
|
|
756
|
-
if (artifact.errors?.length) lines.push(`Search errors: ${artifact.errors.map((entry) => `${entry.query}: ${entry.error}`).join("; ")}`);
|
|
757
|
-
lines.push(getSearchContentTool
|
|
758
|
-
? `Artifact responseId: ${artifact.id} (retrievable via ${getSearchContentTool}).`
|
|
759
|
-
: `Artifact responseId: ${artifact.id}. Content retrieval is not registered.`);
|
|
760
|
-
return lines.join("\n");
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
function duplicateQuerySet(results: QueryResultData[]): Set<string> {
|
|
764
|
-
const counts = new Map<string, number>();
|
|
765
|
-
for (const result of results) {
|
|
766
|
-
counts.set(result.query, (counts.get(result.query) ?? 0) + 1);
|
|
767
|
-
}
|
|
768
|
-
const duplicates = new Set<string>();
|
|
769
|
-
for (const [query, count] of counts) {
|
|
770
|
-
if (count > 1) duplicates.add(query);
|
|
771
|
-
}
|
|
772
|
-
return duplicates;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
function formatQueryHeader(query: string, provider: string | undefined, duplicateQueries: Set<string>): string {
|
|
776
|
-
const suffix = duplicateQueries.has(query) && provider ? ` (${provider})` : "";
|
|
777
|
-
return `## Query: "${query}"${suffix}\n\n`;
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
function hasFullInlineCoverage(urls: string[], inlineContent: ExtractedContent[] | undefined): boolean {
|
|
781
|
-
if (!inlineContent || inlineContent.length === 0) return false;
|
|
782
|
-
const coveredUrls = new Set(inlineContent.map(c => c.url));
|
|
783
|
-
return urls.every(url => coveredUrls.has(url));
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
function formatFullResults(queryData: QueryResultData): string {
|
|
787
|
-
let output = `## Results for: "${queryData.query}"\n\n`;
|
|
788
|
-
const providers = queryData.providers ?? (queryData.provider ? [queryData.provider] : []);
|
|
789
|
-
if (providers.length > 0) output += `**Provider${providers.length === 1 ? "" : "s"}:** ${providers.join(", ")}\n\n`;
|
|
790
|
-
if (queryData.answer) {
|
|
791
|
-
output += `${queryData.answer}\n\n---\n\n`;
|
|
792
|
-
}
|
|
793
|
-
for (const r of queryData.results) {
|
|
794
|
-
output += `### ${r.title}\n${r.url}${r.snippet ? `\n\n${r.snippet}` : ""}\n\n`;
|
|
795
|
-
}
|
|
796
|
-
return output;
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
function boundSearchPresentation(
|
|
800
|
-
text: string,
|
|
801
|
-
guidance: string,
|
|
802
|
-
truncationGuidance: string,
|
|
803
|
-
maxChars: number,
|
|
804
|
-
): { text: string; truncated: boolean; originalChars: number; returnedChars: number; omittedChars: number } {
|
|
805
|
-
const fullText = `${text}${guidance}`;
|
|
806
|
-
if (fullText.length <= maxChars) {
|
|
807
|
-
return { text: fullText, truncated: false, originalChars: text.length, returnedChars: text.length, omittedChars: 0 };
|
|
808
|
-
}
|
|
809
|
-
const marker = `\n\n---\n[Output truncated.]${truncationGuidance}`;
|
|
810
|
-
// Reserve marker space so the complete model-visible response never exceeds the configured ceiling.
|
|
811
|
-
const prefixLength = maxChars - marker.length;
|
|
812
|
-
const bounded = `${text.slice(0, prefixLength)}${marker}`;
|
|
813
|
-
return {
|
|
814
|
-
text: bounded,
|
|
815
|
-
truncated: true,
|
|
816
|
-
originalChars: text.length,
|
|
817
|
-
returnedChars: prefixLength,
|
|
818
|
-
omittedChars: text.length - prefixLength,
|
|
819
|
-
};
|
|
820
|
-
}
|
|
821
|
-
|
|
822
471
|
function abortPendingFetches(): void {
|
|
823
472
|
for (const controller of pendingFetches.values()) {
|
|
824
473
|
controller.abort();
|
|
@@ -1094,17 +743,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
1094
743
|
const sourceCheckEnabled = isToolEnabled(initConfig, "sourceCheck");
|
|
1095
744
|
const fetchContentEnabled = isToolEnabled(initConfig, "fetchContent");
|
|
1096
745
|
const getSearchContentEnabled = isToolEnabled(initConfig, "getSearchContent");
|
|
746
|
+
const coreSettings = {
|
|
747
|
+
toolNames,
|
|
748
|
+
enabledTools: {
|
|
749
|
+
webSearch: webSearchEnabled,
|
|
750
|
+
sourceCheck: sourceCheckEnabled,
|
|
751
|
+
fetchContent: fetchContentEnabled,
|
|
752
|
+
getSearchContent: getSearchContentEnabled,
|
|
753
|
+
},
|
|
754
|
+
maxInlineContentChars: getMaxInlineContentChars(initConfig),
|
|
755
|
+
fetchModes: fetchModeConfig,
|
|
756
|
+
};
|
|
1097
757
|
// Names as registered this session, so fetch failure guidance never points
|
|
1098
758
|
// at tools that are disabled or were renamed after init.
|
|
1099
|
-
const registeredToolNames =
|
|
1100
|
-
|
|
1101
|
-
...(fetchContentEnabled ? { fetchContent: toolNames.fetchContent } : {}),
|
|
1102
|
-
};
|
|
1103
|
-
const storedContentSources = joinToolNames([
|
|
1104
|
-
...(webSearchEnabled ? [toolNames.webSearch] : []),
|
|
1105
|
-
...(sourceCheckEnabled ? [toolNames.sourceCheck] : []),
|
|
1106
|
-
...(fetchContentEnabled ? [toolNames.fetchContent] : []),
|
|
1107
|
-
]);
|
|
759
|
+
const registeredToolNames = registeredFetchToolNames(coreSettings);
|
|
760
|
+
const storedContentSources = storedContentSourceNames(coreSettings);
|
|
1108
761
|
const searchQueryDescription = webSearchEnabled
|
|
1109
762
|
? `Get content for this query (${toolNames.webSearch})`
|
|
1110
763
|
: "Get content for a stored search query";
|
|
@@ -1117,13 +770,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
1117
770
|
const curateKey = initConfig.shortcuts?.curate || DEFAULT_SHORTCUTS.curate;
|
|
1118
771
|
const activityKey = initConfig.shortcuts?.activity || DEFAULT_SHORTCUTS.activity;
|
|
1119
772
|
|
|
1120
|
-
function startBackgroundFetch(urls: string[], proxy?: string): string | null {
|
|
773
|
+
function startBackgroundFetch(urls: string[], proxy?: string, provided?: ExtractedContent[]): string | null {
|
|
1121
774
|
if (urls.length === 0) return null;
|
|
1122
775
|
const fetchId = generateId();
|
|
1123
776
|
const controller = new AbortController();
|
|
1124
777
|
pendingFetches.set(fetchId, controller);
|
|
1125
778
|
Promise.resolve()
|
|
1126
|
-
.then(() => runWithProxy(proxy, () => fetchAllContent(
|
|
779
|
+
.then(() => fetchUncoveredContent(urls, provided, missing => runWithProxy(proxy, () => fetchAllContent(missing, controller.signal, withRegisteredFetchOptions(undefined, registeredToolNames, proxy)))))
|
|
1127
780
|
.then((fetched) => {
|
|
1128
781
|
if (!sessionActive || !pendingFetches.has(fetchId)) return;
|
|
1129
782
|
const data = {
|
|
@@ -1131,7 +784,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1131
784
|
type: "fetch",
|
|
1132
785
|
timestamp: Date.now(),
|
|
1133
786
|
urls: stripThumbnails(fetched),
|
|
1134
|
-
} satisfies
|
|
787
|
+
} satisfies FetchedContentData;
|
|
1135
788
|
pi.appendEntry("web-search-results", storeFetchedContentResult(fetchId, data));
|
|
1136
789
|
const ok = fetched.filter(f => !f.error).length;
|
|
1137
790
|
const availability = ok === fetched.length
|
|
@@ -1167,29 +820,18 @@ export default function (pi: ExtensionAPI) {
|
|
|
1167
820
|
return fetchId;
|
|
1168
821
|
}
|
|
1169
822
|
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
results: QueryResultData[];
|
|
1183
|
-
urls: string[];
|
|
1184
|
-
includeContent: boolean;
|
|
1185
|
-
inlineContent?: ExtractedContent[];
|
|
1186
|
-
curated?: boolean;
|
|
1187
|
-
curatedFrom?: number;
|
|
1188
|
-
workflow?: SummaryWorkflow;
|
|
1189
|
-
approvedSummary?: string;
|
|
1190
|
-
summaryMeta?: SummaryMeta;
|
|
1191
|
-
proxy?: string;
|
|
1192
|
-
}
|
|
823
|
+
const core = createWebToolCore({
|
|
824
|
+
settings: coreSettings,
|
|
825
|
+
storeFetchedContent(id, data) {
|
|
826
|
+
pi.appendEntry("web-search-results", storeFetchedContentResult(id, data));
|
|
827
|
+
},
|
|
828
|
+
publishResult(data) {
|
|
829
|
+
pi.appendEntry("web-search-results", data);
|
|
830
|
+
},
|
|
831
|
+
startBackgroundFetch,
|
|
832
|
+
answerFromPage: (request, ctx, signal) => answerFromPage(request, ctx!, signal),
|
|
833
|
+
});
|
|
834
|
+
const buildSearchReturn = core.buildSearchReturn;
|
|
1193
835
|
|
|
1194
836
|
function normalizeSummaryMeta(meta: SummaryMeta | undefined, summaryText: string): SummaryMeta {
|
|
1195
837
|
const normalizedText = summaryText.trim();
|
|
@@ -1279,6 +921,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1279
921
|
feedback,
|
|
1280
922
|
undefined,
|
|
1281
923
|
getSummaryGenerationDeadlineMs(),
|
|
924
|
+
getSummaryInstructions(),
|
|
1282
925
|
);
|
|
1283
926
|
} catch (err) {
|
|
1284
927
|
const isEmptyResponse = err instanceof Error && err.message.includes("Summary model returned empty response");
|
|
@@ -1391,127 +1034,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
1391
1034
|
};
|
|
1392
1035
|
}
|
|
1393
1036
|
|
|
1394
|
-
function buildSearchReturn(opts: SearchReturnOptions): AgentToolResult<Record<string, unknown>> {
|
|
1395
|
-
const sc = opts.results.filter(r => !r.error).length;
|
|
1396
|
-
const tr = opts.results.reduce((sum, r) => sum + r.results.length, 0);
|
|
1397
|
-
|
|
1398
|
-
const hasApprovedSummary = typeof opts.approvedSummary === "string" && opts.approvedSummary.trim().length > 0;
|
|
1399
|
-
const maxInlineContentChars = getMaxInlineContentChars(initConfig);
|
|
1400
|
-
let output = "";
|
|
1401
|
-
if (hasApprovedSummary) {
|
|
1402
|
-
output = opts.approvedSummary!.trim();
|
|
1403
|
-
} else {
|
|
1404
|
-
if (opts.curated) {
|
|
1405
|
-
output += "[These results were manually curated by the user in the browser. Use them as-is — do not re-search or discard.]\n\n";
|
|
1406
|
-
}
|
|
1407
|
-
const providerNames = opts.results.map(result => {
|
|
1408
|
-
const providers = result.providers ?? (result.provider ? [result.provider] : []);
|
|
1409
|
-
return providers.join(", ") || "unknown";
|
|
1410
|
-
});
|
|
1411
|
-
output += opts.results.length === 1
|
|
1412
|
-
? `**Provider:** ${providerNames[0]}\n\n`
|
|
1413
|
-
: `**Providers used:** ${providerNames.map((name, index) => `Query ${index + 1}: ${name}`).join("; ")}\n\n`;
|
|
1414
|
-
const duplicateQueries = opts.curated ? duplicateQuerySet(opts.results) : new Set<string>();
|
|
1415
|
-
for (const { query, answer, results, error, provider } of opts.results) {
|
|
1416
|
-
if (opts.queryList.length > 1) {
|
|
1417
|
-
output += opts.curated
|
|
1418
|
-
? formatQueryHeader(query, provider, duplicateQueries)
|
|
1419
|
-
: `## Query: "${query}"\n\n`;
|
|
1420
|
-
}
|
|
1421
|
-
if (error) output += `Error: ${error}\n\n`;
|
|
1422
|
-
else output += formatSearchSummary(results, answer) + "\n\n";
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
const hasInlineReady = hasFullInlineCoverage(opts.urls, opts.inlineContent);
|
|
1427
|
-
let fetchId: string | null = null;
|
|
1428
|
-
if (hasInlineReady && opts.inlineContent) {
|
|
1429
|
-
fetchId = generateId();
|
|
1430
|
-
const data = {
|
|
1431
|
-
id: fetchId,
|
|
1432
|
-
type: "fetch",
|
|
1433
|
-
timestamp: Date.now(),
|
|
1434
|
-
urls: opts.inlineContent,
|
|
1435
|
-
} satisfies StoredSearchData & { type: "fetch"; urls: ExtractedContent[] };
|
|
1436
|
-
pi.appendEntry("web-search-results", storeFetchedContentResult(fetchId, data));
|
|
1437
|
-
} else if (opts.includeContent) {
|
|
1438
|
-
fetchId = startBackgroundFetch(opts.urls, opts.proxy);
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
|
-
const searchId = storeAndPublishSearch(opts.results);
|
|
1442
|
-
const isBackgroundFetch = fetchId !== null && !hasInlineReady;
|
|
1443
|
-
const unboundedPresentation = output.trim();
|
|
1444
|
-
const buildGuidance = (forTruncation: boolean): string => {
|
|
1445
|
-
let value = "";
|
|
1446
|
-
if (hasInlineReady && opts.inlineContent && fetchId) {
|
|
1447
|
-
value += `\n---\nFull content for ${opts.inlineContent.length} sources is ready as responseId "${fetchId}". `;
|
|
1448
|
-
value += getSearchContentEnabled
|
|
1449
|
-
? `Use ${toolNames.getSearchContent}({ responseId: "${fetchId}", urlIndex: 0, offset: 0, limit: ${maxInlineContentChars} }) to retrieve the first bounded page.`
|
|
1450
|
-
: forTruncation ? `Enable ${toolNames.getSearchContent} to retrieve the full stored content.` : "";
|
|
1451
|
-
} else if (isBackgroundFetch && fetchId) {
|
|
1452
|
-
value += `\n---\nContent fetching in background as responseId "${fetchId}". Will notify when ready.`;
|
|
1453
|
-
}
|
|
1454
|
-
if (getSearchContentEnabled || forTruncation) {
|
|
1455
|
-
value += `\n---\nFull search results are stored as responseId "${searchId}". `;
|
|
1456
|
-
value += getSearchContentEnabled
|
|
1457
|
-
? `Use ${toolNames.getSearchContent}({ responseId: "${searchId}", queryIndex: 0, offset: 0, limit: ${maxInlineContentChars} }) to retrieve the first bounded page${opts.results.length > 1 ? `; repeat with queryIndex 1 through ${opts.results.length - 1}` : ""}.`
|
|
1458
|
-
: `Enable ${toolNames.getSearchContent} to retrieve the full stored results.`;
|
|
1459
|
-
}
|
|
1460
|
-
return value;
|
|
1461
|
-
};
|
|
1462
|
-
const presentation = hasApprovedSummary
|
|
1463
|
-
? { text: unboundedPresentation, truncated: false, originalChars: unboundedPresentation.length, returnedChars: unboundedPresentation.length, omittedChars: 0 }
|
|
1464
|
-
: boundSearchPresentation(unboundedPresentation, buildGuidance(false), buildGuidance(true), maxInlineContentChars);
|
|
1465
|
-
|
|
1466
|
-
return {
|
|
1467
|
-
content: [{ type: "text", text: presentation.text }],
|
|
1468
|
-
details: {
|
|
1469
|
-
queries: opts.queryList,
|
|
1470
|
-
queryCount: opts.queryList.length,
|
|
1471
|
-
successfulQueries: sc,
|
|
1472
|
-
totalResults: tr,
|
|
1473
|
-
includeContent: opts.includeContent,
|
|
1474
|
-
fetchId,
|
|
1475
|
-
fetchUrls: isBackgroundFetch ? opts.urls : undefined,
|
|
1476
|
-
searchId,
|
|
1477
|
-
queryProviders: opts.results.map(result => ({
|
|
1478
|
-
query: result.query,
|
|
1479
|
-
providers: result.providers ?? (result.provider ? [result.provider] : []),
|
|
1480
|
-
})),
|
|
1481
|
-
truncated: presentation.truncated,
|
|
1482
|
-
originalChars: presentation.originalChars,
|
|
1483
|
-
returnedChars: presentation.returnedChars,
|
|
1484
|
-
omittedChars: presentation.omittedChars,
|
|
1485
|
-
...(opts.curated ? {
|
|
1486
|
-
curated: true,
|
|
1487
|
-
curatedFrom: opts.curatedFrom,
|
|
1488
|
-
curatedQueries: opts.results.map(r => ({
|
|
1489
|
-
query: r.query,
|
|
1490
|
-
provider: r.provider || null,
|
|
1491
|
-
answer: r.answer || null,
|
|
1492
|
-
sources: r.results.map(s => ({ title: s.title, url: s.url })),
|
|
1493
|
-
error: r.error,
|
|
1494
|
-
})),
|
|
1495
|
-
} : {}),
|
|
1496
|
-
...((opts.workflow && hasApprovedSummary)
|
|
1497
|
-
? {
|
|
1498
|
-
summary: {
|
|
1499
|
-
text: opts.approvedSummary!.trim(),
|
|
1500
|
-
workflow: opts.workflow,
|
|
1501
|
-
model: opts.summaryMeta?.model ?? null,
|
|
1502
|
-
durationMs: opts.summaryMeta?.durationMs ?? 0,
|
|
1503
|
-
tokenEstimate: opts.summaryMeta?.tokenEstimate ?? 0,
|
|
1504
|
-
fallbackUsed: opts.summaryMeta?.fallbackUsed === true,
|
|
1505
|
-
fallbackReason: opts.summaryMeta?.fallbackReason,
|
|
1506
|
-
phase: opts.summaryMeta?.phase,
|
|
1507
|
-
edited: opts.summaryMeta?.edited === true,
|
|
1508
|
-
},
|
|
1509
|
-
}
|
|
1510
|
-
: {}),
|
|
1511
|
-
},
|
|
1512
|
-
};
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
1037
|
function filterByQueryIndices(selectedQueryIndices: number[], results: Map<number, QueryResultData>) {
|
|
1516
1038
|
const filteredResults: QueryResultData[] = [];
|
|
1517
1039
|
const filteredUrls: string[] = [];
|
|
@@ -1689,6 +1211,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1689
1211
|
recencyFilter: pc.recencyFilter,
|
|
1690
1212
|
domainFilter: pc.domainFilter,
|
|
1691
1213
|
includeContent: pc.includeContent,
|
|
1214
|
+
category: pc.category,
|
|
1692
1215
|
signal: addSearchSignal,
|
|
1693
1216
|
extensionContext: ctx,
|
|
1694
1217
|
});
|
|
@@ -1813,11 +1336,20 @@ export default function (pi: ExtensionAPI) {
|
|
|
1813
1336
|
},
|
|
1814
1337
|
});
|
|
1815
1338
|
|
|
1816
|
-
|
|
1817
|
-
|
|
1339
|
+
// web_search calls a tool made, such as a codemode script, reach the tool_call
|
|
1340
|
+
// handlers before execute with parentToolCallId set; execute consumes the id.
|
|
1341
|
+
// Ids of calls a later handler blocked are dropped when the session changes.
|
|
1342
|
+
const nestedSearchCallIds = new Set<string>();
|
|
1343
|
+
if (webSearchEnabled) pi.on("tool_call", (event) => {
|
|
1344
|
+
if (event.parentToolCallId && event.toolName === toolNames.webSearch) nestedSearchCallIds.add(event.toolCallId);
|
|
1345
|
+
});
|
|
1346
|
+
|
|
1347
|
+
pi.on("session_start", async (_event, ctx) => { nestedSearchCallIds.clear(); handleSessionChange(ctx); });
|
|
1348
|
+
pi.on("session_tree", async (_event, ctx) => { nestedSearchCallIds.clear(); handleSessionChange(ctx); });
|
|
1818
1349
|
|
|
1819
1350
|
pi.on("session_shutdown", () => {
|
|
1820
1351
|
sessionActive = false;
|
|
1352
|
+
nestedSearchCallIds.clear();
|
|
1821
1353
|
abortPendingFetches();
|
|
1822
1354
|
closeCurator();
|
|
1823
1355
|
clearCloneCache();
|
|
@@ -1832,6 +1364,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1832
1364
|
if (webSearchEnabled) pi.registerTool({
|
|
1833
1365
|
name: toolNames.webSearch,
|
|
1834
1366
|
label: "Web Search",
|
|
1367
|
+
prepareArguments: (args) => parseStringifiedArrays(args, ["provider", "queries", "domainFilter"]) as never,
|
|
1835
1368
|
description:
|
|
1836
1369
|
`Search the web with ${allowedSearchProviders.map(providerLabel).join(", ")}. Provider arrays run simultaneously; ${allPolicyDescription}. The default workflow is none: it returns bounded source-linked search results or provider answers without a curator or generated summary, identifies the providers used, and stores full results for retrieval by responseId. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query. When includeContent is true, full page content is fetched in the background. Set workflow to "summary-review" to open the curator with an auto-generated summary draft or "auto-summary" to generate a summary without the browser curator. The configured provider is used when provider is omitted or set to auto; omit provider unless explicitly overriding it.`,
|
|
1837
1370
|
promptSnippet:
|
|
@@ -1845,6 +1378,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1845
1378
|
StringEnum(["day", "week", "month", "year"], { description: "Filter by recency" }),
|
|
1846
1379
|
),
|
|
1847
1380
|
domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains (prefix with - to exclude)" })),
|
|
1381
|
+
category: Type.Optional(Type.String({ description: "Exa only: result category, e.g. news, research paper" })),
|
|
1848
1382
|
provider: Type.Optional(searchProviderSchema(`Search provider or non-empty list of allowed providers to search simultaneously; ${allPolicyDescription}; omit this field to use the configured provider, or use auto when none is configured`, allowedSearchProviders)),
|
|
1849
1383
|
workflow: Type.Optional(
|
|
1850
1384
|
StringEnum(["none", "summary-review", "auto-summary"], {
|
|
@@ -1855,15 +1389,20 @@ export default function (pi: ExtensionAPI) {
|
|
|
1855
1389
|
description: "http(s) or socks proxy URL (e.g. http://host:port or socks5h://host:port) used for every outbound request in this call (search APIs and content fetches). Node fetch ignores HTTP(S)_PROXY env vars, so set this (or `proxy` in web-search.json) when direct access is blocked; empty string forces direct access.",
|
|
1856
1390
|
})),
|
|
1857
1391
|
}),
|
|
1392
|
+
outputSchema: webSearchOutputSchema,
|
|
1858
1393
|
|
|
1859
1394
|
async execute(callId, params, signal, onUpdate, ctx) {
|
|
1860
|
-
|
|
1395
|
+
// Nested calls neither open the curator unless asked nor fetch content in the background.
|
|
1396
|
+
const nested = nestedSearchCallIds.delete(callId);
|
|
1397
|
+
return markToolError(await runWithProxy(typeof params.proxy === "string" ? params.proxy : undefined, async () => {
|
|
1861
1398
|
const rawQueryList: unknown[] = Array.isArray(params.queries)
|
|
1862
1399
|
? params.queries
|
|
1863
1400
|
: (params.query !== undefined ? expandQueryString(params.query) : []);
|
|
1864
1401
|
const queryList = normalizeQueryList(rawQueryList);
|
|
1865
1402
|
const configWorkflow = loadConfigForExtensionInit().workflow;
|
|
1866
|
-
const workflow =
|
|
1403
|
+
const workflow = nested && params.workflow === undefined
|
|
1404
|
+
? "none"
|
|
1405
|
+
: curatorRunState.resolve(params.workflow, configWorkflow, ctx?.hasUI !== false);
|
|
1867
1406
|
const shouldCurate = workflow === "summary-review";
|
|
1868
1407
|
const recencyFilter = normalizeRecencyFilter(params.recencyFilter);
|
|
1869
1408
|
|
|
@@ -1929,6 +1468,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1929
1468
|
numResults: params.numResults,
|
|
1930
1469
|
recencyFilter,
|
|
1931
1470
|
domainFilter: params.domainFilter,
|
|
1471
|
+
category: params.category,
|
|
1932
1472
|
availableProviders,
|
|
1933
1473
|
defaultProvider,
|
|
1934
1474
|
searchProvider,
|
|
@@ -1990,6 +1530,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1990
1530
|
recencyFilter,
|
|
1991
1531
|
domainFilter: params.domainFilter,
|
|
1992
1532
|
includeContent: params.includeContent,
|
|
1533
|
+
category: params.category,
|
|
1993
1534
|
signal: searchSignal,
|
|
1994
1535
|
extensionContext: ctx,
|
|
1995
1536
|
});
|
|
@@ -2074,102 +1615,41 @@ export default function (pi: ExtensionAPI) {
|
|
|
2074
1615
|
return promise;
|
|
2075
1616
|
}
|
|
2076
1617
|
|
|
2077
|
-
let
|
|
2078
|
-
const allUrls: string[] = [];
|
|
2079
|
-
const allInlineContent: ExtractedContent[] = [];
|
|
2080
|
-
const resolvedProvider = resolveRequestedProvider(params.provider);
|
|
2081
|
-
|
|
2082
|
-
const queryResponses = await runSearchQueries(queryList, async (query) => {
|
|
2083
|
-
signal?.throwIfAborted();
|
|
2084
|
-
onUpdate?.({
|
|
2085
|
-
content: [{ type: "text", text: `Searching "${query}" (${completedSearches}/${queryList.length} complete)...` }],
|
|
2086
|
-
details: { phase: "search", progress: completedSearches / queryList.length, currentQuery: query },
|
|
2087
|
-
});
|
|
2088
|
-
|
|
2089
|
-
try {
|
|
2090
|
-
const { answer, results, inlineContent, provider, providerResponses } = await search(query, {
|
|
2091
|
-
provider: resolvedProvider,
|
|
2092
|
-
numResults: params.numResults,
|
|
2093
|
-
recencyFilter,
|
|
2094
|
-
domainFilter: params.domainFilter,
|
|
2095
|
-
includeContent: params.includeContent,
|
|
2096
|
-
signal,
|
|
2097
|
-
extensionContext: ctx,
|
|
2098
|
-
});
|
|
2099
|
-
|
|
2100
|
-
const providers = providerResponses?.map(response => response.provider) ?? [provider];
|
|
2101
|
-
return { result: { query, answer, results, error: null, provider, providers } satisfies QueryResultData, inlineContent };
|
|
2102
|
-
} catch (err) {
|
|
2103
|
-
if (signal?.aborted || isAbortError(err)) throw err;
|
|
2104
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2105
|
-
const requestedProvider = toCuratorProvider(resolvedProvider);
|
|
2106
|
-
return {
|
|
2107
|
-
result: { query, answer: "", results: [], error: message, provider: requestedProvider } satisfies QueryResultData,
|
|
2108
|
-
inlineContent: undefined,
|
|
2109
|
-
};
|
|
2110
|
-
} finally {
|
|
2111
|
-
completedSearches++;
|
|
2112
|
-
if (!signal?.aborted) {
|
|
2113
|
-
onUpdate?.({
|
|
2114
|
-
content: [{ type: "text", text: `Completed ${completedSearches}/${queryList.length} searches.` }],
|
|
2115
|
-
details: { phase: "search", progress: completedSearches / queryList.length, currentQuery: query },
|
|
2116
|
-
});
|
|
2117
|
-
}
|
|
2118
|
-
}
|
|
2119
|
-
});
|
|
2120
|
-
const searchResults = queryResponses.map(response => response.result);
|
|
2121
|
-
for (const response of queryResponses) {
|
|
2122
|
-
for (const result of response.result.results) {
|
|
2123
|
-
if (!allUrls.includes(result.url)) allUrls.push(result.url);
|
|
2124
|
-
}
|
|
2125
|
-
if (response.inlineContent) allInlineContent.push(...response.inlineContent);
|
|
2126
|
-
}
|
|
2127
|
-
|
|
2128
|
-
let approvedSummary: string | undefined;
|
|
2129
|
-
let summaryMeta: SummaryMeta | undefined;
|
|
1618
|
+
let summarize: WebSearchCallOptions["summarize"];
|
|
2130
1619
|
if (workflow === "auto-summary") {
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
1620
|
+
summarize = async (searchResults) => {
|
|
1621
|
+
if (!ctx) {
|
|
1622
|
+
return {
|
|
1623
|
+
content: [{ type: "text", text: "Error: Auto-summary requires an active extension context." }],
|
|
1624
|
+
details: { error: "Missing extension context" },
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
onUpdate?.({
|
|
1628
|
+
content: [{ type: "text", text: "Generating summary..." }],
|
|
1629
|
+
details: { phase: "generating-summary", progress: 1 },
|
|
1630
|
+
});
|
|
1631
|
+
const summaryContext: SummaryGenerationContext = {
|
|
1632
|
+
model: ctx.model,
|
|
1633
|
+
modelRegistry: ctx.modelRegistry,
|
|
1634
|
+
sessionManager: ctx.sessionManager,
|
|
1635
|
+
scopedModels: ctx.scopedModels,
|
|
2135
1636
|
};
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
1637
|
+
const summaryModelChoices = await loadSummaryModelChoices(summaryContext);
|
|
1638
|
+
const generated = await generateSummaryDraft(
|
|
1639
|
+
searchResults,
|
|
1640
|
+
summaryContext,
|
|
1641
|
+
signal,
|
|
1642
|
+
summaryModelChoices.defaultSummaryModel ?? undefined,
|
|
1643
|
+
undefined,
|
|
1644
|
+
undefined,
|
|
1645
|
+
getSummaryGenerationDeadlineMs(),
|
|
1646
|
+
getSummaryInstructions(),
|
|
1647
|
+
);
|
|
1648
|
+
return { approvedSummary: generated.summary, summaryMeta: generated.meta };
|
|
2146
1649
|
};
|
|
2147
|
-
const summaryModelChoices = await loadSummaryModelChoices(summaryContext);
|
|
2148
|
-
const generated = await generateSummaryDraft(
|
|
2149
|
-
searchResults,
|
|
2150
|
-
summaryContext,
|
|
2151
|
-
signal,
|
|
2152
|
-
summaryModelChoices.defaultSummaryModel ?? undefined,
|
|
2153
|
-
undefined,
|
|
2154
|
-
undefined,
|
|
2155
|
-
getSummaryGenerationDeadlineMs(),
|
|
2156
|
-
);
|
|
2157
|
-
approvedSummary = generated.summary;
|
|
2158
|
-
summaryMeta = generated.meta;
|
|
2159
1650
|
}
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
queryList,
|
|
2163
|
-
results: searchResults,
|
|
2164
|
-
urls: allUrls,
|
|
2165
|
-
includeContent: params.includeContent ?? false,
|
|
2166
|
-
inlineContent: allInlineContent.length > 0 ? allInlineContent : undefined,
|
|
2167
|
-
workflow: workflow === "auto-summary" ? "auto-summary" : undefined,
|
|
2168
|
-
approvedSummary,
|
|
2169
|
-
summaryMeta,
|
|
2170
|
-
proxy: typeof params.proxy === "string" ? params.proxy : undefined,
|
|
2171
|
-
});
|
|
2172
|
-
});
|
|
1651
|
+
return core.webSearch(params, signal, { extensionContext: ctx, onUpdate, summarize, awaitContent: nested });
|
|
1652
|
+
}));
|
|
2173
1653
|
},
|
|
2174
1654
|
|
|
2175
1655
|
renderCall(args, theme) {
|
|
@@ -2444,84 +1924,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2444
1924
|
})),
|
|
2445
1925
|
}),
|
|
2446
1926
|
async execute(_callId, params, signal, _onUpdate, ctx) {
|
|
2447
|
-
return
|
|
2448
|
-
const claim = typeof params.claim === "string" ? params.claim.trim() : "";
|
|
2449
|
-
if (!claim) {
|
|
2450
|
-
return { content: [{ type: "text", text: "Error: 'claim' is required." }], details: { error: "Missing claim" } };
|
|
2451
|
-
}
|
|
2452
|
-
|
|
2453
|
-
const requestedQueries = Array.isArray(params.queries)
|
|
2454
|
-
? params.queries.filter((query): query is string => typeof query === "string").map((query) => query.trim()).filter(Boolean)
|
|
2455
|
-
: [];
|
|
2456
|
-
const queries = (requestedQueries.length > 0 ? requestedQueries : [claim]).slice(0, 8);
|
|
2457
|
-
const numResults = typeof params.numResults === "number" && Number.isFinite(params.numResults)
|
|
2458
|
-
? Math.min(20, Math.max(1, Math.floor(params.numResults)))
|
|
2459
|
-
: 5;
|
|
2460
|
-
const domainFilter = Array.isArray(params.domainFilter)
|
|
2461
|
-
? params.domainFilter.filter((domain): domain is string => typeof domain === "string")
|
|
2462
|
-
: undefined;
|
|
2463
|
-
const recencyFilter = normalizeRecencyFilter(params.recencyFilter);
|
|
2464
|
-
const resultsByUrl = new Map<string, SearchResult>();
|
|
2465
|
-
const summaries: string[] = [];
|
|
2466
|
-
const errors: Array<{ query: string; error: string }> = [];
|
|
2467
|
-
let provider: string | undefined;
|
|
2468
|
-
|
|
2469
|
-
for (const query of queries) {
|
|
2470
|
-
if (signal?.aborted) break;
|
|
2471
|
-
try {
|
|
2472
|
-
const response = await search(query, {
|
|
2473
|
-
provider: resolveRequestedProvider(params.provider),
|
|
2474
|
-
numResults,
|
|
2475
|
-
recencyFilter,
|
|
2476
|
-
domainFilter,
|
|
2477
|
-
signal,
|
|
2478
|
-
extensionContext: ctx,
|
|
2479
|
-
});
|
|
2480
|
-
if (signal?.aborted) break;
|
|
2481
|
-
provider ??= response.provider;
|
|
2482
|
-
if (response.answer) summaries.push(`${query}: ${response.answer}`);
|
|
2483
|
-
for (const result of response.results) {
|
|
2484
|
-
if (!resultsByUrl.has(result.url)) resultsByUrl.set(result.url, result);
|
|
2485
|
-
}
|
|
2486
|
-
} catch (err) {
|
|
2487
|
-
if (signal?.aborted || isAbortError(err)) break;
|
|
2488
|
-
errors.push({ query, error: err instanceof Error ? err.message : String(err) });
|
|
2489
|
-
}
|
|
2490
|
-
}
|
|
2491
|
-
|
|
2492
|
-
const results = [...resultsByUrl.values()].slice(0, 20).map((result, index) => ({ ...result, rank: index + 1 }));
|
|
2493
|
-
let fetched: ExtractedContent[] = [];
|
|
2494
|
-
if (params.fetchContent && results.length > 0) {
|
|
2495
|
-
const urls = results.slice(0, 5).map((result) => result.url);
|
|
2496
|
-
try {
|
|
2497
|
-
fetched = await fetchAllContent(urls, signal, withRegisteredFetchOptions(undefined, registeredToolNames, typeof params.proxy === "string" ? params.proxy : undefined));
|
|
2498
|
-
} catch (err) {
|
|
2499
|
-
if (signal?.aborted || isAbortError(err)) throw err;
|
|
2500
|
-
fetched = urls.map((url) => ({ url, title: "", content: "", error: err instanceof Error ? err.message : String(err) }));
|
|
2501
|
-
}
|
|
2502
|
-
}
|
|
2503
|
-
const artifact = withClaimAssessment(buildResearchArtifact({
|
|
2504
|
-
query: claim,
|
|
2505
|
-
provider,
|
|
2506
|
-
summary: summaries.length > 0 ? summaries.join("\n\n") : undefined,
|
|
2507
|
-
results,
|
|
2508
|
-
fetched,
|
|
2509
|
-
recency: recencyFilter,
|
|
2510
|
-
domainFilter,
|
|
2511
|
-
}), [claim]);
|
|
2512
|
-
if (errors.length > 0) artifact.errors = errors;
|
|
2513
|
-
storeResearchArtifact(artifact);
|
|
2514
|
-
pi.appendEntry("web-search-results", {
|
|
2515
|
-
id: artifact.id,
|
|
2516
|
-
type: "research",
|
|
2517
|
-
timestamp: artifact.timestamp,
|
|
2518
|
-
artifact,
|
|
2519
|
-
});
|
|
2520
|
-
return {
|
|
2521
|
-
content: [{ type: "text", text: formatSourceCheckResult(artifact, getSearchContentEnabled ? toolNames.getSearchContent : null) }],
|
|
2522
|
-
details: { responseId: artifact.id, artifact, sourceCount: artifact.sources.length, passageCount: artifact.passages.length },
|
|
2523
|
-
};
|
|
2524
|
-
});
|
|
1927
|
+
return markToolError(await core.sourceCheck(params, signal, { extensionContext: ctx }));
|
|
2525
1928
|
},
|
|
2526
1929
|
});
|
|
2527
1930
|
|
|
@@ -2568,177 +1971,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
2568
1971
|
description: "http(s) or socks proxy URL (e.g. http://host:port or socks5h://host:port) used for this fetch. Needed when the target is unreachable directly; localhost and NO_PROXY hosts always bypass the proxy. Empty string forces direct access.",
|
|
2569
1972
|
})),
|
|
2570
1973
|
}),
|
|
1974
|
+
outputSchema: fetchContentOutputSchema,
|
|
2571
1975
|
|
|
2572
1976
|
async execute(_toolCallId, params, signal, onUpdate, ctx): Promise<AgentToolResult<Record<string, unknown>>> {
|
|
2573
|
-
|
|
2574
|
-
try {
|
|
2575
|
-
normalized = normalizeFetchContentParams(params);
|
|
2576
|
-
} catch (err) {
|
|
2577
|
-
const error = err instanceof Error ? err.message : String(err);
|
|
2578
|
-
return { content: [{ type: "text", text: `Error: ${error}` }], details: { error } };
|
|
2579
|
-
}
|
|
2580
|
-
const { urlList, options } = normalized;
|
|
2581
|
-
const mode = options.mode ?? fetchModeConfig.defaultMode;
|
|
2582
|
-
if (!fetchModeConfig.allowedModes.includes(mode)) {
|
|
2583
|
-
const error = `Fetch mode "${mode}" is disabled by fetch.allowedModes.`;
|
|
2584
|
-
return { content: [{ type: "text", text: `Error: ${error}` }], details: { error } };
|
|
2585
|
-
}
|
|
2586
|
-
return runWithProxy(options.proxy, async () => {
|
|
2587
|
-
if (mode === "answer" && !options.prompt) {
|
|
2588
|
-
return { content: [{ type: "text", text: "Error: mode answer requires prompt." }], details: { error: "mode answer requires prompt" } };
|
|
2589
|
-
}
|
|
2590
|
-
if (mode === "raw" && (options.forceClone === true || options.timestamp || options.frames || options.prompt || options.model || options.answerModel)) {
|
|
2591
|
-
return { content: [{ type: "text", text: "Error: mode raw cannot be combined with forceClone, prompt, timestamp, frames, model, or answerModel." }], details: { error: "Incompatible raw mode options" } };
|
|
2592
|
-
}
|
|
2593
|
-
if (mode !== "answer" && options.answerModel) {
|
|
2594
|
-
return { content: [{ type: "text", text: "Error: answerModel requires mode answer." }], details: { error: "answerModel requires mode answer" } };
|
|
2595
|
-
}
|
|
2596
|
-
if (mode === "answer" && options.model) {
|
|
2597
|
-
return { content: [{ type: "text", text: "Error: use answerModel, not model, with mode answer." }], details: { error: "model is incompatible with mode answer" } };
|
|
2598
|
-
}
|
|
2599
|
-
if (mode === "answer" && options.auth !== undefined) {
|
|
2600
|
-
return { content: [{ type: "text", text: "Error: auth cannot be combined with mode answer." }], details: { error: "auth cannot be combined with mode answer" } };
|
|
2601
|
-
}
|
|
2602
|
-
let authFetchProfile: AuthFetchProfile | undefined;
|
|
2603
|
-
if (options.auth !== undefined) {
|
|
2604
|
-
try {
|
|
2605
|
-
authFetchProfile = resolveAuthFetchProfile(options.auth);
|
|
2606
|
-
} catch (err) {
|
|
2607
|
-
const error = err instanceof Error ? err.message : String(err);
|
|
2608
|
-
return { content: [{ type: "text", text: `Error: ${error}` }], details: { error } };
|
|
2609
|
-
}
|
|
2610
|
-
}
|
|
2611
|
-
if (urlList.length === 0) {
|
|
2612
|
-
return {
|
|
2613
|
-
content: [{ type: "text", text: "Error: No URL provided." }],
|
|
2614
|
-
details: { error: "No URL provided" },
|
|
2615
|
-
};
|
|
2616
|
-
}
|
|
2617
|
-
|
|
2618
|
-
onUpdate?.({
|
|
2619
|
-
content: [{ type: "text", text: `Fetching ${urlList.length} URL(s)...` }],
|
|
2620
|
-
details: { phase: "fetch", progress: 0 },
|
|
2621
|
-
});
|
|
2622
|
-
|
|
2623
|
-
const { answerModel: _answerModel, auth: _auth, ...extractionOptions } = { ...options, mode };
|
|
2624
|
-
const { prompt: _prompt, ...answerExtractionOptions } = extractionOptions;
|
|
2625
|
-
const fetchOptions = {
|
|
2626
|
-
...(mode === "answer" ? answerExtractionOptions : extractionOptions),
|
|
2627
|
-
...(authFetchProfile ? { authFetchProfile } : {}),
|
|
2628
|
-
};
|
|
2629
|
-
const fetchResults = await fetchAllContent(urlList, signal, withRegisteredFetchOptions(fetchOptions, registeredToolNames, options.proxy));
|
|
2630
|
-
const presentedResults = mode === "answer"
|
|
2631
|
-
? await Promise.all(fetchResults.map(async result => {
|
|
2632
|
-
if (result.error) return result;
|
|
2633
|
-
if (result.thumbnail || result.mimeType?.startsWith("image/")) {
|
|
2634
|
-
return { ...result, error: "Page answer requires textual fetched content" };
|
|
2635
|
-
}
|
|
2636
|
-
try {
|
|
2637
|
-
const answer = await answerFromPage({
|
|
2638
|
-
question: options.prompt!,
|
|
2639
|
-
pageText: result.content,
|
|
2640
|
-
sourceUrl: result.url,
|
|
2641
|
-
...(options.answerModel ? { model: options.answerModel } : {}),
|
|
2642
|
-
}, ctx, signal);
|
|
2643
|
-
return { ...result, content: answer.text };
|
|
2644
|
-
} catch (err) {
|
|
2645
|
-
return { ...result, error: `Page answer failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
2646
|
-
}
|
|
2647
|
-
}))
|
|
2648
|
-
: fetchResults;
|
|
2649
|
-
const successful = presentedResults.filter((r) => !r.error).length;
|
|
2650
|
-
const totalChars = presentedResults.reduce((sum, r) => sum + r.content.length, 0);
|
|
2651
|
-
|
|
2652
|
-
const responseId = generateId();
|
|
2653
|
-
const data = {
|
|
2654
|
-
id: responseId,
|
|
2655
|
-
type: "fetch",
|
|
2656
|
-
timestamp: Date.now(),
|
|
2657
|
-
urls: stripThumbnails(fetchResults),
|
|
2658
|
-
} satisfies StoredSearchData & { type: "fetch"; urls: ExtractedContent[] };
|
|
2659
|
-
const storedContent = storeFetchResult(pi, responseId, data, authFetchProfile);
|
|
2660
|
-
|
|
2661
|
-
if (urlList.length === 1) {
|
|
2662
|
-
const result = presentedResults[0];
|
|
2663
|
-
if (result.error) {
|
|
2664
|
-
return {
|
|
2665
|
-
content: [{ type: "text", text: `Error: ${result.error}` }],
|
|
2666
|
-
details: { urls: urlList, urlCount: 1, successful: 0, error: result.error, ...(storedContent ? { responseId } : {}), prompt: params.prompt, timestamp: params.timestamp, frames: params.frames },
|
|
2667
|
-
};
|
|
2668
|
-
}
|
|
2669
|
-
|
|
2670
|
-
const fullLength = result.content.length;
|
|
2671
|
-
const slice = initialContentSlice(result.content, getMaxInlineContentChars());
|
|
2672
|
-
const truncated = slice.endOffset < fullLength;
|
|
2673
|
-
let output = slice.text;
|
|
2674
|
-
|
|
2675
|
-
if (truncated) {
|
|
2676
|
-
output += `\n\n---\nShowing ${slice.endOffset} of ${fullLength} chars, ${slice.shownBytes} of ${slice.totalBytes} bytes, and ${slice.shownLines} of ${slice.totalLines} lines. `;
|
|
2677
|
-
output += storedContent
|
|
2678
|
-
? getSearchContentEnabled
|
|
2679
|
-
? `Use ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0, offset: ${slice.endOffset} }) for the next slice.`
|
|
2680
|
-
: "Content retrieval is not registered."
|
|
2681
|
-
: "Authenticated fetch cache is off; repeat the fetch to read more.";
|
|
2682
|
-
}
|
|
2683
|
-
|
|
2684
|
-
const content: Array<TextContent | ImageContent> = [];
|
|
2685
|
-
if (result.frames?.length) {
|
|
2686
|
-
for (const frame of result.frames) {
|
|
2687
|
-
content.push({ type: "image", data: frame.data, mimeType: frame.mimeType });
|
|
2688
|
-
content.push({ type: "text", text: `Frame at ${frame.timestamp}` });
|
|
2689
|
-
}
|
|
2690
|
-
} else if (result.thumbnail) {
|
|
2691
|
-
content.push({ type: "image", data: result.thumbnail.data, mimeType: result.thumbnail.mimeType });
|
|
2692
|
-
}
|
|
2693
|
-
content.push({ type: "text", text: output });
|
|
2694
|
-
|
|
2695
|
-
const imageCount = (result.frames?.length ?? 0) + (result.thumbnail ? 1 : 0);
|
|
2696
|
-
return {
|
|
2697
|
-
content,
|
|
2698
|
-
details: {
|
|
2699
|
-
urls: urlList,
|
|
2700
|
-
urlCount: 1,
|
|
2701
|
-
successful: 1,
|
|
2702
|
-
totalChars: fullLength,
|
|
2703
|
-
title: result.title,
|
|
2704
|
-
...(storedContent ? { responseId } : {}),
|
|
2705
|
-
truncated,
|
|
2706
|
-
hasImage: imageCount > 0,
|
|
2707
|
-
imageCount,
|
|
2708
|
-
prompt: params.prompt,
|
|
2709
|
-
timestamp: params.timestamp,
|
|
2710
|
-
frames: params.frames,
|
|
2711
|
-
duration: result.duration,
|
|
2712
|
-
mode,
|
|
2713
|
-
mimeType: result.mimeType,
|
|
2714
|
-
status: result.status,
|
|
2715
|
-
totalBytes: slice.totalBytes,
|
|
2716
|
-
totalLines: slice.totalLines,
|
|
2717
|
-
shownBytes: slice.shownBytes,
|
|
2718
|
-
shownLines: slice.shownLines,
|
|
2719
|
-
},
|
|
2720
|
-
};
|
|
2721
|
-
}
|
|
2722
|
-
|
|
2723
|
-
let output = "## Fetched URLs\n\n";
|
|
2724
|
-
for (const { url, title, content, error } of presentedResults) {
|
|
2725
|
-
if (error) {
|
|
2726
|
-
output += `- ${url}: Error - ${error}\n`;
|
|
2727
|
-
} else {
|
|
2728
|
-
output += `- ${title || url} (${content.length} chars)\n`;
|
|
2729
|
-
}
|
|
2730
|
-
}
|
|
2731
|
-
output += storedContent
|
|
2732
|
-
? getSearchContentEnabled
|
|
2733
|
-
? `\n---\nUse ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0 }) to retrieve bounded content slices.`
|
|
2734
|
-
: "\n---\nContent retrieval is not registered."
|
|
2735
|
-
: "\n---\nAuthenticated fetch cache is off; repeat the fetch to read content.";
|
|
2736
|
-
|
|
2737
|
-
return {
|
|
2738
|
-
content: [{ type: "text", text: output }],
|
|
2739
|
-
details: { urls: urlList, urlCount: urlList.length, successful, totalChars, ...(storedContent ? { responseId } : {}) },
|
|
2740
|
-
};
|
|
2741
|
-
});
|
|
1977
|
+
return markToolError(await core.fetchContent(params, signal, { extensionContext: ctx, onUpdate }));
|
|
2742
1978
|
},
|
|
2743
1979
|
|
|
2744
1980
|
renderCall(args, theme) {
|
|
@@ -2902,286 +2138,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2902
2138
|
}),
|
|
2903
2139
|
|
|
2904
2140
|
async execute(_toolCallId, rawParams): Promise<AgentToolResult<Record<string, unknown>>> {
|
|
2905
|
-
|
|
2906
|
-
if (params.findMode !== undefined && params.findText === undefined) {
|
|
2907
|
-
return {
|
|
2908
|
-
content: [{ type: "text", text: `findMode ${formatInputValue(params.findMode)} requires findText; provide findText or omit findMode.` }],
|
|
2909
|
-
details: { error: "findMode requires findText" },
|
|
2910
|
-
};
|
|
2911
|
-
}
|
|
2912
|
-
const data = getResult(params.responseId);
|
|
2913
|
-
if (!data) {
|
|
2914
|
-
return {
|
|
2915
|
-
content: [{ type: "text", text: `Error: No stored results for responseId ${formatInputValue(params.responseId)}. Use a responseId returned by ${storedContentSources}.` }],
|
|
2916
|
-
details: { error: "Not found", responseId: params.responseId },
|
|
2917
|
-
};
|
|
2918
|
-
}
|
|
2919
|
-
|
|
2920
|
-
if (data.type === "research") {
|
|
2921
|
-
const artifact = getResearchArtifact(params.responseId);
|
|
2922
|
-
if (!artifact) {
|
|
2923
|
-
return {
|
|
2924
|
-
content: [{ type: "text", text: `Error: stored research artifact for responseId ${formatInputValue(params.responseId)} was not found. Use a responseId returned by ${storedContentSources}.` }],
|
|
2925
|
-
details: { error: "Artifact not found", responseId: params.responseId },
|
|
2926
|
-
};
|
|
2927
|
-
}
|
|
2928
|
-
const serialized = JSON.stringify(artifact, null, 2);
|
|
2929
|
-
if (params.findText !== undefined) {
|
|
2930
|
-
try {
|
|
2931
|
-
const found = findContent(serialized, normalizeFindQueries(params.findText), params.findMode ?? "case-insensitive");
|
|
2932
|
-
const { text, ...findDetails } = found;
|
|
2933
|
-
return {
|
|
2934
|
-
content: [{ type: "text", text }],
|
|
2935
|
-
details: { responseId: artifact.id, type: "research", contentLength: serialized.length, findMode: params.findMode ?? "case-insensitive", ...findDetails },
|
|
2936
|
-
};
|
|
2937
|
-
} catch (err) {
|
|
2938
|
-
const error = err instanceof Error ? err.message : String(err);
|
|
2939
|
-
return {
|
|
2940
|
-
content: [{ type: "text", text: `Unable to find ${formatInputValue(params.findText)} in research artifact for responseId ${formatInputValue(params.responseId)}: ${error}. Check findText and use a supported findMode.` }],
|
|
2941
|
-
details: { error, responseId: params.responseId, type: "research" },
|
|
2942
|
-
};
|
|
2943
|
-
}
|
|
2944
|
-
}
|
|
2945
|
-
const offset = params.offset ?? 0;
|
|
2946
|
-
const limit = params.limit ?? maxInlineContentChars;
|
|
2947
|
-
if (!Number.isInteger(offset) || offset < 0) {
|
|
2948
|
-
return {
|
|
2949
|
-
content: [{ type: "text", text: `Invalid offset: received ${formatInputValue(offset)} for responseId ${formatInputValue(params.responseId)}; offset must be a non-negative integer. Use 0 or a larger integer.` }],
|
|
2950
|
-
details: { error: "Invalid offset", offset },
|
|
2951
|
-
};
|
|
2952
|
-
}
|
|
2953
|
-
if (!Number.isInteger(limit) || limit <= 0 || limit > maxInlineContentChars) {
|
|
2954
|
-
return {
|
|
2955
|
-
content: [{ type: "text", text: `Invalid limit: received ${formatInputValue(limit)} for responseId ${formatInputValue(params.responseId)}; limit must be an integer from 1 to ${maxInlineContentChars}. Use a value in that range.` }],
|
|
2956
|
-
details: { error: "Invalid limit", limit, maxLimit: maxInlineContentChars },
|
|
2957
|
-
};
|
|
2958
|
-
}
|
|
2959
|
-
if (offset > serialized.length) {
|
|
2960
|
-
return {
|
|
2961
|
-
content: [{ type: "text", text: `Offset ${offset} is out of range for responseId ${formatInputValue(params.responseId)}. Received offset ${offset}; valid range is 0-${serialized.length}. Use an offset within that range.` }],
|
|
2962
|
-
details: { error: "Offset out of range", offset, contentLength: serialized.length },
|
|
2963
|
-
};
|
|
2964
|
-
}
|
|
2965
|
-
const endOffset = Math.min(offset + limit, serialized.length);
|
|
2966
|
-
const artifactSlice = serialized.slice(offset, endOffset);
|
|
2967
|
-
const hasMore = endOffset < serialized.length;
|
|
2968
|
-
return {
|
|
2969
|
-
content: [{ type: "text", text: artifactSlice }],
|
|
2970
|
-
details: { responseId: artifact.id, type: "research", contentLength: serialized.length, offset, limit, returnedChars: artifactSlice.length, nextOffset: hasMore ? endOffset : null, truncated: hasMore },
|
|
2971
|
-
};
|
|
2972
|
-
}
|
|
2973
|
-
|
|
2974
|
-
if (data.type === "search" && data.queries) {
|
|
2975
|
-
let queryData: QueryResultData | undefined;
|
|
2976
|
-
|
|
2977
|
-
if (params.query !== undefined) {
|
|
2978
|
-
queryData = data.queries.find((q) => q.query === params.query);
|
|
2979
|
-
if (!queryData) {
|
|
2980
|
-
const available = data.queries.map((q) => `"${q.query}"`).join(", ");
|
|
2981
|
-
return {
|
|
2982
|
-
content: [{ type: "text", text: `Query ${formatInputValue(params.query)} was not found for responseId ${formatInputValue(params.responseId)}. Received query=${formatInputValue(params.query)}. Available queries: ${available || "none"}. Use one of the available queries or queryIndex.` }],
|
|
2983
|
-
details: { error: "Query not found" },
|
|
2984
|
-
};
|
|
2985
|
-
}
|
|
2986
|
-
} else if (params.queryIndex !== undefined) {
|
|
2987
|
-
queryData = data.queries[params.queryIndex];
|
|
2988
|
-
if (!queryData) {
|
|
2989
|
-
const available = data.queries.map((q, i) => `${i}: "${q.query}"`).join(", ");
|
|
2990
|
-
return {
|
|
2991
|
-
content: [{ type: "text", text: `Query index ${formatInputValue(params.queryIndex)} is out of range for responseId ${formatInputValue(params.responseId)}. Received queryIndex=${formatInputValue(params.queryIndex)}; valid indexes are 0-${data.queries.length - 1}. Available queries: ${available || "none"}. Use one of the available indexes.` }],
|
|
2992
|
-
details: { error: "Index out of range" },
|
|
2993
|
-
};
|
|
2994
|
-
}
|
|
2995
|
-
} else {
|
|
2996
|
-
const available = data.queries.map((q, i) => `${i}: "${q.query}"`).join(", ");
|
|
2997
|
-
return {
|
|
2998
|
-
content: [{ type: "text", text: `Specify query or queryIndex for responseId ${formatInputValue(params.responseId)}. Available queries: ${available || "none"}.` }],
|
|
2999
|
-
details: { error: "No query specified" },
|
|
3000
|
-
};
|
|
3001
|
-
}
|
|
3002
|
-
|
|
3003
|
-
if (queryData.error) {
|
|
3004
|
-
return {
|
|
3005
|
-
content: [{ type: "text", text: `Error retrieving query ${formatInputValue(queryData.query)} from responseId ${formatInputValue(params.responseId)}: ${queryData.error}. Check the stored search result and retry with another query or queryIndex if needed.` }],
|
|
3006
|
-
details: { error: queryData.error, query: queryData.query },
|
|
3007
|
-
};
|
|
3008
|
-
}
|
|
3009
|
-
|
|
3010
|
-
const fullResults = formatFullResults(queryData);
|
|
3011
|
-
if (params.findText !== undefined) {
|
|
3012
|
-
try {
|
|
3013
|
-
const found = findContent(fullResults, normalizeFindQueries(params.findText), params.findMode ?? "case-insensitive");
|
|
3014
|
-
const { text, ...findDetails } = found;
|
|
3015
|
-
return {
|
|
3016
|
-
content: [{ type: "text", text }],
|
|
3017
|
-
details: { responseId: params.responseId, query: queryData.query, resultCount: queryData.results.length, contentLength: fullResults.length, findMode: params.findMode ?? "case-insensitive", ...findDetails },
|
|
3018
|
-
};
|
|
3019
|
-
} catch (err) {
|
|
3020
|
-
const error = err instanceof Error ? err.message : String(err);
|
|
3021
|
-
return {
|
|
3022
|
-
content: [{ type: "text", text: `Unable to find ${formatInputValue(params.findText)} in query ${formatInputValue(queryData.query)} for responseId ${formatInputValue(params.responseId)}: ${error}. Check findText and use a supported findMode.` }],
|
|
3023
|
-
details: { error, query: queryData.query },
|
|
3024
|
-
};
|
|
3025
|
-
}
|
|
3026
|
-
}
|
|
3027
|
-
|
|
3028
|
-
const offset = params.offset ?? 0;
|
|
3029
|
-
const limit = params.limit ?? maxInlineContentChars;
|
|
3030
|
-
if (!Number.isInteger(offset) || offset < 0) {
|
|
3031
|
-
return {
|
|
3032
|
-
content: [{ type: "text", text: `Invalid offset: received ${formatInputValue(offset)} for query ${formatInputValue(queryData.query)}; offset must be a non-negative integer. Use 0 or a larger integer.` }],
|
|
3033
|
-
details: { error: "Invalid offset", offset },
|
|
3034
|
-
};
|
|
3035
|
-
}
|
|
3036
|
-
if (!Number.isInteger(limit) || limit <= 0 || limit > maxInlineContentChars) {
|
|
3037
|
-
return {
|
|
3038
|
-
content: [{ type: "text", text: `Invalid limit: received ${formatInputValue(limit)} for query ${formatInputValue(queryData.query)}; limit must be an integer from 1 to ${maxInlineContentChars}. Use a value in that range.` }],
|
|
3039
|
-
details: { error: "Invalid limit", limit, maxLimit: maxInlineContentChars },
|
|
3040
|
-
};
|
|
3041
|
-
}
|
|
3042
|
-
if (offset > fullResults.length) {
|
|
3043
|
-
return {
|
|
3044
|
-
content: [{ type: "text", text: `Offset ${offset} is out of range for query ${formatInputValue(queryData.query)} in responseId ${formatInputValue(params.responseId)}. Received offset ${offset}; valid range is 0-${fullResults.length}. Use an offset within that range.` }],
|
|
3045
|
-
details: { error: "Offset out of range", offset, contentLength: fullResults.length },
|
|
3046
|
-
};
|
|
3047
|
-
}
|
|
3048
|
-
const queryIndex = data.queries.indexOf(queryData);
|
|
3049
|
-
let returnedChars = Math.min(limit, fullResults.length - offset);
|
|
3050
|
-
let endOffset = offset + returnedChars;
|
|
3051
|
-
let continuation = "";
|
|
3052
|
-
while (endOffset < fullResults.length) {
|
|
3053
|
-
continuation = `\n\n---\nShowing chars ${offset}-${endOffset} of ${fullResults.length}. Use ${toolNames.getSearchContent}({ responseId: "${params.responseId}", queryIndex: ${queryIndex}, offset: ${endOffset}, limit: ${limit} }) for the next slice.`;
|
|
3054
|
-
const overflow = returnedChars + continuation.length - maxInlineContentChars;
|
|
3055
|
-
if (overflow <= 0) break;
|
|
3056
|
-
returnedChars -= overflow;
|
|
3057
|
-
endOffset = offset + returnedChars;
|
|
3058
|
-
}
|
|
3059
|
-
const resultSlice = fullResults.slice(offset, endOffset);
|
|
3060
|
-
const hasMore = endOffset < fullResults.length;
|
|
3061
|
-
const text = `${resultSlice}${hasMore ? continuation : ""}`;
|
|
3062
|
-
return {
|
|
3063
|
-
content: [{ type: "text", text }],
|
|
3064
|
-
details: {
|
|
3065
|
-
responseId: params.responseId,
|
|
3066
|
-
query: queryData.query,
|
|
3067
|
-
resultCount: queryData.results.length,
|
|
3068
|
-
contentLength: fullResults.length,
|
|
3069
|
-
offset,
|
|
3070
|
-
limit,
|
|
3071
|
-
returnedChars,
|
|
3072
|
-
nextOffset: hasMore ? endOffset : null,
|
|
3073
|
-
truncated: hasMore,
|
|
3074
|
-
},
|
|
3075
|
-
};
|
|
3076
|
-
}
|
|
3077
|
-
|
|
3078
|
-
if (data.type === "fetch" && data.urls) {
|
|
3079
|
-
let urlData: ExtractedContent | undefined;
|
|
3080
|
-
let selectedUrlIndex = -1;
|
|
3081
|
-
|
|
3082
|
-
if (params.url !== undefined) {
|
|
3083
|
-
selectedUrlIndex = data.urls.findIndex((u) => u.url === params.url);
|
|
3084
|
-
urlData = data.urls[selectedUrlIndex];
|
|
3085
|
-
if (!urlData) {
|
|
3086
|
-
const available = data.urls.map((u) => u.url).join("\n ");
|
|
3087
|
-
return {
|
|
3088
|
-
content: [{ type: "text", text: `URL ${formatInputValue(params.url)} was not found for responseId ${formatInputValue(params.responseId)}. Received url=${formatInputValue(params.url)}. Available URLs:\n ${available || " none"}\nUse one of the available URLs or urlIndex.` }],
|
|
3089
|
-
details: { error: "URL not found" },
|
|
3090
|
-
};
|
|
3091
|
-
}
|
|
3092
|
-
} else if (params.urlIndex !== undefined) {
|
|
3093
|
-
selectedUrlIndex = params.urlIndex;
|
|
3094
|
-
urlData = data.urls[selectedUrlIndex];
|
|
3095
|
-
if (!urlData) {
|
|
3096
|
-
const available = data.urls.map((u, i) => `${i}: ${u.url}`).join("\n ");
|
|
3097
|
-
return {
|
|
3098
|
-
content: [{ type: "text", text: `URL index ${formatInputValue(params.urlIndex)} is out of range for responseId ${formatInputValue(params.responseId)}. Received urlIndex=${formatInputValue(params.urlIndex)}; valid indexes are 0-${data.urls.length - 1}. Available URLs:\n ${available || " none"}\nUse one of the available indexes.` }],
|
|
3099
|
-
details: { error: "Index out of range" },
|
|
3100
|
-
};
|
|
3101
|
-
}
|
|
3102
|
-
} else {
|
|
3103
|
-
const available = data.urls.map((u, i) => `${i}: ${u.url}`).join("\n ");
|
|
3104
|
-
return {
|
|
3105
|
-
content: [{ type: "text", text: `Specify url or urlIndex for responseId ${formatInputValue(params.responseId)}. Available URLs:\n ${available || " none"}` }],
|
|
3106
|
-
details: { error: "No URL specified" },
|
|
3107
|
-
};
|
|
3108
|
-
}
|
|
3109
|
-
|
|
3110
|
-
if (urlData.error) {
|
|
3111
|
-
return {
|
|
3112
|
-
content: [{ type: "text", text: `Error retrieving URL ${formatInputValue(urlData.url)} from responseId ${formatInputValue(params.responseId)}: ${urlData.error}. Check the stored fetch result and retry with another URL or urlIndex if needed.` }],
|
|
3113
|
-
details: { error: urlData.error, url: urlData.url },
|
|
3114
|
-
};
|
|
3115
|
-
}
|
|
3116
|
-
|
|
3117
|
-
if (params.findText !== undefined) {
|
|
3118
|
-
try {
|
|
3119
|
-
const found = findContent(urlData.content, normalizeFindQueries(params.findText), params.findMode ?? "case-insensitive");
|
|
3120
|
-
const { text, ...findDetails } = found;
|
|
3121
|
-
return {
|
|
3122
|
-
content: [{ type: "text", text: `# ${urlData.title || urlData.url}\n\n${text}` }],
|
|
3123
|
-
details: { url: urlData.url, title: urlData.title, contentLength: urlData.content.length, findMode: params.findMode ?? "case-insensitive", ...findDetails },
|
|
3124
|
-
};
|
|
3125
|
-
} catch (err) {
|
|
3126
|
-
const error = err instanceof Error ? err.message : String(err);
|
|
3127
|
-
return {
|
|
3128
|
-
content: [{ type: "text", text: `Unable to find ${formatInputValue(params.findText)} in URL ${formatInputValue(urlData.url)} for responseId ${formatInputValue(params.responseId)}: ${error}. Check findText and use a supported findMode.` }],
|
|
3129
|
-
details: { error, url: urlData.url },
|
|
3130
|
-
};
|
|
3131
|
-
}
|
|
3132
|
-
}
|
|
3133
|
-
|
|
3134
|
-
const offset = params.offset ?? 0;
|
|
3135
|
-
const limit = params.limit ?? maxInlineContentChars;
|
|
3136
|
-
if (!Number.isInteger(offset) || offset < 0) {
|
|
3137
|
-
return {
|
|
3138
|
-
content: [{ type: "text", text: `Invalid offset: received ${formatInputValue(offset)} for URL ${formatInputValue(urlData.url)}; offset must be a non-negative integer. Use 0 or a larger integer.` }],
|
|
3139
|
-
details: { error: "Invalid offset", offset },
|
|
3140
|
-
};
|
|
3141
|
-
}
|
|
3142
|
-
if (!Number.isInteger(limit) || limit <= 0 || limit > maxInlineContentChars) {
|
|
3143
|
-
return {
|
|
3144
|
-
content: [{ type: "text", text: `Invalid limit: received ${formatInputValue(limit)} for URL ${formatInputValue(urlData.url)}; limit must be an integer from 1 to ${maxInlineContentChars}. Use a value in that range.` }],
|
|
3145
|
-
details: { error: "Invalid limit", limit, maxLimit: maxInlineContentChars },
|
|
3146
|
-
};
|
|
3147
|
-
}
|
|
3148
|
-
if (offset > urlData.content.length) {
|
|
3149
|
-
return {
|
|
3150
|
-
content: [{ type: "text", text: `Offset ${offset} is out of range for URL ${formatInputValue(urlData.url)} in responseId ${formatInputValue(params.responseId)}. Received offset ${offset}; valid range is 0-${urlData.content.length}. Use an offset within that range.` }],
|
|
3151
|
-
details: { error: "Offset out of range", offset, contentLength: urlData.content.length },
|
|
3152
|
-
};
|
|
3153
|
-
}
|
|
3154
|
-
|
|
3155
|
-
const endOffset = Math.min(offset + limit, urlData.content.length);
|
|
3156
|
-
const contentSlice = urlData.content.slice(offset, endOffset);
|
|
3157
|
-
const hasMore = endOffset < urlData.content.length;
|
|
3158
|
-
let text = `# ${urlData.title || urlData.url}\n\n${contentSlice}`;
|
|
3159
|
-
if (hasMore || offset > 0) {
|
|
3160
|
-
text += `\n\n---\nShowing chars ${offset}-${endOffset} of ${urlData.content.length}.`;
|
|
3161
|
-
if (hasMore) {
|
|
3162
|
-
text += ` Use ${toolNames.getSearchContent}({ responseId: "${params.responseId}", urlIndex: ${selectedUrlIndex}, offset: ${endOffset}, limit: ${limit} }) for the next slice.`;
|
|
3163
|
-
}
|
|
3164
|
-
}
|
|
3165
|
-
|
|
3166
|
-
return {
|
|
3167
|
-
content: [{ type: "text", text }],
|
|
3168
|
-
details: {
|
|
3169
|
-
url: urlData.url,
|
|
3170
|
-
title: urlData.title,
|
|
3171
|
-
contentLength: urlData.content.length,
|
|
3172
|
-
offset,
|
|
3173
|
-
limit,
|
|
3174
|
-
returnedChars: contentSlice.length,
|
|
3175
|
-
nextOffset: hasMore ? endOffset : null,
|
|
3176
|
-
truncated: hasMore,
|
|
3177
|
-
},
|
|
3178
|
-
};
|
|
3179
|
-
}
|
|
3180
|
-
|
|
3181
|
-
return {
|
|
3182
|
-
content: [{ type: "text", text: `Invalid stored data for responseId ${formatInputValue(params.responseId)}: received type ${formatInputValue(data.type)}. Use a responseId returned by ${storedContentSources}.` }],
|
|
3183
|
-
details: { error: "Invalid data" },
|
|
3184
|
-
};
|
|
2141
|
+
return markToolError(await core.getSearchContent(rawParams));
|
|
3185
2142
|
},
|
|
3186
2143
|
|
|
3187
2144
|
renderCall(args, theme) {
|