@herbertgao/pi-extensions 2026.9.19 → 2026.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -16
- package/THIRD_PARTY_NOTICES.md +29 -3
- 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 +85 -68
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +79 -62
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/preview.webp +0 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +116 -34
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +370 -118
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +6 -36
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/index.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +31 -33
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +330 -360
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/index.ts +56 -108
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/session.ts +97 -15
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/subagent.ts +80 -45
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/flush-docked-bash.ts +36 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer-layout.ts +229 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer.ts +521 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/mcp-chip.ts +136 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/startup-header.ts +92 -61
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/working-message.ts +58 -38
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +19 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +1127 -231
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +267 -262
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +38 -48
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +8 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +2 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +287 -114
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +15 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +18 -72
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +61 -26
- 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/ansi-utils.ts +25 -57
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-component.ts +125 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-edit-render.ts +227 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-header.ts +136 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-highlight.ts +151 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-inline.ts +324 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-layout.ts +762 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-limits.ts +152 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-palette.ts +410 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-parse.ts +535 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-presentation.ts +4 -4
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +21 -3219
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-text.ts +44 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-write-render.ts +568 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +103 -48
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts +8 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts +2 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +171 -196
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/mcp-title.ts +39 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{message-display.ts → tool/message-display.ts} +27 -93
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/names.ts +429 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +259 -164
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{show-more-hint.ts → tool/show-more-hint.ts} +10 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +8 -26
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-color.ts +44 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-text.ts +77 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/component-tree.ts +34 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/format.ts +28 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +51 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/patch-keys.ts +124 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/sgr-mouse.ts +70 -0
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +10 -8
- 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/@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-web-access/CHANGELOG.md +17 -0
- package/node_modules/pi-web-access/README.md +6 -3
- package/node_modules/pi-web-access/brave-rate-limit.ts +170 -0
- package/node_modules/pi-web-access/brave.ts +28 -11
- package/node_modules/pi-web-access/dist/index.js +202 -33
- package/node_modules/pi-web-access/gemini-search.ts +2 -5
- package/node_modules/pi-web-access/index.ts +13 -7
- package/node_modules/pi-web-access/openai-search.ts +24 -10
- package/node_modules/pi-web-access/package.json +1 -1
- package/node_modules/pi-web-access/summary-review.ts +8 -2
- 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 +18 -22
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +0 -101
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.svg +0 -101
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/welcome.webp +0 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/render-utils.ts +0 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +0 -30
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +0 -12
- 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/pi-footer/CHANGELOG.md +0 -145
- package/node_modules/pi-footer/LICENSE +0 -21
- package/node_modules/pi-footer/README.md +0 -354
- package/node_modules/pi-footer/package.json +0 -68
- package/node_modules/pi-footer/src/cache.ts +0 -106
- package/node_modules/pi-footer/src/colors.ts +0 -237
- package/node_modules/pi-footer/src/config.ts +0 -202
- package/node_modules/pi-footer/src/event-widgets.ts +0 -41
- package/node_modules/pi-footer/src/extension-statuses.ts +0 -102
- package/node_modules/pi-footer/src/git.ts +0 -135
- package/node_modules/pi-footer/src/index.ts +0 -268
- package/node_modules/pi-footer/src/metrics.ts +0 -141
- package/node_modules/pi-footer/src/presets.ts +0 -383
- package/node_modules/pi-footer/src/render.ts +0 -114
- package/node_modules/pi-footer/src/separators.ts +0 -52
- package/node_modules/pi-footer/src/types.ts +0 -122
- package/node_modules/pi-footer/src/ui/color-level-confirm.ts +0 -22
- package/node_modules/pi-footer/src/ui/color-options.ts +0 -195
- package/node_modules/pi-footer/src/ui/config-lifecycle.ts +0 -61
- package/node_modules/pi-footer/src/ui/edit-colors.ts +0 -13
- package/node_modules/pi-footer/src/ui/events.ts +0 -35
- package/node_modules/pi-footer/src/ui/extension-status-picker.ts +0 -60
- package/node_modules/pi-footer/src/ui/extension-statuses.ts +0 -73
- package/node_modules/pi-footer/src/ui/fields.ts +0 -252
- package/node_modules/pi-footer/src/ui/global-menu.ts +0 -126
- package/node_modules/pi-footer/src/ui/helpers.ts +0 -44
- package/node_modules/pi-footer/src/ui/layout.ts +0 -17
- package/node_modules/pi-footer/src/ui/line-list.ts +0 -19
- package/node_modules/pi-footer/src/ui/model.ts +0 -64
- package/node_modules/pi-footer/src/ui/navigation.ts +0 -29
- package/node_modules/pi-footer/src/ui/option-edit.ts +0 -93
- package/node_modules/pi-footer/src/ui/overlay-render.ts +0 -103
- package/node_modules/pi-footer/src/ui/screen-context.ts +0 -18
- package/node_modules/pi-footer/src/ui/screen-controller.ts +0 -27
- package/node_modules/pi-footer/src/ui/screen-render.ts +0 -32
- package/node_modules/pi-footer/src/ui/screen-state.ts +0 -20
- package/node_modules/pi-footer/src/ui/screen.ts +0 -243
- package/node_modules/pi-footer/src/ui/screens/add-widget.ts +0 -92
- package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +0 -52
- package/node_modules/pi-footer/src/ui/screens/controller.ts +0 -12
- package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +0 -68
- package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +0 -115
- package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +0 -61
- package/node_modules/pi-footer/src/ui/screens/global.ts +0 -75
- package/node_modules/pi-footer/src/ui/screens/line-list.ts +0 -108
- package/node_modules/pi-footer/src/ui/screens/main.ts +0 -99
- package/node_modules/pi-footer/src/ui/screens/terminal.ts +0 -121
- package/node_modules/pi-footer/src/ui/screens/widget-list.ts +0 -154
- package/node_modules/pi-footer/src/ui/terminal-menu.ts +0 -32
- package/node_modules/pi-footer/src/ui/theme.ts +0 -48
- package/node_modules/pi-footer/src/ui/title-bar.ts +0 -100
- package/node_modules/pi-footer/src/ui/widget-actions.ts +0 -76
- package/node_modules/pi-footer/src/ui.ts +0 -66
- package/node_modules/pi-footer/src/widgets/context.ts +0 -28
- package/node_modules/pi-footer/src/widgets/core/active-tools.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/context-window.ts +0 -24
- package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +0 -19
- package/node_modules/pi-footer/src/widgets/core/cwd.ts +0 -108
- package/node_modules/pi-footer/src/widgets/core/event.ts +0 -34
- package/node_modules/pi-footer/src/widgets/core/external-status.ts +0 -102
- package/node_modules/pi-footer/src/widgets/core/model-provider.ts +0 -20
- package/node_modules/pi-footer/src/widgets/core/model.ts +0 -35
- package/node_modules/pi-footer/src/widgets/core/provider.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/session-name.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/branch.ts +0 -81
- package/node_modules/pi-footer/src/widgets/git/clean.ts +0 -19
- package/node_modules/pi-footer/src/widgets/git/deletions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/diff.ts +0 -38
- package/node_modules/pi-footer/src/widgets/git/insertions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/remote.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/root.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/sha.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/staged.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/status.ts +0 -19
- package/node_modules/pi-footer/src/widgets/git/unstaged.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/untracked.ts +0 -17
- package/node_modules/pi-footer/src/widgets/instance.ts +0 -118
- package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +0 -18
- package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +0 -32
- package/node_modules/pi-footer/src/widgets/layout/separator.ts +0 -57
- package/node_modules/pi-footer/src/widgets/layout/spacer.ts +0 -31
- package/node_modules/pi-footer/src/widgets/options.ts +0 -122
- package/node_modules/pi-footer/src/widgets/project/runtime.ts +0 -361
- package/node_modules/pi-footer/src/widgets/registry.ts +0 -223
- package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/compactions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/elapsed.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/last-activity.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/messages.ts +0 -19
- package/node_modules/pi-footer/src/widgets/session/session-id.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/session-start.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/tool-results.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/total-messages.ts +0 -19
- package/node_modules/pi-footer/src/widgets/session/total-time.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/user-messages.ts +0 -17
- package/node_modules/pi-footer/src/widgets/store.ts +0 -26
- package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +0 -55
- package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +0 -108
- package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +0 -26
- package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +0 -29
- package/node_modules/pi-footer/src/widgets/tokens/context.ts +0 -29
- package/node_modules/pi-footer/src/widgets/tokens/cost.ts +0 -51
- package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +0 -20
- package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/types.ts +0 -181
- package/node_modules/pi-footer/src/widgets/utils/colors.ts +0 -11
- package/node_modules/pi-footer/src/widgets/utils/context.ts +0 -112
- package/node_modules/pi-footer/src/widgets/utils/session.ts +0 -19
- package/node_modules/pi-footer/src/widgets/utils/token-format.ts +0 -78
- /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
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Explicit routing preferences; never foreground selections or authorization for a cheaper tier. */
|
|
2
|
+
import { mutateConfigFile } from "./config-file-transaction.ts";
|
|
3
|
+
|
|
4
|
+
export function validModelPinScope(scope: string): boolean {
|
|
5
|
+
return /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(scope);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function normalizeModelPins(value: unknown): Record<string, string> {
|
|
9
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
10
|
+
return Object.fromEntries(Object.entries(value).filter(([scope, model]) =>
|
|
11
|
+
validModelPinScope(scope) && typeof model === "string" && model.trim().length > 0 &&
|
|
12
|
+
!/[\s\x00-\x1f]/.test(model.trim()),
|
|
13
|
+
).map(([scope, model]) => [scope, (model as string).trim()]));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Change only one group's pin through the shared extension configuration transaction. */
|
|
17
|
+
export function persistModelPin(
|
|
18
|
+
path: string,
|
|
19
|
+
scope: string,
|
|
20
|
+
model: string | undefined,
|
|
21
|
+
canCommit: () => boolean = () => true,
|
|
22
|
+
): Record<string, string> {
|
|
23
|
+
if (!validModelPinScope(scope) || (model !== undefined && normalizeModelPins({ [scope]: model })[scope] !== model)) {
|
|
24
|
+
throw new Error("invalid model pin");
|
|
25
|
+
}
|
|
26
|
+
const next = mutateConfigFile(path, disk => {
|
|
27
|
+
if (disk.pinnedModels !== undefined && (!disk.pinnedModels || typeof disk.pinnedModels !== "object" || Array.isArray(disk.pinnedModels))) {
|
|
28
|
+
throw new Error("pinnedModels must be an object");
|
|
29
|
+
}
|
|
30
|
+
const pins = { ...disk.pinnedModels };
|
|
31
|
+
if (model === undefined) delete pins[scope];
|
|
32
|
+
else pins[scope] = model;
|
|
33
|
+
return { pinnedModels: pins };
|
|
34
|
+
}, canCommit);
|
|
35
|
+
return normalizeModelPins(next.pinnedModels);
|
|
36
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-multi-account",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.24.0",
|
|
4
4
|
"description": "Automatic multi-account failover & rotation for Pi Agent across Anthropic (Claude), OpenAI/ChatGPT Codex, Kimi For Coding, Cursor, Qwen/Alibaba, and Ollama. Auto-discovers authenticated accounts, grows the rotation on login, and drops accounts on logout, expiry, or quota/rate-limit errors",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,7 +45,9 @@
|
|
|
45
45
|
"provider-payload-stream.ts",
|
|
46
46
|
"auth-file-transaction.ts",
|
|
47
47
|
"state-file-transaction.ts",
|
|
48
|
+
"config-file-transaction.ts",
|
|
48
49
|
"model-catalog.ts",
|
|
50
|
+
"model-pins.ts",
|
|
49
51
|
"usage.ts",
|
|
50
52
|
"cursor-bridge.ts",
|
|
51
53
|
"cursor-model-name.ts",
|
|
@@ -78,13 +80,13 @@
|
|
|
78
80
|
"access": "public"
|
|
79
81
|
},
|
|
80
82
|
"peerDependencies": {
|
|
81
|
-
"@earendil-works/pi-
|
|
82
|
-
"@earendil-works/pi-
|
|
83
|
+
"@earendil-works/pi-ai": "*",
|
|
84
|
+
"@earendil-works/pi-coding-agent": "*"
|
|
83
85
|
},
|
|
84
86
|
"devDependencies": {
|
|
85
|
-
"@earendil-works/pi-
|
|
86
|
-
"@earendil-works/pi-
|
|
87
|
-
"@types/node": "^22.20.
|
|
87
|
+
"@earendil-works/pi-ai": "0.87.1",
|
|
88
|
+
"@earendil-works/pi-coding-agent": "0.87.1",
|
|
89
|
+
"@types/node": "^22.20.4",
|
|
88
90
|
"typescript": "^6.0.3"
|
|
89
91
|
},
|
|
90
92
|
"pi": {
|
|
@@ -93,7 +95,7 @@
|
|
|
93
95
|
]
|
|
94
96
|
},
|
|
95
97
|
"dependencies": {
|
|
96
|
-
"@bufbuild/protobuf": "^2.
|
|
98
|
+
"@bufbuild/protobuf": "^2.15.0",
|
|
97
99
|
"proper-lockfile": "4.1.2"
|
|
98
100
|
},
|
|
99
101
|
"overrides": {
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
} from "./cursor-bridge.ts";
|
|
29
29
|
import {
|
|
30
30
|
needsChildFacingApiKey,
|
|
31
|
+
isPlaceholderFor,
|
|
31
32
|
placeholderKeyFor,
|
|
32
33
|
proxyFamilyFor,
|
|
33
34
|
type ProxyFamily,
|
|
@@ -67,7 +68,7 @@ export function isChildFacingPlaceholder(
|
|
|
67
68
|
entry: AuthBlob | undefined,
|
|
68
69
|
family: ProxyFamily,
|
|
69
70
|
): boolean {
|
|
70
|
-
return entry?.type === "api_key" && entry.key
|
|
71
|
+
return entry?.type === "api_key" && isPlaceholderFor(family, entry.key);
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
export function isChildFacingPlaceholderForSlot(
|
|
@@ -75,7 +76,9 @@ export function isChildFacingPlaceholderForSlot(
|
|
|
75
76
|
slotId: string,
|
|
76
77
|
): boolean {
|
|
77
78
|
const key = childFacingPlaceholderKey(slotId);
|
|
78
|
-
|
|
79
|
+
const family = proxyFamilyFor(slotId);
|
|
80
|
+
return !!key && entry?.type === "api_key" &&
|
|
81
|
+
(family ? isPlaceholderFor(family, entry.key) : entry.key === key);
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
/** What the parent should use for refresh/upstream: sidecar OAuth wins over a child-facing placeholder. */
|
|
@@ -101,6 +104,13 @@ export function applyShadowPlan(
|
|
|
101
104
|
return { auth: { ...auth }, sidecar: { ...sidecar }, changed: false };
|
|
102
105
|
}
|
|
103
106
|
const entry = auth[slotId];
|
|
107
|
+
const hidden = sidecar[slotId];
|
|
108
|
+
if (isChildFacingPlaceholderForSlot(entry, slotId) && entry?.key !== facing.key &&
|
|
109
|
+
hidden?.type === "oauth" && typeof hidden.access === "string" && hidden.access.length > 0) {
|
|
110
|
+
// Upgrade only a publication backed by the parent's actual OAuth blob. Never rewrite a
|
|
111
|
+
// user's real API key or replace the hidden credential with a placeholder during migration.
|
|
112
|
+
return { auth: { ...auth, [slotId]: facing }, sidecar: { ...sidecar }, changed: true };
|
|
113
|
+
}
|
|
104
114
|
if (entry?.type === "oauth" && typeof entry.access === "string" && entry.access.length > 0) {
|
|
105
115
|
return {
|
|
106
116
|
auth: { ...auth, [slotId]: facing },
|
|
@@ -36,6 +36,14 @@
|
|
|
36
36
|
*/
|
|
37
37
|
export const PROXY_PLACEHOLDER_KEY = "pi-multi-account-proxy";
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Non-secret Anthropic placeholder with the shape Pi's native transport recognizes as OAuth.
|
|
41
|
+
* Request identity, tool-name conversion and response mapping all depend on this marker; adding
|
|
42
|
+
* a Bearer header at the proxy is too late. The proxy replaces it with the real token on egress.
|
|
43
|
+
* The legacy generic placeholder remains recognizable for restoration during an upgrade.
|
|
44
|
+
*/
|
|
45
|
+
export const ANTHROPIC_PROXY_PLACEHOLDER_KEY = "sk-ant-oat01-pi-multi-account-proxy";
|
|
46
|
+
|
|
39
47
|
/**
|
|
40
48
|
* The `apiKey` published for a **Codex** slot.
|
|
41
49
|
*
|
|
@@ -60,9 +68,10 @@ export const PROXY_PLACEHOLDER_JWT = [
|
|
|
60
68
|
"",
|
|
61
69
|
].join(".");
|
|
62
70
|
|
|
63
|
-
/**
|
|
71
|
+
/** Current or legacy published placeholder — none is a real credential. */
|
|
64
72
|
export function isPublishedPlaceholder(value: string | undefined): boolean {
|
|
65
|
-
return value === PROXY_PLACEHOLDER_KEY || value === PROXY_PLACEHOLDER_JWT
|
|
73
|
+
return value === PROXY_PLACEHOLDER_KEY || value === PROXY_PLACEHOLDER_JWT ||
|
|
74
|
+
value === ANTHROPIC_PROXY_PLACEHOLDER_KEY;
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
export type ProxyFamily = "anthropic" | "codex";
|
|
@@ -273,7 +282,14 @@ export function shapeUpstreamRequest(args: {
|
|
|
273
282
|
}
|
|
274
283
|
}
|
|
275
284
|
|
|
276
|
-
|
|
285
|
+
// Image tools append these endpoints directly to the slot base; chat already sends /codex.
|
|
286
|
+
// Leave all other paths and families intact, including usage requests.
|
|
287
|
+
const upstreamRest =
|
|
288
|
+
route.family === "codex" && /^\/images\/(?:generations|edits)(?:\?.*)?$/.test(rest)
|
|
289
|
+
? `/codex${rest}`
|
|
290
|
+
: rest;
|
|
291
|
+
|
|
292
|
+
return { ok: true, url: `${base}${upstreamRest}`, headers };
|
|
277
293
|
}
|
|
278
294
|
|
|
279
295
|
/** Keep any beta flags the caller asked for, and add ours once. */
|
|
@@ -289,7 +305,13 @@ function mergeBeta(existing: string | undefined, required: string): string {
|
|
|
289
305
|
|
|
290
306
|
/** The `apiKey` to publish for a slot of this family. */
|
|
291
307
|
export function placeholderKeyFor(family: ProxyFamily): string {
|
|
292
|
-
return family === "codex" ? PROXY_PLACEHOLDER_JWT :
|
|
308
|
+
return family === "codex" ? PROXY_PLACEHOLDER_JWT : ANTHROPIC_PROXY_PLACEHOLDER_KEY;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** Recognize the previous Anthropic publication without mistaking real tokens for placeholders. */
|
|
312
|
+
export function isPlaceholderFor(family: ProxyFamily, value: unknown): boolean {
|
|
313
|
+
return value === placeholderKeyFor(family) ||
|
|
314
|
+
(family === "anthropic" && value === PROXY_PLACEHOLDER_KEY);
|
|
293
315
|
}
|
|
294
316
|
|
|
295
317
|
/** The `models.json` route to publish for a slot served by this proxy. */
|
|
@@ -305,7 +327,7 @@ export function isOwnLoopbackPublication(
|
|
|
305
327
|
if (!existing || typeof existing !== "object") return false;
|
|
306
328
|
const rec = existing as Record<string, unknown>;
|
|
307
329
|
return (
|
|
308
|
-
rec.apiKey
|
|
330
|
+
isPlaceholderFor(family, rec.apiKey) &&
|
|
309
331
|
typeof rec.baseUrl === "string" &&
|
|
310
332
|
rec.baseUrl.startsWith("http://127.0.0.1:")
|
|
311
333
|
);
|
|
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.35.0] - 2026-09-30
|
|
8
|
+
|
|
9
|
+
### Highlights
|
|
10
|
+
|
|
11
|
+
- Tell the summarizer what matters to you, such as keeping prices, limits, and versions exactly as written.
|
|
12
|
+
- Brave search holds up on low-rate plans instead of failing when several searches run at once.
|
|
13
|
+
- OpenAI web search works when you signed in to Pi with your ChatGPT account.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Add your own instructions to search summaries with `summaryInstructions` in `web-search.json`, for example "keep prices, limits, and versions verbatim." They apply to summaries from the curator and the `auto-summary` workflow, and the built-in safety rules still apply. Thanks to [@rcharrisg](https://github.com/rcharrisg) for [PR #489](https://github.com/nicobailon/pi-web-access/pull/489).
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Brave searches now wait their turn according to the rate limits Brave reports, so running several searches at once no longer overruns low-rate plans. If Brave says to slow down and the wait is short, the search retries once. If the wait is long, it fails right away and later searches respect the same wait. The 30-second search timeout includes this waiting. Thanks to [@saschaSpoonbill](https://github.com/saschaSpoonbill) for [PR #487](https://github.com/nicobailon/pi-web-access/pull/487).
|
|
22
|
+
- OpenAI web search now works when you signed in to Pi with "Sign in with ChatGPT" on the `openai` provider. Searches go to OpenAI's official API, and `auto` search tries OpenAI first for this login, as it already did for `openai-codex`. With an `openai` API key, `auto` still tries Exa first. Thanks to [@kasumikira](https://github.com/kasumikira) for [issue #488](https://github.com/nicobailon/pi-web-access/issues/488).
|
|
23
|
+
|
|
7
24
|
## [0.34.0] - 2026-09-30
|
|
8
25
|
|
|
9
26
|
### Highlights
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
**Video Understanding** — Point it at a YouTube video or local screen recording and ask questions about what's on screen. Full transcripts, visual descriptions, and frame extraction at exact timestamps.
|
|
20
20
|
|
|
21
|
-
**Smart Fallbacks** — Every capability has a fallback chain. Search tries configured SearXNG first for local/private search. When the active Pi model
|
|
21
|
+
**Smart Fallbacks** — Every capability has a fallback chain. Search tries configured SearXNG first for local/private search. When the active Pi model uses a ChatGPT subscription (`openai-codex`, or `openai` with Sign in with ChatGPT), it then tries subscription-backed OpenAI search. Otherwise it tries Exa before OpenAI, then Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, Perplexity, Gemini API, and Gemini Web when browser cookies are enabled. YouTube tries Gemini Web when enabled, then API, then Perplexity. Blocked pages try configured self-hosted Firecrawl or Crawl4AI first. Third-party hosted page fetchers require explicit `fetchRouting.allowRemoteHostedProviders` opt-in for remote HTTP(S) targets.
|
|
22
22
|
|
|
23
23
|
**GitHub Cloning** — GitHub URLs are cloned locally instead of scraped. The agent gets real file contents and a local path to explore, not rendered HTML.
|
|
24
24
|
|
|
@@ -47,7 +47,7 @@ Works immediately with no API keys — Exa MCP provides zero-config search. If P
|
|
|
47
47
|
}
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
In `auto` mode (default), `web_search` tries a configured SearXNG endpoint first for local/private search. When the active Pi model
|
|
50
|
+
In `auto` mode (default), `web_search` tries a configured SearXNG endpoint first for local/private search. When the active Pi model uses a ChatGPT subscription (`openai-codex`, or `openai` with Sign in with ChatGPT), it then tries subscription-backed OpenAI search. Otherwise it tries Exa (direct API if keyed, MCP if not) before OpenAI, then Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Perplexity, Gemini API, and Gemini Web when browser-cookie access is enabled. Exa handles search; curator summary drafts are generated separately by the configured Pi summary model, defaulting to Claude Haiku, Codex Luna, Codex Terra, Gemini 3.6 Flash, GPT-5 mini, then DeepSeek V4 Flash when available. Slow summary drafts fall back to a deterministic result summary after a bounded deadline.
|
|
51
51
|
|
|
52
52
|
For a third-party Responses-compatible gateway, set `openaiResponsesUrl` to its full Responses endpoint, or opt into reusing the selected Pi provider's base URL as described below. Without either opt-in, Pi credentials with a custom `baseUrl` are refused before sending a request, and availability checks mark OpenAI unavailable without blocking other providers. The auth-resolved base URL takes precedence over the model's; gateway Responses/`web_search` support is not inferred. An explicit endpoint overrides this guard, including an explicit official endpoint. Existing Codex subscription endpoint selection is preserved when provider URL reuse is not active.
|
|
53
53
|
|
|
@@ -190,7 +190,7 @@ web_search({ queries: ["query 1", "query 2"], workflow: "auto-summary" })
|
|
|
190
190
|
| `includeContent` | Fetch full page content from sources in background |
|
|
191
191
|
| `workflow` | `none` (skip curator; fresh-install default), `summary-review` (open curator and auto-generate a summary draft), or `auto-summary` (generate a summary without opening the curator) |
|
|
192
192
|
|
|
193
|
-
Batch searches run up to three queries concurrently. Provider routing and fallback within each query remain sequential.
|
|
193
|
+
Batch searches run up to three queries concurrently. Provider routing and fallback within each query remain sequential. Brave requests share an in-process queue and honor the rate-limit buckets returned in `X-RateLimit-*`; exhausted short windows delay the next request with a small safety margin, while long quota resets fail fast and keep the advertised cooldown active for later queued calls. HTTP 429 responses are retried once after the advertised `Retry-After` or rate-limit reset. Brave's 30-second search deadline covers queueing, cooldown waits, and both request attempts. The queue coordinates one Pi process; separate Pi processes still rely on Brave's 429 response.
|
|
194
194
|
|
|
195
195
|
### fetch_content
|
|
196
196
|
|
|
@@ -517,6 +517,7 @@ Config defaults to `~/.pi/agent/web-search.json` when neither `PI_CODING_AGENT_D
|
|
|
517
517
|
"searchModel": "gemini-3.6-flash",
|
|
518
518
|
"summaryModel": "anthropic/claude-haiku-4-5",
|
|
519
519
|
"summaryGenerationDeadlineMs": 30000,
|
|
520
|
+
"summaryInstructions": "- Preserve concrete figures verbatim: prices, limits, versions, dates.\n- Name the product version each claim applies to.",
|
|
520
521
|
"maxInlineContentChars": 30000,
|
|
521
522
|
"workflow": "summary-review",
|
|
522
523
|
"curatorTimeoutSeconds": 20,
|
|
@@ -571,6 +572,8 @@ Config defaults to `~/.pi/agent/web-search.json` when neither `PI_CODING_AGENT_D
|
|
|
571
572
|
|
|
572
573
|
`summaryModel` accepts an optional thinking-level suffix, such as `anthropic/claude-haiku-4-5:low`. Supported suffixes are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`.
|
|
573
574
|
|
|
575
|
+
`summaryInstructions` appends custom text to the Requirements section of the summary prompt used by the curator UI and `auto-summary` mode (e.g. `"- Preserve concrete figures verbatim: prices, limits, versions, dates.\n- Name the product version each claim applies to."`). The text is passed verbatim to the summary model, so use one `- ` bullet per requirement. It is additive: the built-in guardrails (no invented sources, explicit weak/conflicting-evidence notes, the Sources section) always stay in place, and curator regeneration feedback is still appended afterwards. Missing, non-string, or blank values leave the default prompt unchanged.
|
|
576
|
+
|
|
574
577
|
All provider API-key fields (`openaiApiKey`, `braveApiKey`, `parallelApiKey`, `tinyfishApiKey`, `search1apiApiKey`, `searchinfinityApiKey`, `queritApiKey`, `tavilyApiKey`, `youApiKey`, `jinaApiKey`, `serpdiveApiKey`, `kagiApiKey`, `bochaApiKey`, `ollamaApiKey`, `valyuApiKey`, `serpbaseApiKey`, `serpapiApiKey`, `serperApiKey`, `serplyApiKey`, `baizhiApiKey`, `zaiApiKey`, `anysearchApiKey`, `xcrawlApiKey`, `xaiApiKey`, `mistralApiKey`, `brightdataApiKey`, `firecrawlApiKey`, `crawl4aiApiToken`, `exaApiKey`, `perplexityApiKey`, `geminiApiKey`, `datalabApiKey`, and `cloudflareApiKey`) accept explicit credential sources. Use `$NAME` or `${NAME}` to read one named environment variable, or prefix a trusted local shell command with `!` to resolve one value at provider request time. Escape `$$` as a literal leading `$` and `$!` as a literal leading `!`:
|
|
575
578
|
|
|
576
579
|
```json
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const DEFAULT_SAFETY_MARGIN_MS = 100;
|
|
2
|
+
const DEFAULT_MAX_WAIT_MS = 30_000;
|
|
3
|
+
|
|
4
|
+
export interface BraveRateLimitBucket {
|
|
5
|
+
limit: number;
|
|
6
|
+
windowSeconds: number | null;
|
|
7
|
+
remaining: number;
|
|
8
|
+
resetSeconds: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface BraveRateLimitCoordinatorOptions {
|
|
12
|
+
now?: () => number;
|
|
13
|
+
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
14
|
+
safetyMarginMs?: number;
|
|
15
|
+
maxWaitMs?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function parseNumberList(value: string | null): number[] {
|
|
19
|
+
if (!value) return [];
|
|
20
|
+
return value.split(",").map(part => Number(part.trim()));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parsePolicyWindows(value: string | null): Array<number | null> {
|
|
24
|
+
if (!value) return [];
|
|
25
|
+
return value.split(",").map(part => {
|
|
26
|
+
const match = /(?:^|;)\s*w=(\d+(?:\.\d+)?)\s*(?:;|$)/i.exec(part.trim());
|
|
27
|
+
if (!match) return null;
|
|
28
|
+
const seconds = Number(match[1]);
|
|
29
|
+
return Number.isFinite(seconds) && seconds >= 0 ? seconds : null;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Parse Brave's parallel rate-limit bucket headers by list position. */
|
|
34
|
+
export function parseBraveRateLimitHeaders(headers: Headers): BraveRateLimitBucket[] {
|
|
35
|
+
const limits = parseNumberList(headers.get("x-ratelimit-limit"));
|
|
36
|
+
const remaining = parseNumberList(headers.get("x-ratelimit-remaining"));
|
|
37
|
+
const resets = parseNumberList(headers.get("x-ratelimit-reset"));
|
|
38
|
+
const windows = parsePolicyWindows(headers.get("x-ratelimit-policy"));
|
|
39
|
+
const count = Math.max(limits.length, remaining.length, resets.length, windows.length);
|
|
40
|
+
const buckets: BraveRateLimitBucket[] = [];
|
|
41
|
+
|
|
42
|
+
for (let index = 0; index < count; index++) {
|
|
43
|
+
const limit = limits[index];
|
|
44
|
+
const left = remaining[index];
|
|
45
|
+
const resetSeconds = resets[index];
|
|
46
|
+
if (![limit, left, resetSeconds].every(Number.isFinite) || limit < 0 || left < 0 || resetSeconds < 0) continue;
|
|
47
|
+
buckets.push({
|
|
48
|
+
limit,
|
|
49
|
+
windowSeconds: windows[index] ?? null,
|
|
50
|
+
remaining: left,
|
|
51
|
+
resetSeconds,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return buckets;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function parseRetryAfterMs(headers: Headers, now = Date.now()): number | null {
|
|
58
|
+
const value = headers.get("retry-after")?.trim();
|
|
59
|
+
if (!value) return null;
|
|
60
|
+
const seconds = Number(value);
|
|
61
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1000;
|
|
62
|
+
const date = Date.parse(value);
|
|
63
|
+
if (!Number.isFinite(date)) return null;
|
|
64
|
+
return Math.max(0, date - now);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function defaultSleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
68
|
+
if (signal?.aborted) return Promise.reject(new Error("Aborted"));
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
const cleanup = () => signal?.removeEventListener("abort", onAbort);
|
|
71
|
+
const timer = setTimeout(() => {
|
|
72
|
+
cleanup();
|
|
73
|
+
resolve();
|
|
74
|
+
}, ms);
|
|
75
|
+
const onAbort = () => {
|
|
76
|
+
clearTimeout(timer);
|
|
77
|
+
cleanup();
|
|
78
|
+
reject(new Error("Aborted"));
|
|
79
|
+
};
|
|
80
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function formatWait(ms: number): string {
|
|
85
|
+
const seconds = Math.ceil(ms / 1000);
|
|
86
|
+
if (seconds < 60) return `${seconds}s`;
|
|
87
|
+
const minutes = Math.ceil(seconds / 60);
|
|
88
|
+
if (minutes < 60) return `${minutes}m`;
|
|
89
|
+
const hours = Math.ceil(minutes / 60);
|
|
90
|
+
return `${hours}h`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export class BraveRateLimitCoordinator {
|
|
94
|
+
private queue: Promise<void> = Promise.resolve();
|
|
95
|
+
private blockedUntil = 0;
|
|
96
|
+
private readonly now: () => number;
|
|
97
|
+
private readonly sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
98
|
+
private readonly safetyMarginMs: number;
|
|
99
|
+
private readonly maxWaitMs: number;
|
|
100
|
+
|
|
101
|
+
constructor(options: BraveRateLimitCoordinatorOptions = {}) {
|
|
102
|
+
this.now = options.now ?? Date.now;
|
|
103
|
+
this.sleep = options.sleep ?? defaultSleep;
|
|
104
|
+
this.safetyMarginMs = options.safetyMarginMs ?? DEFAULT_SAFETY_MARGIN_MS;
|
|
105
|
+
this.maxWaitMs = options.maxWaitMs ?? DEFAULT_MAX_WAIT_MS;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
run<T>(operation: () => Promise<T>, signal?: AbortSignal): Promise<T> {
|
|
109
|
+
const scheduled = this.queue.then(async () => {
|
|
110
|
+
await this.waitForAvailability(signal);
|
|
111
|
+
if (signal?.aborted) throw new Error("Aborted");
|
|
112
|
+
return operation();
|
|
113
|
+
});
|
|
114
|
+
this.queue = scheduled.then(() => undefined, () => undefined);
|
|
115
|
+
if (!signal) return scheduled;
|
|
116
|
+
if (signal.aborted) return Promise.reject(new Error("Aborted"));
|
|
117
|
+
return new Promise<T>((resolve, reject) => {
|
|
118
|
+
const onAbort = () => reject(new Error("Aborted"));
|
|
119
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
120
|
+
scheduled.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
observe(headers: Headers): void {
|
|
125
|
+
const exhausted = parseBraveRateLimitHeaders(headers).filter(bucket => bucket.remaining === 0);
|
|
126
|
+
if (exhausted.length === 0) return;
|
|
127
|
+
const resetMs = Math.max(...exhausted.map(bucket => bucket.resetSeconds * 1000));
|
|
128
|
+
this.blockFor(resetMs);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
retryDelay(headers: Headers): number | null {
|
|
132
|
+
const advertised = parseRetryAfterMs(headers, this.now());
|
|
133
|
+
const exhausted = parseBraveRateLimitHeaders(headers).filter(bucket => bucket.remaining === 0);
|
|
134
|
+
const bucketDelay = exhausted.length > 0
|
|
135
|
+
? Math.max(...exhausted.map(bucket => bucket.resetSeconds * 1000))
|
|
136
|
+
: null;
|
|
137
|
+
if (advertised === null && bucketDelay === null) return null;
|
|
138
|
+
return Math.max(advertised ?? 0, bucketDelay ?? 0) + this.safetyMarginMs;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
recordRetryDelay(delayMs: number): void {
|
|
142
|
+
this.blockFor(delayMs, false);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async waitForRecordedCooldown(signal?: AbortSignal): Promise<void> {
|
|
146
|
+
await this.waitForAvailability(signal);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async waitForRetry(delayMs: number, signal?: AbortSignal): Promise<void> {
|
|
150
|
+
this.recordRetryDelay(delayMs);
|
|
151
|
+
await this.waitForRecordedCooldown(signal);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private blockFor(delayMs: number, addMargin = true): void {
|
|
155
|
+
const boundedDelay = Math.max(0, delayMs) + (addMargin ? this.safetyMarginMs : 0);
|
|
156
|
+
this.blockedUntil = Math.max(this.blockedUntil, this.now() + boundedDelay);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private async waitForAvailability(signal?: AbortSignal): Promise<void> {
|
|
160
|
+
const waitMs = Math.max(0, this.blockedUntil - this.now());
|
|
161
|
+
if (waitMs === 0) return;
|
|
162
|
+
this.assertWaitIsBounded(waitMs);
|
|
163
|
+
await this.sleep(waitMs, signal);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private assertWaitIsBounded(waitMs: number): void {
|
|
167
|
+
if (waitMs <= this.maxWaitMs) return;
|
|
168
|
+
throw new Error(`Brave Search quota exhausted; estimated reset in ${formatWait(waitMs)}. Refusing to wait longer than ${formatWait(this.maxWaitMs)} inside one search call.`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { activityMonitor } from "./activity.ts";
|
|
3
|
+
import { BraveRateLimitCoordinator } from "./brave-rate-limit.ts";
|
|
3
4
|
import { normalizeDomain } from "./domain-filter-normalization.ts";
|
|
4
5
|
import { normalizeSearchResultCount } from "./search-result-count-normalization.ts";
|
|
5
6
|
import type { SearchOptions, SearchResult, SearchResponse } from "./perplexity.ts";
|
|
@@ -9,6 +10,7 @@ import { fetchWithCredentialRedirects, getWebSearchConfigPath, resolveApiBaseUrl
|
|
|
9
10
|
const BRAVE_API_BASE_URL = "https://api.search.brave.com/res/v1";
|
|
10
11
|
const CONFIG_PATH = getWebSearchConfigPath();
|
|
11
12
|
const SEARCH_TIMEOUT_MS = 30_000;
|
|
13
|
+
const braveRateLimit = new BraveRateLimitCoordinator();
|
|
12
14
|
|
|
13
15
|
interface WebSearchConfig {
|
|
14
16
|
braveApiKey?: unknown;
|
|
@@ -154,17 +156,32 @@ export async function searchWithBrave(
|
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
try {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
159
|
+
const searchDeadline = AbortSignal.timeout(SEARCH_TIMEOUT_MS);
|
|
160
|
+
const searchSignal = options.signal
|
|
161
|
+
? AbortSignal.any([searchDeadline, options.signal])
|
|
162
|
+
: searchDeadline;
|
|
163
|
+
const response = await braveRateLimit.run(async () => {
|
|
164
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
165
|
+
const current = await fetchWithCredentialRedirects(`${apiUrl}?${params.toString()}`, {
|
|
166
|
+
method: "GET",
|
|
167
|
+
headers: {
|
|
168
|
+
"X-Subscription-Token": apiKey,
|
|
169
|
+
"Accept": "application/json",
|
|
170
|
+
"Accept-Encoding": "gzip",
|
|
171
|
+
},
|
|
172
|
+
signal: searchSignal,
|
|
173
|
+
}, ["X-Subscription-Token"]);
|
|
174
|
+
|
|
175
|
+
braveRateLimit.observe(current.headers);
|
|
176
|
+
if (current.status !== 429 || attempt === 1) return current;
|
|
177
|
+
const retryDelay = braveRateLimit.retryDelay(current.headers);
|
|
178
|
+
if (retryDelay === null) return current;
|
|
179
|
+
braveRateLimit.recordRetryDelay(retryDelay);
|
|
180
|
+
await current.body?.cancel().catch(() => undefined);
|
|
181
|
+
await braveRateLimit.waitForRecordedCooldown(searchSignal);
|
|
182
|
+
}
|
|
183
|
+
throw new Error("Brave Search retry loop exited unexpectedly");
|
|
184
|
+
}, searchSignal);
|
|
168
185
|
|
|
169
186
|
if (!response.ok) {
|
|
170
187
|
activityMonitor.logError(activityId, `HTTP ${response.status}`);
|