@phuetz/code-buddy 1.1.0 → 1.3.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 +211 -163
- package/dist/agent/autonomous/agentic-coding-contract.d.ts +12 -12
- package/dist/agent/base-agent.d.ts +4 -0
- package/dist/agent/base-agent.js +6 -0
- package/dist/agent/codebuddy-agent.d.ts +1 -1
- package/dist/agent/codebuddy-agent.js +69 -14
- package/dist/agent/execution/agent-executor.js +122 -12
- package/dist/agent/execution/tool-dependency-graph.js +7 -0
- package/dist/agent/execution/tool-hooks.d.ts +6 -0
- package/dist/agent/execution/tool-hooks.js +13 -5
- package/dist/agent/execution/tool-selection-strategy.d.ts +11 -2
- package/dist/agent/execution/tool-selection-strategy.js +67 -10
- package/dist/agent/extended-thinking.d.ts +8 -0
- package/dist/agent/extended-thinking.js +27 -0
- package/dist/agent/facades/infrastructure-facade.d.ts +9 -2
- package/dist/agent/facades/infrastructure-facade.js +15 -6
- package/dist/agent/hermes-browser-backends.js +151 -11
- package/dist/agent/hermes-claw-migrate.d.ts +59 -0
- package/dist/agent/hermes-claw-migrate.js +427 -26
- package/dist/agent/hermes-parity-manifest.js +78 -71
- package/dist/agent/hermes-runtime-lifecycle.d.ts +67 -0
- package/dist/agent/hermes-runtime-lifecycle.js +468 -0
- package/dist/agent/middleware/index.d.ts +1 -0
- package/dist/agent/middleware/index.js +1 -0
- package/dist/agent/middleware/visual-validation-middleware.d.ts +17 -0
- package/dist/agent/middleware/visual-validation-middleware.js +67 -0
- package/dist/agent/model-benchmark.d.ts +77 -0
- package/dist/agent/model-benchmark.js +309 -0
- package/dist/agent/model-tier.d.ts +14 -0
- package/dist/agent/model-tier.js +71 -0
- package/dist/agent/multi-agent/agents/orchestrator-agent.js +17 -6
- package/dist/agent/multi-agent/multi-agent-system.js +3 -1
- package/dist/agent/multi-agent/session-fleet-bridge.d.ts +2 -0
- package/dist/agent/multi-agent/session-fleet-bridge.js +9 -1
- package/dist/agent/multi-agent/session-tools.js +22 -5
- package/dist/agent/reasoning/mcts.js +20 -6
- package/dist/agent/self-improvement/authored-artifact-gate.d.ts +18 -0
- package/dist/agent/self-improvement/authored-artifact-gate.js +42 -0
- package/dist/agent/self-improvement/authored-tool-runtime.d.ts +27 -0
- package/dist/agent/self-improvement/authored-tool-runtime.js +57 -0
- package/dist/agent/self-improvement/authored-tool-store.d.ts +24 -0
- package/dist/agent/self-improvement/authored-tool-store.js +57 -0
- package/dist/agent/self-improvement/llm-tool-proposer.d.ts +41 -0
- package/dist/agent/self-improvement/llm-tool-proposer.js +136 -0
- package/dist/agent/self-improvement/sandbox-scorer.d.ts +17 -0
- package/dist/agent/self-improvement/sandbox-scorer.js +43 -0
- package/dist/agent/self-improvement/self-knowledge.d.ts +8 -0
- package/dist/agent/self-improvement/self-knowledge.js +24 -0
- package/dist/agent/self-improvement/skill-benchmark.d.ts +9 -0
- package/dist/agent/self-improvement/skill-benchmark.js +22 -0
- package/dist/agent/self-improvement/skill-consolidator.d.ts +71 -0
- package/dist/agent/self-improvement/skill-consolidator.js +137 -0
- package/dist/agent/self-improvement/skill-engine.d.ts +42 -0
- package/dist/agent/self-improvement/skill-engine.js +87 -0
- package/dist/agent/self-improvement/skill-gate.d.ts +19 -0
- package/dist/agent/self-improvement/skill-gate.js +62 -0
- package/dist/agent/self-improvement/skill-mutator.d.ts +74 -0
- package/dist/agent/self-improvement/skill-mutator.js +223 -0
- package/dist/agent/self-improvement/skill-proposer.d.ts +40 -0
- package/dist/agent/self-improvement/skill-proposer.js +82 -0
- package/dist/agent/self-improvement/skill-types.d.ts +41 -0
- package/dist/agent/self-improvement/skill-types.js +13 -0
- package/dist/agent/self-improvement/tool-benchmark.d.ts +10 -0
- package/dist/agent/self-improvement/tool-benchmark.js +37 -0
- package/dist/agent/self-improvement/tool-engine.d.ts +54 -0
- package/dist/agent/self-improvement/tool-engine.js +101 -0
- package/dist/agent/self-improvement/tool-gate.d.ts +20 -0
- package/dist/agent/self-improvement/tool-gate.js +78 -0
- package/dist/agent/self-improvement/tool-proposer.d.ts +31 -0
- package/dist/agent/self-improvement/tool-proposer.js +34 -0
- package/dist/agent/self-improvement/tool-skill-mutator.d.ts +40 -0
- package/dist/agent/self-improvement/tool-skill-mutator.js +79 -0
- package/dist/agent/self-improvement/tool-types.d.ts +48 -0
- package/dist/agent/self-improvement/tool-types.js +9 -0
- package/dist/agent/self-improvement/types.d.ts +3 -1
- package/dist/agent/session-end-flush.d.ts +6 -1
- package/dist/agent/session-end-flush.js +20 -4
- package/dist/agent/streaming/streaming-handler.d.ts +7 -0
- package/dist/agent/streaming/streaming-handler.js +8 -1
- package/dist/agent/tool-executor.js +1 -0
- package/dist/agent/tool-handler.d.ts +5 -0
- package/dist/agent/tool-handler.js +133 -4
- package/dist/browser-automation/browser-operator-executor.js +15 -0
- package/dist/browser-automation/browser-use-runner.d.ts +96 -0
- package/dist/browser-automation/browser-use-runner.js +492 -0
- package/dist/browser-automation/camofox-runner.d.ts +107 -0
- package/dist/browser-automation/camofox-runner.js +287 -0
- package/dist/channels/dingtalk/index.js +3 -0
- package/dist/channels/discord/client.d.ts +9 -0
- package/dist/channels/discord/client.js +12 -0
- package/dist/channels/feishu/index.d.ts +167 -1
- package/dist/channels/feishu/index.js +432 -7
- package/dist/channels/gateway-lifecycle.d.ts +132 -0
- package/dist/channels/gateway-lifecycle.js +219 -0
- package/dist/channels/google-chat/index.d.ts +5 -1
- package/dist/channels/google-chat/index.js +5 -1
- package/dist/channels/imessage/index.d.ts +14 -0
- package/dist/channels/imessage/index.js +64 -17
- package/dist/channels/index.d.ts +4 -0
- package/dist/channels/index.js +4 -0
- package/dist/channels/irc/index.d.ts +73 -3
- package/dist/channels/irc/index.js +446 -11
- package/dist/channels/line/index.js +3 -0
- package/dist/channels/mattermost/index.d.ts +51 -4
- package/dist/channels/mattermost/index.js +303 -20
- package/dist/channels/nextcloud-talk/index.d.ts +89 -8
- package/dist/channels/nextcloud-talk/index.js +367 -16
- package/dist/channels/nostr/index.d.ts +121 -1
- package/dist/channels/nostr/index.js +396 -8
- package/dist/channels/ntfy/index.js +3 -0
- package/dist/channels/qq/index.js +3 -0
- package/dist/channels/slash-parity.d.ts +117 -0
- package/dist/channels/slash-parity.js +185 -0
- package/dist/channels/synology-chat/index.js +3 -0
- package/dist/channels/teams/index.d.ts +3 -0
- package/dist/channels/teams/index.js +3 -0
- package/dist/channels/telegram/client.d.ts +9 -0
- package/dist/channels/telegram/client.js +12 -0
- package/dist/channels/twilio-voice/index.js +3 -0
- package/dist/channels/webchat/index.d.ts +7 -1
- package/dist/channels/webchat/index.js +7 -1
- package/dist/channels/wecom/index.js +3 -0
- package/dist/channels/weixin/index.js +3 -0
- package/dist/channels/zalo/index.js +3 -0
- package/dist/cli/config-loader.js +1 -1
- package/dist/codebuddy/client.d.ts +30 -1
- package/dist/codebuddy/client.js +167 -20
- package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +5 -0
- package/dist/codebuddy/providers/provider-chatgpt-responses.js +7 -1
- package/dist/codebuddy/providers/provider-gemini-native.js +1 -1
- package/dist/codebuddy/providers/provider-openai-compat.d.ts +5 -0
- package/dist/codebuddy/providers/provider-openai-compat.js +82 -2
- package/dist/codebuddy/tool-definitions/computer-control-tools.js +32 -2
- package/dist/codebuddy/tool-definitions/core-tools.js +3 -3
- package/dist/codebuddy/tool-definitions/index.d.ts +2 -1
- package/dist/codebuddy/tool-definitions/index.js +2 -1
- package/dist/codebuddy/tool-definitions/multimodal-tools.d.ts +1 -0
- package/dist/codebuddy/tool-definitions/multimodal-tools.js +44 -1
- package/dist/codebuddy/tool-definitions/windows-tools.d.ts +2 -0
- package/dist/codebuddy/tool-definitions/windows-tools.js +34 -0
- package/dist/codebuddy/tools.d.ts +8 -1
- package/dist/codebuddy/tools.js +67 -24
- package/dist/commands/cli/hermes-commands.js +108 -0
- package/dist/commands/cli/improve-command.js +123 -0
- package/dist/commands/cli/native-engine-commands.js +102 -3
- package/dist/commands/cli/utility-commands.js +45 -0
- package/dist/commands/enhanced-command-handler.js +2 -2
- package/dist/commands/goal-cli.d.ts +30 -0
- package/dist/commands/goal-cli.js +210 -27
- package/dist/commands/handlers/goal-handler.d.ts +9 -2
- package/dist/commands/handlers/goal-handler.js +36 -9
- package/dist/commands/handlers/infra-handlers.js +36 -0
- package/dist/commands/handlers/memory-handlers.js +96 -1
- package/dist/commands/handlers/missing-handlers.d.ts +1 -1
- package/dist/commands/handlers/missing-handlers.js +26 -3
- package/dist/commands/headless-slash.d.ts +2 -0
- package/dist/commands/headless-slash.js +12 -0
- package/dist/commands/llm-provider-resolution.d.ts +5 -3
- package/dist/commands/llm-provider-resolution.js +87 -33
- package/dist/commands/ollama.d.ts +25 -0
- package/dist/commands/ollama.js +100 -0
- package/dist/commands/provider.d.ts +5 -0
- package/dist/commands/provider.js +106 -55
- package/dist/commands/skills-cli/index.js +123 -0
- package/dist/commands/slash/builtin-commands.js +1 -1
- package/dist/commands/spec-next.js +2 -1
- package/dist/commands/spec-plan.js +5 -16
- package/dist/commands/tunnel.d.ts +2 -0
- package/dist/commands/tunnel.js +48 -0
- package/dist/companion/percepts.js +11 -1
- package/dist/config/config-resolver.d.ts +2 -1
- package/dist/config/config-resolver.js +54 -41
- package/dist/config/constants.d.ts +28 -0
- package/dist/config/constants.js +7 -0
- package/dist/config/env-schema.js +756 -0
- package/dist/config/model-tools.js +4 -4
- package/dist/config/toml-config.d.ts +16 -0
- package/dist/config/toml-config.js +3 -0
- package/dist/context/bootstrap-loader.js +6 -23
- package/dist/context/context-manager-v2.js +1 -0
- package/dist/context/import-directive-parser.d.ts +4 -0
- package/dist/context/import-directive-parser.js +51 -6
- package/dist/context/instruction-excludes.d.ts +30 -1
- package/dist/context/instruction-excludes.js +71 -1
- package/dist/context/jit-context.d.ts +8 -10
- package/dist/context/jit-context.js +28 -106
- package/dist/context/project-context.d.ts +90 -0
- package/dist/context/project-context.js +295 -0
- package/dist/daemon/autonomous-daemon.d.ts +1 -1
- package/dist/daemon/autonomous-daemon.js +3 -3
- package/dist/daemon/autonomous-loop.d.ts +31 -1
- package/dist/daemon/autonomous-loop.js +91 -4
- package/dist/daemon/colab-goal.js +9 -1
- package/dist/daemon/cron-agent-bridge.d.ts +12 -3
- package/dist/daemon/cron-agent-bridge.js +25 -9
- package/dist/desktop/codebuddy-engine-adapter.d.ts +32 -0
- package/dist/desktop/codebuddy-engine-adapter.js +257 -80
- package/dist/desktop/engine-adapter.d.ts +14 -0
- package/dist/desktop-automation/automation-manager.js +16 -0
- package/dist/desktop-automation/omniparser-runner.d.ts +63 -0
- package/dist/desktop-automation/omniparser-runner.js +115 -0
- package/dist/desktop-automation/smart-snapshot.d.ts +11 -0
- package/dist/desktop-automation/smart-snapshot.js +72 -5
- package/dist/doctor/index.js +40 -23
- package/dist/events/types.d.ts +11 -0
- package/dist/fleet/colab-store.js +14 -2
- package/dist/fleet/peer-session-bridge.js +50 -12
- package/dist/fleet/peer-tool-bridge.js +14 -0
- package/dist/goals/goal-decomposer.d.ts +27 -0
- package/dist/goals/goal-decomposer.js +293 -0
- package/dist/goals/goal-judge-client.d.ts +15 -0
- package/dist/goals/goal-judge-client.js +45 -0
- package/dist/goals/goal-judge.d.ts +2 -0
- package/dist/goals/goal-judge.js +35 -6
- package/dist/goals/goal-loop.d.ts +14 -0
- package/dist/goals/goal-loop.js +47 -2
- package/dist/goals/goal-manager.d.ts +6 -0
- package/dist/goals/goal-manager.js +67 -14
- package/dist/goals/goal-state.d.ts +13 -0
- package/dist/goals/goal-state.js +83 -13
- package/dist/goals/goal-store.d.ts +3 -0
- package/dist/goals/goal-store.js +31 -15
- package/dist/harness/contract.d.ts +28 -28
- package/dist/hooks/user-hooks.js +17 -3
- package/dist/identity/identity-manager.js +3 -2
- package/dist/index.js +481 -26
- package/dist/input/text-to-speech.d.ts +2 -6
- package/dist/input/text-to-speech.js +2 -27
- package/dist/integrations/screenpipe/screenpipe-client.d.ts +10 -0
- package/dist/integrations/screenpipe/screenpipe-client.js +12 -2
- package/dist/integrations/tailscale.d.ts +13 -0
- package/dist/integrations/tailscale.js +87 -34
- package/dist/knowledge/workspace-indexer.js +53 -9
- package/dist/mcp/client.js +1 -0
- package/dist/mcp/mcp-desktop-tools.d.ts +23 -0
- package/dist/mcp/mcp-desktop-tools.js +158 -0
- package/dist/mcp/mcp-resources.js +2 -3
- package/dist/mcp/mcp-server.js +2 -0
- package/dist/memory/enhanced-memory.d.ts +10 -0
- package/dist/memory/enhanced-memory.js +33 -2
- package/dist/memory/index.d.ts +3 -1
- package/dist/memory/index.js +4 -1
- package/dist/memory/memory-auto-proposer.d.ts +23 -0
- package/dist/memory/memory-auto-proposer.js +308 -0
- package/dist/memory/memory-candidate-queue.d.ts +92 -0
- package/dist/memory/memory-candidate-queue.js +261 -0
- package/dist/memory/persistent-memory.d.ts +45 -2
- package/dist/memory/persistent-memory.js +236 -40
- package/dist/ml/bayesian-qualifier.d.ts +1 -1
- package/dist/ml/bayesian-qualifier.js +35 -2
- package/dist/openclaw/gateway-bridge.js +5 -0
- package/dist/plugins/marketplace.d.ts +1 -0
- package/dist/plugins/marketplace.js +7 -0
- package/dist/prompts/execution-discipline.d.ts +14 -0
- package/dist/prompts/execution-discipline.js +29 -0
- package/dist/prompts/variation-injector.js +9 -2
- package/dist/protocols/acp/acp-agentic-runner.d.ts +7 -6
- package/dist/protocols/acp/acp-agentic-runner.js +115 -18
- package/dist/protocols/acp/acp-session-store.d.ts +23 -0
- package/dist/protocols/acp/acp-session-store.js +77 -0
- package/dist/protocols/acp/acp-stdio-server.d.ts +7 -0
- package/dist/protocols/acp/acp-stdio-server.js +71 -16
- package/dist/providers/active-llm-registry.d.ts +37 -0
- package/dist/providers/active-llm-registry.js +186 -0
- package/dist/providers/auxiliary-provider.d.ts +25 -0
- package/dist/providers/auxiliary-provider.js +192 -0
- package/dist/providers/codex-oauth.d.ts +1 -1
- package/dist/providers/codex-oauth.js +27 -4
- package/dist/providers/index.d.ts +3 -0
- package/dist/providers/index.js +4 -0
- package/dist/providers/provider-catalog.d.ts +70 -0
- package/dist/providers/provider-catalog.js +738 -0
- package/dist/providers/provider-fallback.d.ts +35 -0
- package/dist/providers/provider-fallback.js +171 -0
- package/dist/providers/xai-oauth.d.ts +128 -0
- package/dist/providers/xai-oauth.js +735 -0
- package/dist/scheduler/cron-scheduler.d.ts +15 -3
- package/dist/scheduler/cron-scheduler.js +53 -7
- package/dist/scheduler/script-runner.d.ts +2 -0
- package/dist/scheduler/script-runner.js +19 -4
- package/dist/scheduler/watchdog-handlers.js +7 -20
- package/dist/search/usearch-index.js +7 -2
- package/dist/security/tool-policy/tool-groups.js +2 -0
- package/dist/sensory/dreaming.d.ts +45 -0
- package/dist/sensory/dreaming.js +114 -0
- package/dist/sensory/heartbeat-scheduler.d.ts +38 -0
- package/dist/sensory/heartbeat-scheduler.js +72 -0
- package/dist/sensory/reactions.d.ts +24 -0
- package/dist/sensory/reactions.js +31 -0
- package/dist/sensory/screen-reaction.d.ts +23 -0
- package/dist/sensory/screen-reaction.js +59 -0
- package/dist/sensory/sensory-bridge.d.ts +23 -0
- package/dist/sensory/sensory-bridge.js +85 -0
- package/dist/sensory/sensory-memory.d.ts +20 -0
- package/dist/sensory/sensory-memory.js +39 -0
- package/dist/sensory/speech-reaction.d.ts +21 -0
- package/dist/sensory/speech-reaction.js +83 -0
- package/dist/sensory/vision-reaction.d.ts +31 -0
- package/dist/sensory/vision-reaction.js +74 -0
- package/dist/server/index.js +106 -2
- package/dist/server/routes/mobile.d.ts +12 -4
- package/dist/server/routes/mobile.js +116 -1
- package/dist/server/tls-config.d.ts +35 -0
- package/dist/server/tls-config.js +142 -0
- package/dist/server/tunnel-manager.d.ts +20 -0
- package/dist/server/tunnel-manager.js +58 -0
- package/dist/server/websocket/fleet-bridge.js +5 -0
- package/dist/services/prompt-builder.d.ts +11 -0
- package/dist/services/prompt-builder.js +124 -17
- package/dist/shared/engine-types.d.ts +15 -1
- package/dist/sidecar/sidecar-bridge.d.ts +1 -0
- package/dist/sidecar/sidecar-bridge.js +21 -10
- package/dist/skills/parser.js +3 -0
- package/dist/skills/skill-importer.d.ts +58 -0
- package/dist/skills/skill-importer.js +261 -0
- package/dist/skills/skill-sources.d.ts +20 -0
- package/dist/skills/skill-sources.js +102 -0
- package/dist/skills/types.d.ts +6 -0
- package/dist/spec/spec-store.js +8 -1
- package/dist/talk-mode/providers/audioreader-tts.d.ts +1 -1
- package/dist/talk-mode/providers/audioreader-tts.js +8 -8
- package/dist/templates/project-scaffolding.js +3 -2
- package/dist/themes/theme-schema.d.ts +10 -10
- package/dist/tools/application-profiles.js +38 -0
- package/dist/tools/bash/bash-tool.d.ts +1 -0
- package/dist/tools/bash/bash-tool.js +16 -2
- package/dist/tools/bash/command-validator.js +3 -0
- package/dist/tools/bash/rtk-rewrite.d.ts +16 -0
- package/dist/tools/bash/rtk-rewrite.js +100 -0
- package/dist/tools/bash/streaming-executor.js +14 -1
- package/dist/tools/computer-control-tool.d.ts +16 -1
- package/dist/tools/computer-control-tool.js +317 -5
- package/dist/tools/document-generator.d.ts +14 -0
- package/dist/tools/document-generator.js +79 -0
- package/dist/tools/execute-code-rpc-invoker.d.ts +26 -2
- package/dist/tools/execute-code-rpc-invoker.js +46 -5
- package/dist/tools/metadata.js +28 -0
- package/dist/tools/office-macro-tool.d.ts +10 -0
- package/dist/tools/office-macro-tool.js +93 -0
- package/dist/tools/register-tool-handler.d.ts +25 -0
- package/dist/tools/register-tool-handler.js +100 -0
- package/dist/tools/registry/index.d.ts +2 -1
- package/dist/tools/registry/index.js +2 -1
- package/dist/tools/registry/memory-tools.d.ts +20 -0
- package/dist/tools/registry/memory-tools.js +225 -3
- package/dist/tools/registry/multimodal-tools.js +2 -2
- package/dist/tools/registry/vision-tools.d.ts +33 -0
- package/dist/tools/registry/vision-tools.js +308 -6
- package/dist/tools/registry/windows-tools.d.ts +2 -0
- package/dist/tools/registry/windows-tools.js +47 -0
- package/dist/tools/registry.d.ts +6 -0
- package/dist/tools/registry.js +8 -0
- package/dist/tools/route-peer-tool.js +14 -0
- package/dist/tools/screenshot-tool.js +14 -2
- package/dist/tools/text-to-speech-tool.d.ts +1 -1
- package/dist/tools/text-to-speech-tool.js +2 -12
- package/dist/tools/tool-selector.js +3 -59
- package/dist/utils/config-validation/schema.d.ts +40 -7
- package/dist/utils/config-validation/schema.js +130 -1
- package/dist/utils/cost-tracker.js +21 -1
- package/dist/utils/disk-guard.d.ts +180 -0
- package/dist/utils/disk-guard.js +404 -0
- package/dist/utils/init-project.d.ts +7 -0
- package/dist/utils/init-project.js +37 -0
- package/dist/utils/interactive-setup.js +35 -2
- package/dist/utils/model-utils.js +11 -2
- package/dist/utils/output-sanitizer.js +11 -0
- package/dist/utils/provider-detector.d.ts +8 -7
- package/dist/utils/provider-detector.js +19 -77
- package/dist/utils/settings-manager.d.ts +19 -0
- package/dist/utils/token-display.d.ts +3 -3
- package/dist/utils/token-display.js +22 -4
- package/dist/wizard/onboarding.d.ts +39 -0
- package/dist/wizard/onboarding.js +433 -21
- package/dist/wizard/provider-onboarding.d.ts +8 -23
- package/dist/wizard/provider-onboarding.js +39 -158
- package/package.json +4 -2
- package/dist/plugins/provider-onboarding.d.ts +0 -23
- package/dist/plugins/provider-onboarding.js +0 -116
- package/dist/utils/kokoro-tts.d.ts +0 -8
- package/dist/utils/kokoro-tts.js +0 -67
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-Platform Slash-Command Parity Manifest
|
|
3
|
+
*
|
|
4
|
+
* Defines the expected slash commands per messaging platform and
|
|
5
|
+
* compares them against the actual registered commands per adapter.
|
|
6
|
+
* Returns a machine-readable readiness report for each platform.
|
|
7
|
+
*/
|
|
8
|
+
import { getChannelManager } from './core.js';
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Expected Commands Manifest
|
|
11
|
+
// ============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* The canonical set of slash commands expected on each platform.
|
|
14
|
+
*
|
|
15
|
+
* These represent the Code Buddy bot commands that users should be
|
|
16
|
+
* able to invoke from each messaging platform. The manifest is
|
|
17
|
+
* intentionally conservative — platforms may support additional
|
|
18
|
+
* commands beyond what is listed here.
|
|
19
|
+
*/
|
|
20
|
+
export const EXPECTED_SLASH_COMMANDS = {
|
|
21
|
+
discord: [
|
|
22
|
+
{ name: 'ask', description: 'Ask Code Buddy a question' },
|
|
23
|
+
{ name: 'status', description: 'Show bot and channel status' },
|
|
24
|
+
{ name: 'clear', description: 'Clear conversation history' },
|
|
25
|
+
{ name: 'help', description: 'Show available commands' },
|
|
26
|
+
{ name: 'model', description: 'Switch or show current model' },
|
|
27
|
+
{ name: 'think', description: 'Set reasoning depth', required: false },
|
|
28
|
+
{ name: 'compact', description: 'Compact conversation context', required: false },
|
|
29
|
+
{ name: 'repo', description: 'Show repository info', required: false },
|
|
30
|
+
],
|
|
31
|
+
telegram: [
|
|
32
|
+
{ name: 'ask', description: 'Ask Code Buddy a question' },
|
|
33
|
+
{ name: 'status', description: 'Show bot and channel status' },
|
|
34
|
+
{ name: 'clear', description: 'Clear conversation history' },
|
|
35
|
+
{ name: 'help', description: 'Show available commands' },
|
|
36
|
+
{ name: 'model', description: 'Switch or show current model' },
|
|
37
|
+
{ name: 'yolo', description: 'Toggle YOLO mode', required: false },
|
|
38
|
+
{ name: 'repo', description: 'Show repository info', required: false },
|
|
39
|
+
{ name: 'branch', description: 'Show branch info', required: false },
|
|
40
|
+
],
|
|
41
|
+
slack: [
|
|
42
|
+
{ name: 'ask', description: 'Ask Code Buddy a question' },
|
|
43
|
+
{ name: 'status', description: 'Show bot and channel status' },
|
|
44
|
+
{ name: 'clear', description: 'Clear conversation history' },
|
|
45
|
+
{ name: 'help', description: 'Show available commands' },
|
|
46
|
+
{ name: 'model', description: 'Switch or show current model' },
|
|
47
|
+
{ name: 'compact', description: 'Compact conversation context', required: false },
|
|
48
|
+
{ name: 'think', description: 'Set reasoning depth', required: false },
|
|
49
|
+
],
|
|
50
|
+
matrix: [
|
|
51
|
+
{ name: 'ask', description: 'Ask Code Buddy a question' },
|
|
52
|
+
{ name: 'status', description: 'Show bot and channel status' },
|
|
53
|
+
{ name: 'clear', description: 'Clear conversation history' },
|
|
54
|
+
{ name: 'help', description: 'Show available commands' },
|
|
55
|
+
{ name: 'model', description: 'Switch or show current model', required: false },
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Check if a channel adapter implements SlashCommandProvider.
|
|
60
|
+
*/
|
|
61
|
+
function isSlashCommandProvider(channel) {
|
|
62
|
+
return typeof channel.getRegisteredCommands === 'function';
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Extract the actual registered commands from a channel adapter.
|
|
66
|
+
*
|
|
67
|
+
* Uses the SlashCommandProvider interface if available, otherwise
|
|
68
|
+
* returns an empty array (no commands can be detected).
|
|
69
|
+
*/
|
|
70
|
+
export function extractActualCommands(channel) {
|
|
71
|
+
if (isSlashCommandProvider(channel)) {
|
|
72
|
+
return channel.getRegisteredCommands();
|
|
73
|
+
}
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// Parity Checking
|
|
78
|
+
// ============================================================================
|
|
79
|
+
/**
|
|
80
|
+
* Build a parity report for a single platform.
|
|
81
|
+
*
|
|
82
|
+
* @param platform - The platform channel type
|
|
83
|
+
* @param expectedCommands - The expected slash commands
|
|
84
|
+
* @param actualCommands - The actual registered commands
|
|
85
|
+
* @param adapterRegistered - Whether the adapter is in the ChannelManager
|
|
86
|
+
*/
|
|
87
|
+
export function buildPlatformParityReport(platform, expectedCommands, actualCommands, adapterRegistered) {
|
|
88
|
+
if (!adapterRegistered) {
|
|
89
|
+
return {
|
|
90
|
+
platform,
|
|
91
|
+
adapterRegistered: false,
|
|
92
|
+
expectedCount: expectedCommands.length,
|
|
93
|
+
presentCount: 0,
|
|
94
|
+
missingRequiredCount: expectedCommands.filter((c) => c.required !== false).length,
|
|
95
|
+
missingOptionalCount: expectedCommands.filter((c) => c.required === false).length,
|
|
96
|
+
commands: expectedCommands.map((cmd) => ({
|
|
97
|
+
name: cmd.name,
|
|
98
|
+
present: false,
|
|
99
|
+
required: cmd.required !== false,
|
|
100
|
+
})),
|
|
101
|
+
status: 'no-adapter',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const actualSet = new Set(actualCommands.map((c) => c.toLowerCase()));
|
|
105
|
+
const commands = expectedCommands.map((cmd) => ({
|
|
106
|
+
name: cmd.name,
|
|
107
|
+
present: actualSet.has(cmd.name.toLowerCase()),
|
|
108
|
+
required: cmd.required !== false,
|
|
109
|
+
}));
|
|
110
|
+
const presentCount = commands.filter((c) => c.present).length;
|
|
111
|
+
const missingRequiredCount = commands.filter((c) => !c.present && c.required).length;
|
|
112
|
+
const missingOptionalCount = commands.filter((c) => !c.present && !c.required).length;
|
|
113
|
+
let status;
|
|
114
|
+
if (presentCount === expectedCommands.length) {
|
|
115
|
+
status = 'full';
|
|
116
|
+
}
|
|
117
|
+
else if (presentCount > 0) {
|
|
118
|
+
status = 'partial';
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
status = 'none';
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
platform,
|
|
125
|
+
adapterRegistered: true,
|
|
126
|
+
expectedCount: expectedCommands.length,
|
|
127
|
+
presentCount,
|
|
128
|
+
missingRequiredCount,
|
|
129
|
+
missingOptionalCount,
|
|
130
|
+
commands,
|
|
131
|
+
status,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Build the full slash-command parity manifest across all tracked platforms.
|
|
136
|
+
*
|
|
137
|
+
* @param manager - Optional ChannelManager instance (defaults to singleton)
|
|
138
|
+
*/
|
|
139
|
+
export function buildSlashParityManifest(manager) {
|
|
140
|
+
const mgr = manager ?? getChannelManager();
|
|
141
|
+
const reports = [];
|
|
142
|
+
for (const [platformKey, expectedCmds] of Object.entries(EXPECTED_SLASH_COMMANDS)) {
|
|
143
|
+
const platform = platformKey;
|
|
144
|
+
const channel = mgr.getChannel(platform);
|
|
145
|
+
const adapterRegistered = !!channel;
|
|
146
|
+
const actualCommands = channel ? extractActualCommands(channel) : [];
|
|
147
|
+
reports.push(buildPlatformParityReport(platform, expectedCmds, actualCommands, adapterRegistered));
|
|
148
|
+
}
|
|
149
|
+
const fullParityCount = reports.filter((r) => r.status === 'full').length;
|
|
150
|
+
const partialParityCount = reports.filter((r) => r.status === 'partial' || r.status === 'none').length;
|
|
151
|
+
const noAdapterCount = reports.filter((r) => r.status === 'no-adapter').length;
|
|
152
|
+
return {
|
|
153
|
+
generatedAt: new Date().toISOString(),
|
|
154
|
+
ok: reports.every((r) => r.missingRequiredCount === 0),
|
|
155
|
+
totalPlatforms: reports.length,
|
|
156
|
+
fullParityCount,
|
|
157
|
+
partialParityCount,
|
|
158
|
+
noAdapterCount,
|
|
159
|
+
platforms: reports,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Render the parity manifest as a human-readable text report.
|
|
164
|
+
*/
|
|
165
|
+
export function renderSlashParityManifest(manifest) {
|
|
166
|
+
const lines = [
|
|
167
|
+
'Slash-command parity report:',
|
|
168
|
+
` Status: ${manifest.ok ? 'ok' : 'needs attention'}`,
|
|
169
|
+
` Platforms: ${manifest.totalPlatforms} (${manifest.fullParityCount} full, ${manifest.partialParityCount} partial, ${manifest.noAdapterCount} no-adapter)`,
|
|
170
|
+
'',
|
|
171
|
+
];
|
|
172
|
+
for (const report of manifest.platforms) {
|
|
173
|
+
const missing = report.commands.filter((c) => !c.present);
|
|
174
|
+
const present = report.commands.filter((c) => c.present);
|
|
175
|
+
lines.push(` ${report.platform}: ${report.status} (${report.presentCount}/${report.expectedCount})`);
|
|
176
|
+
if (present.length > 0) {
|
|
177
|
+
lines.push(` Present: ${present.map((c) => c.name).join(', ')}`);
|
|
178
|
+
}
|
|
179
|
+
if (missing.length > 0) {
|
|
180
|
+
lines.push(` Missing: ${missing.map((c) => `${c.name}${c.required ? ' (required)' : ''}`).join(', ')}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return lines.join('\n');
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=slash-parity.js.map
|
|
@@ -65,6 +65,9 @@ export class SynologyChatChannel extends BaseChannel {
|
|
|
65
65
|
constructor(config) {
|
|
66
66
|
super('synology-chat', config);
|
|
67
67
|
}
|
|
68
|
+
// REST/webhook adapter — outbound via incoming webhooks, inbound via
|
|
69
|
+
// outgoing-webhook callbacks; no persistent connection is held open, so
|
|
70
|
+
// reconnection (ReconnectionManager) is N/A.
|
|
68
71
|
async connect() {
|
|
69
72
|
const cfg = this.config;
|
|
70
73
|
this.adapter = new SynologyChatAdapter({
|
|
@@ -129,6 +129,9 @@ export declare class TeamsChannel extends BaseChannel {
|
|
|
129
129
|
/**
|
|
130
130
|
* Connect to Teams by validating the app credentials.
|
|
131
131
|
* The actual message receiving happens via webhooks.
|
|
132
|
+
*
|
|
133
|
+
* REST/webhook adapter — no persistent connection is held open here, so
|
|
134
|
+
* reconnection (ReconnectionManager) is N/A.
|
|
132
135
|
*/
|
|
133
136
|
connect(): Promise<void>;
|
|
134
137
|
/**
|
|
@@ -111,6 +111,9 @@ export class TeamsChannel extends BaseChannel {
|
|
|
111
111
|
/**
|
|
112
112
|
* Connect to Teams by validating the app credentials.
|
|
113
113
|
* The actual message receiving happens via webhooks.
|
|
114
|
+
*
|
|
115
|
+
* REST/webhook adapter — no persistent connection is held open here, so
|
|
116
|
+
* reconnection (ReconnectionManager) is N/A.
|
|
114
117
|
*/
|
|
115
118
|
async connect() {
|
|
116
119
|
try {
|
|
@@ -58,6 +58,15 @@ export declare class TelegramChannel extends BaseChannel {
|
|
|
58
58
|
* Set bot commands
|
|
59
59
|
*/
|
|
60
60
|
private setCommands;
|
|
61
|
+
/**
|
|
62
|
+
* Return the bot command names registered with this adapter.
|
|
63
|
+
*
|
|
64
|
+
* Implements {@link SlashCommandProvider} so the slash-parity checker can
|
|
65
|
+
* report real parity instead of falling back to an empty list. Names come
|
|
66
|
+
* from the configured `commands` (the same set pushed to Telegram via
|
|
67
|
+
* {@link setCommands}), normalized without a leading slash.
|
|
68
|
+
*/
|
|
69
|
+
getRegisteredCommands(): string[];
|
|
61
70
|
/**
|
|
62
71
|
* Start polling for updates
|
|
63
72
|
*/
|
|
@@ -294,6 +294,18 @@ export class TelegramChannel extends BaseChannel {
|
|
|
294
294
|
}));
|
|
295
295
|
await this.apiRequest('setMyCommands', { commands });
|
|
296
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* Return the bot command names registered with this adapter.
|
|
299
|
+
*
|
|
300
|
+
* Implements {@link SlashCommandProvider} so the slash-parity checker can
|
|
301
|
+
* report real parity instead of falling back to an empty list. Names come
|
|
302
|
+
* from the configured `commands` (the same set pushed to Telegram via
|
|
303
|
+
* {@link setCommands}), normalized without a leading slash.
|
|
304
|
+
*/
|
|
305
|
+
getRegisteredCommands() {
|
|
306
|
+
const commands = this.telegramConfig.commands ?? [];
|
|
307
|
+
return commands.map((cmd) => cmd.command.replace(/^\//, ''));
|
|
308
|
+
}
|
|
297
309
|
/**
|
|
298
310
|
* Start polling for updates
|
|
299
311
|
*/
|
|
@@ -94,6 +94,9 @@ export class TwilioVoiceChannel extends BaseChannel {
|
|
|
94
94
|
webhookUrl: config.webhookUrl,
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
|
+
// REST/webhook adapter — calls are placed via the Twilio REST API and
|
|
98
|
+
// driven by TwiML webhook callbacks; no persistent connection is held open,
|
|
99
|
+
// so reconnection (ReconnectionManager) is N/A.
|
|
97
100
|
async connect() {
|
|
98
101
|
await this.adapter.start();
|
|
99
102
|
this.status.connected = true;
|
|
@@ -40,7 +40,13 @@ export declare class WebChatChannel extends BaseChannel {
|
|
|
40
40
|
constructor(config: WebChatConfig);
|
|
41
41
|
private get webChatConfig();
|
|
42
42
|
/**
|
|
43
|
-
* Start the HTTP and WebSocket servers
|
|
43
|
+
* Start the HTTP and WebSocket servers.
|
|
44
|
+
*
|
|
45
|
+
* Server-side adapter — this hosts a WebSocket *server* that browsers
|
|
46
|
+
* connect *into*; it does not dial out to a remote endpoint that could
|
|
47
|
+
* drop, so there is nothing to reconnect to and ReconnectionManager is N/A.
|
|
48
|
+
* (Per-client socket drops are handled by the connections map, not by
|
|
49
|
+
* re-establishing an outbound link.)
|
|
44
50
|
*/
|
|
45
51
|
connect(): Promise<void>;
|
|
46
52
|
/**
|
|
@@ -46,7 +46,13 @@ export class WebChatChannel extends BaseChannel {
|
|
|
46
46
|
// Lifecycle
|
|
47
47
|
// ==========================================================================
|
|
48
48
|
/**
|
|
49
|
-
* Start the HTTP and WebSocket servers
|
|
49
|
+
* Start the HTTP and WebSocket servers.
|
|
50
|
+
*
|
|
51
|
+
* Server-side adapter — this hosts a WebSocket *server* that browsers
|
|
52
|
+
* connect *into*; it does not dial out to a remote endpoint that could
|
|
53
|
+
* drop, so there is nothing to reconnect to and ReconnectionManager is N/A.
|
|
54
|
+
* (Per-client socket drops are handled by the connections map, not by
|
|
55
|
+
* re-establishing an outbound link.)
|
|
50
56
|
*/
|
|
51
57
|
async connect() {
|
|
52
58
|
let WebSocketModule;
|
|
@@ -97,6 +97,9 @@ export class WeComChannel extends BaseChannel {
|
|
|
97
97
|
mentionedMobileList: config.mentionedMobileList,
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
|
+
// REST/webhook adapter — outbound messages are one-shot HTTPS POSTs to a
|
|
101
|
+
// WeCom group-robot webhook; no persistent connection is held open, so
|
|
102
|
+
// reconnection (ReconnectionManager) is N/A.
|
|
100
103
|
async connect() {
|
|
101
104
|
await this.adapter.start();
|
|
102
105
|
this.status.connected = true;
|
|
@@ -93,6 +93,9 @@ export class WeixinChannel extends BaseChannel {
|
|
|
93
93
|
kfAccount: config.kfAccount,
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
|
+
// REST/webhook adapter — outbound via the WeChat (Weixin) HTTP API, inbound
|
|
97
|
+
// via webhook callbacks; no persistent connection is held open, so
|
|
98
|
+
// reconnection (ReconnectionManager) is N/A.
|
|
96
99
|
async connect() {
|
|
97
100
|
await this.adapter.start();
|
|
98
101
|
this.status.connected = true;
|
|
@@ -70,6 +70,9 @@ export class ZaloChannel extends BaseChannel {
|
|
|
70
70
|
mode: config.mode,
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
|
+
// REST/webhook adapter — outbound via the Zalo API, inbound via webhook
|
|
74
|
+
// callbacks; no persistent connection is held open, so reconnection
|
|
75
|
+
// (ReconnectionManager) is N/A.
|
|
73
76
|
async connect() {
|
|
74
77
|
await this.adapter.start();
|
|
75
78
|
this.status.connected = true;
|
|
@@ -91,7 +91,7 @@ export async function saveCommandLineSettings(apiKey, baseURL) {
|
|
|
91
91
|
export function validateConfig(config) {
|
|
92
92
|
const errors = [];
|
|
93
93
|
if (!config.apiKey) {
|
|
94
|
-
errors.push('API key required.
|
|
94
|
+
errors.push('API key required. Sign in free with `buddy login` (ChatGPT plan), set GROK_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY / GOOGLE_API_KEY, or use --api-key. Run `buddy doctor` to check your setup.');
|
|
95
95
|
}
|
|
96
96
|
return {
|
|
97
97
|
valid: errors.length === 0,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ChatCompletionMessageParam, ChatCompletionChunk, ChatCompletionCreateParamsNonStreaming, ChatCompletionCreateParamsStreaming } from "openai/resources/chat";
|
|
2
2
|
import type { CircuitBreakerConfig } from "../providers/circuit-breaker.js";
|
|
3
|
+
import { type RuntimeFallbackProvider } from "../providers/provider-fallback.js";
|
|
3
4
|
export { hasToolCalls } from './message-guards.js';
|
|
4
5
|
export type { CodeBuddyMessageWithToolCalls } from './message-guards.js';
|
|
5
6
|
/** Sentinel apiKey for the ChatGPT OAuth path — auto-detect chain in
|
|
@@ -78,6 +79,8 @@ export type GeminiThinkingLevel = 'minimal' | 'low' | 'medium' | 'high';
|
|
|
78
79
|
export interface ChatOptions {
|
|
79
80
|
model?: string;
|
|
80
81
|
temperature?: number;
|
|
82
|
+
/** Optional per-call output-token cap. Providers fall back to their model default when omitted. */
|
|
83
|
+
maxTokens?: number;
|
|
81
84
|
searchOptions?: SearchOptions;
|
|
82
85
|
/** Optional request timeout override (ms) for Gemini native API calls */
|
|
83
86
|
timeoutMs?: number;
|
|
@@ -95,6 +98,8 @@ export interface ChatOptions {
|
|
|
95
98
|
responseFormat?: 'text' | 'json';
|
|
96
99
|
/** tool_choice override for this request */
|
|
97
100
|
tool_choice?: 'auto' | 'none' | 'required';
|
|
101
|
+
/** Disable Hermes-style cross-provider fallback for this one request. */
|
|
102
|
+
disableProviderFallback?: boolean;
|
|
98
103
|
/**
|
|
99
104
|
* Enable Gemini's native server-side Google Search grounding for this
|
|
100
105
|
* request. Only affects the GeminiNativeProvider — ignored by the
|
|
@@ -129,6 +134,16 @@ export interface ChatOptions {
|
|
|
129
134
|
maxDelayMs?: number;
|
|
130
135
|
};
|
|
131
136
|
}
|
|
137
|
+
export interface CodeBuddyClientOptions {
|
|
138
|
+
/** Enable env-configured fallback providers. Default: true. */
|
|
139
|
+
enableFallbacks?: boolean;
|
|
140
|
+
/** Enable same-provider auth profile rotation before cross-provider fallback. Default: true. */
|
|
141
|
+
enableCredentialPool?: boolean;
|
|
142
|
+
/** Explicit same-provider credential pool candidates, mainly for tests and controlled embedding. */
|
|
143
|
+
credentialPoolProviders?: RuntimeFallbackProvider[];
|
|
144
|
+
/** Explicit fallback providers, mainly for tests and controlled embedding. */
|
|
145
|
+
fallbackProviders?: RuntimeFallbackProvider[];
|
|
146
|
+
}
|
|
132
147
|
export interface CodeBuddyResponse {
|
|
133
148
|
choices: Array<{
|
|
134
149
|
message: {
|
|
@@ -168,6 +183,10 @@ export declare class CodeBuddyClient {
|
|
|
168
183
|
private geminiCliProvider;
|
|
169
184
|
/** True when routed through the Gemini CLI subprocess. */
|
|
170
185
|
private isGeminiCliProvider;
|
|
186
|
+
/** Same-provider auth profile candidates, tried before cross-provider fallback. */
|
|
187
|
+
private credentialPoolProviders;
|
|
188
|
+
/** Hermes-style cross-provider fallback candidates, tried per failed chat turn. */
|
|
189
|
+
private fallbackProviders;
|
|
171
190
|
/**
|
|
172
191
|
* Configure the circuit breaker for this client.
|
|
173
192
|
* Once configured, calls with `circuitBreaker: true` in ChatOptions
|
|
@@ -184,8 +203,14 @@ export declare class CodeBuddyClient {
|
|
|
184
203
|
*/
|
|
185
204
|
setDefaultGoogleSearch(enabled: boolean): void;
|
|
186
205
|
setCircuitBreakerConfig(config: Partial<CircuitBreakerConfig>): void;
|
|
206
|
+
/**
|
|
207
|
+
* Override the cross-provider fallback list after construction. Used by the
|
|
208
|
+
* active-LLM registry to auto-populate failover from the user's live logins.
|
|
209
|
+
* `chatWithProviderFallback` reads this per-call, so it takes effect immediately.
|
|
210
|
+
*/
|
|
211
|
+
setRuntimeFallbackProviders(providers: RuntimeFallbackProvider[]): void;
|
|
187
212
|
private static isGeminiModelName;
|
|
188
|
-
constructor(apiKey: string, model?: string, baseURL?: string);
|
|
213
|
+
constructor(apiKey: string, model?: string, baseURL?: string, options?: CodeBuddyClientOptions);
|
|
189
214
|
/**
|
|
190
215
|
* Probe the model to check if it supports function calling
|
|
191
216
|
* Makes a quick test request with a simple tool
|
|
@@ -226,6 +251,10 @@ export declare class CodeBuddyClient {
|
|
|
226
251
|
*/
|
|
227
252
|
isGemini(): boolean;
|
|
228
253
|
chat(messages: CodeBuddyMessage[], tools?: CodeBuddyTool[], options?: string | ChatOptions, searchOptions?: SearchOptions): Promise<CodeBuddyResponse>;
|
|
254
|
+
private dispatchChat;
|
|
255
|
+
private chatWithProviderFallback;
|
|
229
256
|
chatStream(messages: CodeBuddyMessage[], tools?: CodeBuddyTool[], options?: string | ChatOptions, searchOptions?: SearchOptions): AsyncGenerator<ChatCompletionChunk, void, unknown>;
|
|
257
|
+
private dispatchChatStream;
|
|
258
|
+
private chatStreamWithProviderFallback;
|
|
230
259
|
search(query: string, searchParameters?: SearchParameters): Promise<CodeBuddyResponse>;
|
|
231
260
|
}
|
package/dist/codebuddy/client.js
CHANGED
|
@@ -7,6 +7,7 @@ import { OpenAICompatProvider } from "./providers/provider-openai-compat.js";
|
|
|
7
7
|
import { ChatGptResponsesProvider } from "./providers/provider-chatgpt-responses.js";
|
|
8
8
|
import { GeminiCliProvider } from "./providers/provider-gemini-cli.js";
|
|
9
9
|
import { withStreamRetry } from "./stream-retry.js";
|
|
10
|
+
import { recordRuntimeFallbackFailure, recordRuntimeFallbackSuccess, resolveRuntimeCredentialPoolProviders, resolveRuntimeFallbackProviders, } from "../providers/provider-fallback.js";
|
|
10
11
|
export { hasToolCalls } from './message-guards.js';
|
|
11
12
|
/** Sentinel apiKey for the ChatGPT OAuth path — auto-detect chain in
|
|
12
13
|
* `src/index.ts` passes this when `~/.codebuddy/codex-auth.json` exists. */
|
|
@@ -41,6 +42,10 @@ export class CodeBuddyClient {
|
|
|
41
42
|
geminiCliProvider = null;
|
|
42
43
|
/** True when routed through the Gemini CLI subprocess. */
|
|
43
44
|
isGeminiCliProvider = false;
|
|
45
|
+
/** Same-provider auth profile candidates, tried before cross-provider fallback. */
|
|
46
|
+
credentialPoolProviders = [];
|
|
47
|
+
/** Hermes-style cross-provider fallback candidates, tried per failed chat turn. */
|
|
48
|
+
fallbackProviders = [];
|
|
44
49
|
/**
|
|
45
50
|
* Configure the circuit breaker for this client.
|
|
46
51
|
* Once configured, calls with `circuitBreaker: true` in ChatOptions
|
|
@@ -68,10 +73,18 @@ export class CodeBuddyClient {
|
|
|
68
73
|
this.circuitBreakerConfig = config;
|
|
69
74
|
// Provider reads via getter at call-time, so no propagation needed.
|
|
70
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Override the cross-provider fallback list after construction. Used by the
|
|
78
|
+
* active-LLM registry to auto-populate failover from the user's live logins.
|
|
79
|
+
* `chatWithProviderFallback` reads this per-call, so it takes effect immediately.
|
|
80
|
+
*/
|
|
81
|
+
setRuntimeFallbackProviders(providers) {
|
|
82
|
+
this.fallbackProviders = providers;
|
|
83
|
+
}
|
|
71
84
|
static isGeminiModelName(model) {
|
|
72
85
|
return model.toLowerCase().includes('gemini');
|
|
73
86
|
}
|
|
74
|
-
constructor(apiKey, model, baseURL) {
|
|
87
|
+
constructor(apiKey, model, baseURL, options = {}) {
|
|
75
88
|
// Validate API key
|
|
76
89
|
if (!apiKey || typeof apiKey !== 'string') {
|
|
77
90
|
throw new Error('API key is required and must be a non-empty string');
|
|
@@ -198,6 +211,24 @@ export class CodeBuddyClient {
|
|
|
198
211
|
logger.warn(`Model '${model}' is not officially supported. Using default token limits.`);
|
|
199
212
|
}
|
|
200
213
|
}
|
|
214
|
+
if (options.enableFallbacks !== false) {
|
|
215
|
+
if (options.enableCredentialPool !== false) {
|
|
216
|
+
this.credentialPoolProviders = options.credentialPoolProviders ?? resolveRuntimeCredentialPoolProviders({
|
|
217
|
+
active: {
|
|
218
|
+
apiKey: this.apiKey,
|
|
219
|
+
baseURL: this.baseURL,
|
|
220
|
+
model: this.currentModel,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
this.fallbackProviders = options.fallbackProviders ?? resolveRuntimeFallbackProviders({
|
|
225
|
+
active: {
|
|
226
|
+
apiKey: this.apiKey,
|
|
227
|
+
baseURL: this.baseURL,
|
|
228
|
+
model: this.currentModel,
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
}
|
|
201
232
|
}
|
|
202
233
|
/**
|
|
203
234
|
* Probe the model to check if it supports function calling
|
|
@@ -323,6 +354,14 @@ export class CodeBuddyClient {
|
|
|
323
354
|
? { model: options, searchOptions }
|
|
324
355
|
: options || {};
|
|
325
356
|
// Dispatch to the active strategy.
|
|
357
|
+
try {
|
|
358
|
+
return await this.dispatchChat(messages, tools, opts, searchOptions);
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
return await this.chatWithProviderFallback(error, messages, tools, opts, searchOptions);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async dispatchChat(messages, tools, opts, searchOptions) {
|
|
326
365
|
if (this.geminiProvider) {
|
|
327
366
|
return this.geminiProvider.chat(messages, tools, opts);
|
|
328
367
|
}
|
|
@@ -334,36 +373,144 @@ export class CodeBuddyClient {
|
|
|
334
373
|
}
|
|
335
374
|
return this.openaiCompatProvider.chat(messages, tools, opts, searchOptions);
|
|
336
375
|
}
|
|
376
|
+
async chatWithProviderFallback(primaryError, messages, tools, opts, searchOptions) {
|
|
377
|
+
const fallbackCandidates = [
|
|
378
|
+
...this.credentialPoolProviders,
|
|
379
|
+
...this.fallbackProviders,
|
|
380
|
+
];
|
|
381
|
+
if (opts.disableProviderFallback || fallbackCandidates.length === 0) {
|
|
382
|
+
throw primaryError;
|
|
383
|
+
}
|
|
384
|
+
const fallbackOptsBase = {
|
|
385
|
+
...opts,
|
|
386
|
+
disableProviderFallback: true,
|
|
387
|
+
};
|
|
388
|
+
for (const fallback of fallbackCandidates) {
|
|
389
|
+
try {
|
|
390
|
+
logger.warn('Primary provider failed; trying fallback provider', {
|
|
391
|
+
source: 'CodeBuddyClient',
|
|
392
|
+
primaryProvider: this.getProviderName(),
|
|
393
|
+
fallbackProvider: fallback.provider,
|
|
394
|
+
fallbackModel: fallback.model,
|
|
395
|
+
fallbackSource: fallback.fallbackSource,
|
|
396
|
+
profileId: fallback.profileId,
|
|
397
|
+
error: primaryError instanceof Error ? primaryError.message : String(primaryError),
|
|
398
|
+
});
|
|
399
|
+
const fallbackClient = new CodeBuddyClient(fallback.apiKey, fallback.model, fallback.baseURL, { enableFallbacks: false });
|
|
400
|
+
if (this.circuitBreakerConfig) {
|
|
401
|
+
fallbackClient.setCircuitBreakerConfig(this.circuitBreakerConfig);
|
|
402
|
+
}
|
|
403
|
+
const response = await fallbackClient.chat(messages, tools, {
|
|
404
|
+
...fallbackOptsBase,
|
|
405
|
+
model: fallback.model,
|
|
406
|
+
}, searchOptions);
|
|
407
|
+
recordRuntimeFallbackSuccess(fallback);
|
|
408
|
+
return response;
|
|
409
|
+
}
|
|
410
|
+
catch (fallbackError) {
|
|
411
|
+
recordRuntimeFallbackFailure(fallback, fallbackError);
|
|
412
|
+
logger.warn('Fallback provider failed', {
|
|
413
|
+
source: 'CodeBuddyClient',
|
|
414
|
+
fallbackProvider: fallback.provider,
|
|
415
|
+
fallbackModel: fallback.model,
|
|
416
|
+
fallbackSource: fallback.fallbackSource,
|
|
417
|
+
profileId: fallback.profileId,
|
|
418
|
+
error: fallbackError instanceof Error ? fallbackError.message : String(fallbackError),
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
throw primaryError;
|
|
423
|
+
}
|
|
337
424
|
async *chatStream(messages, tools, options, searchOptions) {
|
|
338
425
|
// Support both old signature (model as string) and new signature (options object)
|
|
339
426
|
const opts = typeof options === "string"
|
|
340
427
|
? { model: options, searchOptions }
|
|
341
428
|
: options || {};
|
|
342
|
-
// Dispatch to the active strategy. Wrapped in a factory so we can
|
|
343
|
-
// re-invoke it inside `withStreamRetry` when opt-in retry is enabled.
|
|
344
|
-
const factory = () => {
|
|
345
|
-
if (this.geminiProvider) {
|
|
346
|
-
return this.geminiProvider.chatStream(messages, tools, opts);
|
|
347
|
-
}
|
|
348
|
-
if (this.chatgptProvider) {
|
|
349
|
-
return this.chatgptProvider.chatStream(messages, tools, opts);
|
|
350
|
-
}
|
|
351
|
-
if (this.geminiCliProvider) {
|
|
352
|
-
return this.geminiCliProvider.chatStream(messages, tools, opts);
|
|
353
|
-
}
|
|
354
|
-
return this.openaiCompatProvider.chatStream(messages, tools, opts, searchOptions);
|
|
355
|
-
};
|
|
356
429
|
// Resolve retry opt-in. Per-call `streamRetry` wins over env var when
|
|
357
430
|
// explicitly set (including `false`, which forces no retry).
|
|
358
431
|
const envOptIn = process.env.CODEBUDDY_STREAM_RETRY === '1';
|
|
359
432
|
const callOptIn = opts.streamRetry;
|
|
360
433
|
const retryEnabled = callOptIn !== undefined ? !!callOptIn : envOptIn;
|
|
361
|
-
if (!retryEnabled) {
|
|
362
|
-
yield* factory();
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
434
|
const retryOpts = typeof callOptIn === 'object' && callOptIn !== null ? callOptIn : {};
|
|
366
|
-
|
|
435
|
+
const primaryFactory = () => this.dispatchChatStream(messages, tools, opts, searchOptions);
|
|
436
|
+
const primaryStream = retryEnabled
|
|
437
|
+
? withStreamRetry(primaryFactory, retryOpts)
|
|
438
|
+
: primaryFactory();
|
|
439
|
+
let yieldedAnyChunk = false;
|
|
440
|
+
try {
|
|
441
|
+
for await (const chunk of primaryStream) {
|
|
442
|
+
yieldedAnyChunk = true;
|
|
443
|
+
yield chunk;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
if (yieldedAnyChunk) {
|
|
448
|
+
throw error;
|
|
449
|
+
}
|
|
450
|
+
yield* this.chatStreamWithProviderFallback(error, messages, tools, opts, searchOptions);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
dispatchChatStream(messages, tools, opts, searchOptions) {
|
|
454
|
+
if (this.geminiProvider) {
|
|
455
|
+
return this.geminiProvider.chatStream(messages, tools, opts);
|
|
456
|
+
}
|
|
457
|
+
if (this.chatgptProvider) {
|
|
458
|
+
return this.chatgptProvider.chatStream(messages, tools, opts);
|
|
459
|
+
}
|
|
460
|
+
if (this.geminiCliProvider) {
|
|
461
|
+
return this.geminiCliProvider.chatStream(messages, tools, opts);
|
|
462
|
+
}
|
|
463
|
+
return this.openaiCompatProvider.chatStream(messages, tools, opts, searchOptions);
|
|
464
|
+
}
|
|
465
|
+
async *chatStreamWithProviderFallback(primaryError, messages, tools, opts, searchOptions) {
|
|
466
|
+
const fallbackCandidates = [
|
|
467
|
+
...this.credentialPoolProviders,
|
|
468
|
+
...this.fallbackProviders,
|
|
469
|
+
];
|
|
470
|
+
if (opts.disableProviderFallback || fallbackCandidates.length === 0) {
|
|
471
|
+
throw primaryError;
|
|
472
|
+
}
|
|
473
|
+
const fallbackOptsBase = {
|
|
474
|
+
...opts,
|
|
475
|
+
disableProviderFallback: true,
|
|
476
|
+
};
|
|
477
|
+
for (const fallback of fallbackCandidates) {
|
|
478
|
+
try {
|
|
479
|
+
logger.warn('Primary provider stream failed before first chunk; trying fallback provider', {
|
|
480
|
+
source: 'CodeBuddyClient',
|
|
481
|
+
primaryProvider: this.getProviderName(),
|
|
482
|
+
fallbackProvider: fallback.provider,
|
|
483
|
+
fallbackModel: fallback.model,
|
|
484
|
+
fallbackSource: fallback.fallbackSource,
|
|
485
|
+
profileId: fallback.profileId,
|
|
486
|
+
error: primaryError instanceof Error ? primaryError.message : String(primaryError),
|
|
487
|
+
});
|
|
488
|
+
const fallbackClient = new CodeBuddyClient(fallback.apiKey, fallback.model, fallback.baseURL, { enableFallbacks: false });
|
|
489
|
+
if (this.circuitBreakerConfig) {
|
|
490
|
+
fallbackClient.setCircuitBreakerConfig(this.circuitBreakerConfig);
|
|
491
|
+
}
|
|
492
|
+
for await (const chunk of fallbackClient.chatStream(messages, tools, {
|
|
493
|
+
...fallbackOptsBase,
|
|
494
|
+
model: fallback.model,
|
|
495
|
+
}, searchOptions)) {
|
|
496
|
+
yield chunk;
|
|
497
|
+
}
|
|
498
|
+
recordRuntimeFallbackSuccess(fallback);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
catch (fallbackError) {
|
|
502
|
+
recordRuntimeFallbackFailure(fallback, fallbackError);
|
|
503
|
+
logger.warn('Fallback provider stream failed', {
|
|
504
|
+
source: 'CodeBuddyClient',
|
|
505
|
+
fallbackProvider: fallback.provider,
|
|
506
|
+
fallbackModel: fallback.model,
|
|
507
|
+
fallbackSource: fallback.fallbackSource,
|
|
508
|
+
profileId: fallback.profileId,
|
|
509
|
+
error: fallbackError instanceof Error ? fallbackError.message : String(fallbackError),
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
throw primaryError;
|
|
367
514
|
}
|
|
368
515
|
async search(query, searchParameters) {
|
|
369
516
|
const searchMessage = {
|