muonroi-cli 1.7.2 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/packages/agent-harness-core/src/event-filter.js +1 -0
- package/dist/packages/agent-harness-core/src/event-redact.js +8 -0
- package/dist/packages/agent-harness-core/src/mcp-server.d.ts +16 -0
- package/dist/packages/agent-harness-core/src/mcp-server.js +88 -10
- package/dist/packages/agent-harness-core/src/protocol.d.ts +15 -0
- package/dist/packages/agent-harness-core/src/selector.js +2 -2
- package/dist/packages/agent-harness-core/src/transports/sidechannel.js +1 -1
- package/dist/packages/agent-harness-opentui/src/agent-mode.js +1 -1
- package/dist/packages/agent-harness-opentui/src/install.js +1 -1
- package/dist/src/__test-stubs__/vitest-setup.js +4 -0
- package/dist/src/agent-harness/__tests__/mock-model.spec.js +54 -0
- package/dist/src/agent-harness/mock-model.d.ts +1 -1
- package/dist/src/agent-harness/mock-model.js +143 -6
- package/dist/src/agent-harness/test-spawn.js +3 -1
- package/dist/src/chat/chat-keychain.js +9 -1
- package/dist/src/cli/cost-forensics.js +7 -4
- package/dist/src/cli/keys.d.ts +2 -2
- package/dist/src/cli/keys.js +107 -28
- package/dist/src/cli/usage-report.js +2 -2
- package/dist/src/council/__tests__/abort-threading.test.d.ts +1 -0
- package/dist/src/council/__tests__/abort-threading.test.js +193 -0
- package/dist/src/council/__tests__/clarification-prompt.test.js +21 -1
- package/dist/src/council/__tests__/council-turn-length-emit.test.d.ts +1 -0
- package/dist/src/council/__tests__/council-turn-length-emit.test.js +130 -0
- package/dist/src/council/__tests__/debate-round-budget.test.d.ts +1 -0
- package/dist/src/council/__tests__/debate-round-budget.test.js +45 -0
- package/dist/src/council/__tests__/evaluator-metrics.test.js +3 -0
- package/dist/src/council/__tests__/post-debate-recommendation.test.d.ts +1 -0
- package/dist/src/council/__tests__/post-debate-recommendation.test.js +58 -0
- package/dist/src/council/__tests__/research-tools.test.js +4 -0
- package/dist/src/council/__tests__/round-tools.test.js +6 -0
- package/dist/src/council/clarifier-question-cap.test.d.ts +12 -0
- package/dist/src/council/clarifier-question-cap.test.js +73 -0
- package/dist/src/council/clarifier.d.ts +29 -1
- package/dist/src/council/clarifier.js +64 -1
- package/dist/src/council/debate-planner.d.ts +2 -1
- package/dist/src/council/debate-planner.js +5 -3
- package/dist/src/council/debate.d.ts +9 -0
- package/dist/src/council/debate.js +88 -8
- package/dist/src/council/index.d.ts +46 -1
- package/dist/src/council/index.js +125 -19
- package/dist/src/council/llm.js +41 -13
- package/dist/src/council/prompts.js +36 -1
- package/dist/src/council/types.d.ts +14 -1
- package/dist/src/ee/__tests__/export-transcripts.test.js +1 -1
- package/dist/src/ee/bridge.d.ts +3 -1
- package/dist/src/ee/bridge.js +9 -3
- package/dist/src/ee/bridge.test.js +1 -2
- package/dist/src/ee/client.js +15 -2
- package/dist/src/ee/recall-ledger.d.ts +20 -0
- package/dist/src/ee/recall-ledger.js +40 -1
- package/dist/src/ee/search.d.ts +25 -0
- package/dist/src/ee/search.js +73 -0
- package/dist/src/ee/types.d.ts +15 -1
- package/dist/src/ee/who-am-i.d.ts +41 -0
- package/dist/src/ee/who-am-i.js +143 -0
- package/dist/src/ee/who-am-i.test.d.ts +1 -0
- package/dist/src/ee/who-am-i.test.js +101 -0
- package/dist/src/flow/compaction/index.d.ts +1 -0
- package/dist/src/flow/compaction/index.js +4 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/headless/council-answers.js +1 -0
- package/dist/src/headless/output.d.ts +30 -1
- package/dist/src/headless/output.js +180 -0
- package/dist/src/headless/output.test.js +61 -1
- package/dist/src/hooks/index.js +48 -2
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +59 -62
- package/dist/src/lsp/client-normalize.test.d.ts +12 -0
- package/dist/src/lsp/client-normalize.test.js +34 -0
- package/dist/src/lsp/client.d.ts +1 -0
- package/dist/src/lsp/client.js +10 -2
- package/dist/src/lsp/manager.d.ts +1 -1
- package/dist/src/lsp/manager.js +13 -5
- package/dist/src/lsp/manager.test.js +41 -2
- package/dist/src/lsp/runtime.d.ts +1 -1
- package/dist/src/lsp/runtime.js +2 -2
- package/dist/src/maintain/__tests__/gh-create-pr.test.js +7 -3
- package/dist/src/maintain/__tests__/pr-builder.test.js +7 -3
- package/dist/src/mcp/__tests__/client-pool.spec.js +3 -3
- package/dist/src/mcp/__tests__/ee-tools.test.js +42 -14
- package/dist/src/mcp/__tests__/forensics-tools.test.js +3 -3
- package/dist/src/mcp/__tests__/lsp-tools.test.js +4 -4
- package/dist/src/mcp/auto-setup.js +0 -8
- package/dist/src/mcp/client-pool.d.ts +1 -1
- package/dist/src/mcp/client-pool.js +25 -9
- package/dist/src/mcp/ee-tools.d.ts +10 -0
- package/dist/src/mcp/ee-tools.js +34 -0
- package/dist/src/mcp/self-verify-jobs.d.ts +1 -0
- package/dist/src/mcp/self-verify-jobs.js +1 -3
- package/dist/src/mcp/self-verify-runner.js +1 -1
- package/dist/src/models/catalog-gemini.test.js +12 -10
- package/dist/src/models/catalog.json +106 -38
- package/dist/src/orchestrator/__tests__/auto-commit.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/auto-commit.test.js +142 -0
- package/dist/src/orchestrator/__tests__/batch-turn-runner.test.js +3 -0
- package/dist/src/orchestrator/__tests__/delegations.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/delegations.test.js +96 -0
- package/dist/src/orchestrator/__tests__/message-processor.test.js +2 -0
- package/dist/src/orchestrator/__tests__/prompts.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/prompts.test.js +88 -0
- package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -0
- package/dist/src/orchestrator/__tests__/sub-session-delegation.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/sub-session-delegation.test.js +507 -0
- package/dist/src/orchestrator/agent.test.js +5 -2
- package/dist/src/orchestrator/auto-commit.d.ts +110 -0
- package/dist/src/orchestrator/auto-commit.js +366 -0
- package/dist/src/orchestrator/batch-turn-runner.d.ts +3 -2
- package/dist/src/orchestrator/batch-turn-runner.js +2 -1
- package/dist/src/orchestrator/compaction-proposer-prompt.d.ts +8 -0
- package/dist/src/orchestrator/compaction-proposer-prompt.js +43 -0
- package/dist/src/orchestrator/compaction.d.ts +22 -0
- package/dist/src/orchestrator/compaction.js +42 -0
- package/dist/src/orchestrator/delegations.d.ts +1 -0
- package/dist/src/orchestrator/delegations.js +41 -0
- package/dist/src/orchestrator/error-utils.js +27 -2
- package/dist/src/orchestrator/message-processor.d.ts +23 -5
- package/dist/src/orchestrator/message-processor.js +226 -2573
- package/dist/src/orchestrator/orchestrator.d.ts +11 -2
- package/dist/src/orchestrator/orchestrator.js +530 -43
- package/dist/src/orchestrator/pending-calls.js +2 -1
- package/dist/src/orchestrator/preprocessor.d.ts +13 -0
- package/dist/src/orchestrator/preprocessor.js +146 -0
- package/dist/src/orchestrator/prompts.d.ts +16 -0
- package/dist/src/orchestrator/prompts.js +130 -67
- package/dist/src/orchestrator/safety-askcard.d.ts +66 -0
- package/dist/src/orchestrator/safety-askcard.js +74 -0
- package/dist/src/orchestrator/sandbox.test.js +8 -5
- package/dist/src/orchestrator/scope-ceiling.js +1 -1
- package/dist/src/orchestrator/stall-watchdog.d.ts +47 -0
- package/dist/src/orchestrator/stall-watchdog.js +23 -0
- package/dist/src/orchestrator/stall-watchdog.test.js +36 -1
- package/dist/src/orchestrator/stream-runner.js +52 -30
- package/dist/src/orchestrator/sub-agent-cap.d.ts +2 -0
- package/dist/src/orchestrator/sub-agent-cap.js +12 -4
- package/dist/src/orchestrator/sub-agent-model-tier.js +1 -1
- package/dist/src/orchestrator/subagent-compactor.d.ts +21 -0
- package/dist/src/orchestrator/subagent-compactor.js +148 -24
- package/dist/src/orchestrator/subagent-compactor.spec.js +249 -1
- package/dist/src/orchestrator/text-tool-call-detector.d.ts +9 -5
- package/dist/src/orchestrator/text-tool-call-detector.js +16 -12
- package/dist/src/orchestrator/text-tool-call-detector.test.js +66 -8
- package/dist/src/orchestrator/tool-args-repair.js +1 -1
- package/dist/src/orchestrator/tool-engine.d.ts +153 -0
- package/dist/src/orchestrator/tool-engine.js +3138 -0
- package/dist/src/orchestrator/turn-runner-deps.d.ts +3 -0
- package/dist/src/pil/__tests__/discovery-types.test.js +15 -4
- package/dist/src/pil/__tests__/discovery.test.js +89 -145
- package/dist/src/pil/__tests__/dual-run.test.js +2 -0
- package/dist/src/pil/__tests__/layer1-intent-trace.test.js +3 -0
- package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
- package/dist/src/pil/__tests__/layer16-clarity.test.js +35 -32
- package/dist/src/pil/__tests__/layer18-acceptance.test.js +1 -78
- package/dist/src/pil/__tests__/layer2_5-ponytail.test.d.ts +1 -0
- package/dist/src/pil/__tests__/layer2_5-ponytail.test.js +53 -0
- package/dist/src/pil/__tests__/layer3-ee-injection.test.js +67 -4
- package/dist/src/pil/__tests__/layer3-injected-chunk.test.js +14 -2
- package/dist/src/pil/__tests__/layer4-gsd.test.js +30 -0
- package/dist/src/pil/__tests__/layer5-context.test.js +39 -1
- package/dist/src/pil/__tests__/layer6-output.test.js +30 -2
- package/dist/src/pil/__tests__/llm-classify.test.js +79 -1
- package/dist/src/pil/__tests__/orchestrator-integration.test.js +3 -1
- package/dist/src/pil/__tests__/pipeline.test.js +11 -8
- package/dist/src/pil/__tests__/response-tools.test.js +52 -0
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +5 -3
- package/dist/src/pil/agent-operating-contract.test.js +2 -2
- package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
- package/dist/src/pil/cheap-model-playbook.js +1 -1
- package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
- package/dist/src/pil/cheap-model-workbooks.js +1 -1
- package/dist/src/pil/config.d.ts +20 -0
- package/dist/src/pil/config.js +35 -0
- package/dist/src/pil/discovery-types.d.ts +32 -9
- package/dist/src/pil/discovery.d.ts +3 -3
- package/dist/src/pil/discovery.js +138 -244
- package/dist/src/pil/index.d.ts +1 -1
- package/dist/src/pil/index.js +1 -1
- package/dist/src/pil/layer1-intent.d.ts +9 -1
- package/dist/src/pil/layer1-intent.js +62 -7
- package/dist/src/pil/layer1-intent.test.js +112 -2
- package/dist/src/pil/layer15-context-scan.js +1 -1
- package/dist/src/pil/layer16-clarity.d.ts +21 -25
- package/dist/src/pil/layer16-clarity.js +52 -88
- package/dist/src/pil/layer18-acceptance.d.ts +13 -3
- package/dist/src/pil/layer18-acceptance.js +1 -75
- package/dist/src/pil/layer2-personality.js +7 -8
- package/dist/src/pil/layer2_5-ponytail.d.ts +2 -0
- package/dist/src/pil/layer2_5-ponytail.js +41 -0
- package/dist/src/pil/layer3-ee-injection.js +180 -14
- package/dist/src/pil/layer4-gsd.js +22 -8
- package/dist/src/pil/layer5-context.d.ts +1 -0
- package/dist/src/pil/layer5-context.js +26 -11
- package/dist/src/pil/layer6-output.js +23 -5
- package/dist/src/pil/llm-classify.d.ts +11 -0
- package/dist/src/pil/llm-classify.js +196 -3
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +4 -3
- package/dist/src/pil/pipeline.js +15 -1
- package/dist/src/pil/response-tools.d.ts +16 -0
- package/dist/src/pil/response-tools.js +41 -0
- package/dist/src/pil/schema.d.ts +8 -0
- package/dist/src/pil/schema.js +24 -3
- package/dist/src/pil/task-tier-map.js +7 -7
- package/dist/src/pil/types.d.ts +9 -3
- package/dist/src/product-loop/__tests__/discovery-interview.test.js +103 -1
- package/dist/src/product-loop/__tests__/gather-selectable-alts.test.js +34 -0
- package/dist/src/product-loop/__tests__/sprint-self-verify.test.js +12 -12
- package/dist/src/product-loop/discovery-interview.d.ts +20 -0
- package/dist/src/product-loop/discovery-interview.js +121 -7
- package/dist/src/product-loop/gather.js +19 -5
- package/dist/src/product-loop/loop-driver.js +2 -2
- package/dist/src/product-loop/sprint-self-verify.js +2 -2
- package/dist/src/providers/__tests__/reasoning-roundtrip.test.js +12 -0
- package/dist/src/providers/auth/__tests__/gemini-oauth.test.d.ts +1 -5
- package/dist/src/providers/auth/__tests__/gemini-oauth.test.js +1 -5
- package/dist/src/providers/auth/gcloud.d.ts +28 -0
- package/dist/src/providers/auth/gcloud.js +102 -0
- package/dist/src/providers/auth/gemini-oauth.d.ts +46 -19
- package/dist/src/providers/auth/gemini-oauth.js +292 -105
- package/dist/src/providers/auth/grok-oauth.d.ts +1 -1
- package/dist/src/providers/auth/grok-oauth.js +1 -1
- package/dist/src/providers/auth/registry.js +28 -6
- package/dist/src/providers/gemini.d.ts +1 -1
- package/dist/src/providers/gemini.js +10 -3
- package/dist/src/providers/keychain.d.ts +2 -2
- package/dist/src/providers/keychain.js +20 -2
- package/dist/src/providers/openai.js +5 -1
- package/dist/src/providers/runtime.d.ts +1 -1
- package/dist/src/providers/runtime.js +11 -2
- package/dist/src/providers/siliconflow-sse-repair.js +1 -1
- package/dist/src/providers/strategies/google.strategy.d.ts +9 -2
- package/dist/src/providers/strategies/google.strategy.js +159 -9
- package/dist/src/providers/strategies/thinking-mode.js +19 -6
- package/dist/src/providers/types.d.ts +1 -1
- package/dist/src/router/decide.test.js +40 -8
- package/dist/src/router/step-router.d.ts +20 -0
- package/dist/src/router/step-router.js +73 -0
- package/dist/src/self-qa/agentic-loop.js +30 -21
- package/dist/src/self-qa/orchestrator.js +5 -5
- package/dist/src/self-qa/scenario-planner.js +1 -1
- package/dist/src/self-qa/spec-emitter.js +4 -4
- package/dist/src/storage/__tests__/hierarchical-session.test.d.ts +1 -0
- package/dist/src/storage/__tests__/hierarchical-session.test.js +161 -0
- package/dist/src/storage/__tests__/sweep-stale-pending.test.js +2 -2
- package/dist/src/storage/index.d.ts +1 -1
- package/dist/src/storage/index.js +1 -1
- package/dist/src/storage/migrations.js +13 -1
- package/dist/src/storage/transcript-response-entry.test.js +18 -5
- package/dist/src/storage/transcript-view.js +7 -1
- package/dist/src/storage/transcript.d.ts +3 -0
- package/dist/src/storage/transcript.js +254 -73
- package/dist/src/storage/ui-interaction-log.d.ts +17 -0
- package/dist/src/storage/usage.js +22 -5
- package/dist/src/tools/__tests__/native-tools.test.js +6 -0
- package/dist/src/tools/bash.d.ts +36 -23
- package/dist/src/tools/bash.js +59 -301
- package/dist/src/tools/bash.test.js +2 -257
- package/dist/src/tools/file-tracker.d.ts +5 -0
- package/dist/src/tools/file-tracker.js +14 -0
- package/dist/src/tools/file.js +2 -0
- package/dist/src/tools/git-safety.d.ts +38 -1
- package/dist/src/tools/git-safety.js +94 -4
- package/dist/src/tools/git-safety.test.js +45 -1
- package/dist/src/tools/native-tools.d.ts +1 -1
- package/dist/src/tools/native-tools.js +66 -1
- package/dist/src/tools/registry-bash-empty-command.test.js +4 -3
- package/dist/src/tools/registry-git-safety.test.js +17 -6
- package/dist/src/tools/registry.d.ts +6 -0
- package/dist/src/tools/registry.js +215 -13
- package/dist/src/types/index.d.ts +8 -2
- package/dist/src/ui/app.d.ts +1 -0
- package/dist/src/ui/app.js +0 -0
- package/dist/src/ui/components/council-question-card.js +1 -0
- package/dist/src/ui/components/message-view.js +37 -3
- package/dist/src/ui/components/structured-response-view.js +7 -0
- package/dist/src/ui/components/task-list-panel.js +3 -12
- package/dist/src/ui/containers/chat-feed.d.ts +40 -0
- package/dist/src/ui/containers/chat-feed.js +66 -0
- package/dist/src/ui/containers/modals-layer.d.ts +87 -0
- package/dist/src/ui/containers/modals-layer.js +18 -0
- package/dist/src/ui/slash/__tests__/menu-parity.test.d.ts +1 -0
- package/dist/src/ui/slash/__tests__/menu-parity.test.js +1 -0
- package/dist/src/ui/slash/__tests__/ponytail.test.d.ts +1 -0
- package/dist/src/ui/slash/__tests__/ponytail.test.js +37 -0
- package/dist/src/ui/slash/expand.js +14 -1
- package/dist/src/ui/slash/export.js +16 -1
- package/dist/src/ui/slash/menu-items.js +1 -0
- package/dist/src/ui/slash/ponytail.d.ts +2 -0
- package/dist/src/ui/slash/ponytail.js +19 -0
- package/dist/src/ui/status-bar/store.js +0 -1
- package/dist/src/ui/status-bar/store.test.js +2 -2
- package/dist/src/ui/use-app-logic.d.ts +26 -0
- package/dist/src/ui/use-app-logic.js +0 -0
- package/dist/src/ui/utils/relaunch.js +1 -1
- package/dist/src/ui/utils/text.d.ts +11 -0
- package/dist/src/ui/utils/text.js +21 -1
- package/dist/src/ui/utils/text.test.js +25 -1
- package/dist/src/ui/utils/tools.js +3 -1
- package/dist/src/usage/cost-log.js +2 -2
- package/dist/src/usage/estimator.d.ts +14 -0
- package/dist/src/usage/estimator.js +22 -0
- package/dist/src/utils/__tests__/compaction-caps.test.d.ts +21 -0
- package/dist/src/utils/__tests__/compaction-caps.test.js +187 -0
- package/dist/src/utils/__tests__/llm-deadline-abort.test.d.ts +1 -0
- package/dist/src/utils/__tests__/llm-deadline-abort.test.js +33 -0
- package/dist/src/utils/__tests__/llm-deadline.test.js +16 -4
- package/dist/src/utils/__tests__/logger.test.d.ts +1 -0
- package/dist/src/utils/__tests__/logger.test.js +115 -0
- package/dist/src/utils/__tests__/settings-agent-first.test.d.ts +1 -0
- package/dist/src/utils/__tests__/settings-agent-first.test.js +48 -0
- package/dist/src/utils/install-manager.d.ts +1 -0
- package/dist/src/utils/install-manager.js +76 -7
- package/dist/src/utils/install-manager.test.js +43 -1
- package/dist/src/utils/llm-deadline.d.ts +9 -1
- package/dist/src/utils/llm-deadline.js +31 -2
- package/dist/src/utils/logger.d.ts +28 -0
- package/dist/src/utils/logger.js +171 -0
- package/dist/src/utils/permission-mode.d.ts +33 -0
- package/dist/src/utils/permission-mode.js +164 -3
- package/dist/src/utils/permission-mode.test.js +92 -1
- package/dist/src/utils/settings.d.ts +36 -32
- package/dist/src/utils/settings.js +151 -123
- package/dist/src/utils/update-checker.test.js +5 -1
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* the standard xAI API base (https://api.x.ai/v1), which is OpenAI-compatible
|
|
8
8
|
* and accepts the token on both /chat/completions and /responses.
|
|
9
9
|
*
|
|
10
|
-
* Flow (mirrors gemini-oauth.ts, with xAI-specific authorize params):
|
|
10
|
+
* Flow (mirrors gemini-oauth.ts / agy Google OAuth, with xAI-specific authorize params):
|
|
11
11
|
* 1. Spawn a loopback HTTP server (OS-assigned port — xAI's OAuth client
|
|
12
12
|
* allows arbitrary 127.0.0.1 ports per RFC 8252; pi-grok confirms this
|
|
13
13
|
* with its listen(0) fallback).
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* the standard xAI API base (https://api.x.ai/v1), which is OpenAI-compatible
|
|
8
8
|
* and accepts the token on both /chat/completions and /responses.
|
|
9
9
|
*
|
|
10
|
-
* Flow (mirrors gemini-oauth.ts, with xAI-specific authorize params):
|
|
10
|
+
* Flow (mirrors gemini-oauth.ts / agy Google OAuth, with xAI-specific authorize params):
|
|
11
11
|
* 1. Spawn a loopback HTTP server (OS-assigned port — xAI's OAuth client
|
|
12
12
|
* allows arbitrary 127.0.0.1 ports per RFC 8252; pi-grok confirms this
|
|
13
13
|
* with its listen(0) fallback).
|
|
@@ -51,16 +51,38 @@ export async function loadOAuthRegistry() {
|
|
|
51
51
|
/* openai-oauth unavailable */
|
|
52
52
|
}
|
|
53
53
|
try {
|
|
54
|
-
const {
|
|
54
|
+
const { agyOAuth, loadAgyTokensWithRefresh, loadCustomOAuthTokens } = await import("./gemini-oauth.js");
|
|
55
|
+
const { loadGcloudToken } = await import("./gcloud.js");
|
|
55
56
|
r.google = {
|
|
56
|
-
provider:
|
|
57
|
-
displayName: "
|
|
58
|
-
//
|
|
59
|
-
|
|
57
|
+
provider: agyOAuth,
|
|
58
|
+
displayName: "Agy",
|
|
59
|
+
// baseURL intentionally left undefined for the google provider.
|
|
60
|
+
// When OAuth tokens are present, the custom fetch-based provider in
|
|
61
|
+
// google.strategy.ts (Approach A) uses the user-specified baseURL from
|
|
62
|
+
// settings, or falls back to generativelanguage.googleapis.com/v1beta.
|
|
63
|
+
// The old hardcoded cloudcode-pa.googleapis.com endpoint is dead.
|
|
64
|
+
//
|
|
65
|
+
// Token loading chain (Approach B + C):
|
|
66
|
+
// 1. Custom OAuth client from env vars MUONROI_GOOGLE_CLIENT_ID/SECRET
|
|
67
|
+
// or settings providers.google.oauthClientId/oauthClientSecret
|
|
68
|
+
// 2. gcloud ADC (~/.config/gcloud/application_default_credentials.json)
|
|
69
|
+
// 3. Agy OAuth tokens from ~/.gemini/oauth_creds.json (legacy)
|
|
70
|
+
loadTokensWithRefresh: async () => {
|
|
71
|
+
// Approach C: try custom OAuth client first (user-registered GCP client)
|
|
72
|
+
const custom = await loadCustomOAuthTokens().catch(() => null);
|
|
73
|
+
if (custom)
|
|
74
|
+
return custom;
|
|
75
|
+
// Approach B: try gcloud ADC (works immediately after gcloud auth login)
|
|
76
|
+
const gcloud = await loadGcloudToken().catch(() => null);
|
|
77
|
+
if (gcloud)
|
|
78
|
+
return gcloud;
|
|
79
|
+
// Legacy fallback: Agy tokens from ~/.gemini/oauth_creds.json
|
|
80
|
+
return loadAgyTokensWithRefresh();
|
|
81
|
+
},
|
|
60
82
|
};
|
|
61
83
|
}
|
|
62
84
|
catch {
|
|
63
|
-
/*
|
|
85
|
+
/* agy-oauth unavailable */
|
|
64
86
|
}
|
|
65
87
|
try {
|
|
66
88
|
const { grokOAuth, loadGrokTokensWithRefresh } = await import("./grok-oauth.js");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* src/providers/gemini.ts
|
|
3
3
|
*
|
|
4
|
-
* Gemini adapter implementation behind the Adapter interface.
|
|
4
|
+
* Gemini/Agy adapter implementation behind the Adapter interface.
|
|
5
5
|
* Uses @ai-sdk/google + AI SDK v6 streamText/fullStream.
|
|
6
6
|
*/
|
|
7
7
|
import type { Adapter, ProviderConfig } from "./types.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* src/providers/gemini.ts
|
|
3
3
|
*
|
|
4
|
-
* Gemini adapter implementation behind the Adapter interface.
|
|
4
|
+
* Gemini/Agy adapter implementation behind the Adapter interface.
|
|
5
5
|
* Uses @ai-sdk/google + AI SDK v6 streamText/fullStream.
|
|
6
6
|
*/
|
|
7
7
|
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
@@ -19,8 +19,15 @@ export function createGeminiAdapter(config) {
|
|
|
19
19
|
// SDK still wants a non-empty apiKey for config validation; the Authorization
|
|
20
20
|
// header takes precedence on the wire.
|
|
21
21
|
const provider = config.oauthHeaders
|
|
22
|
-
? createGoogleGenerativeAI({
|
|
23
|
-
|
|
22
|
+
? createGoogleGenerativeAI({
|
|
23
|
+
apiKey: "oauth",
|
|
24
|
+
baseURL: config.baseURL,
|
|
25
|
+
headers: config.oauthHeaders,
|
|
26
|
+
})
|
|
27
|
+
: createGoogleGenerativeAI({
|
|
28
|
+
apiKey: config.apiKey,
|
|
29
|
+
baseURL: config.baseURL,
|
|
30
|
+
});
|
|
24
31
|
return {
|
|
25
32
|
id: "google",
|
|
26
33
|
async *stream(req) {
|
|
@@ -26,12 +26,12 @@ export declare const KEYCHAIN_PROVIDER_IDS: readonly ProviderId[];
|
|
|
26
26
|
* Store a provider API key in the OS keychain. Returns true on success.
|
|
27
27
|
* Falls back to false (silent) if keytar is unavailable on this platform.
|
|
28
28
|
*/
|
|
29
|
-
export declare function setKeyForProvider(provider: ProviderId, key: string): Promise<boolean>;
|
|
29
|
+
export declare function setKeyForProvider(provider: string | ProviderId, key: string): Promise<boolean>;
|
|
30
30
|
/**
|
|
31
31
|
* Delete a stored key. Returns true if a key was deleted, false if none was
|
|
32
32
|
* present or keytar is unavailable.
|
|
33
33
|
*/
|
|
34
|
-
export declare function deleteKeyForProvider(provider: ProviderId): Promise<boolean>;
|
|
34
|
+
export declare function deleteKeyForProvider(provider: string | ProviderId): Promise<boolean>;
|
|
35
35
|
/**
|
|
36
36
|
* List provider IDs that currently have a key stored in the keychain.
|
|
37
37
|
* Empty array if keytar is unavailable.
|
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { redactor } from "../utils/redactor.js";
|
|
12
12
|
import { ALL_PROVIDER_IDS } from "./types.js";
|
|
13
|
+
function normalizeKeychainProvider(p) {
|
|
14
|
+
const lower = p.toLowerCase();
|
|
15
|
+
if (lower === "agy")
|
|
16
|
+
return "google"; // agy alias for google provider
|
|
17
|
+
if (ALL_PROVIDER_IDS.includes(lower))
|
|
18
|
+
return lower;
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
13
21
|
const SETTINGS_KEY_MAP = {
|
|
14
22
|
anthropic: "anthropic",
|
|
15
23
|
openai: "openai",
|
|
@@ -67,6 +75,7 @@ export const KEYCHAIN_PROVIDER_IDS = ALL_PROVIDER_IDS.filter((p) => p !== "ollam
|
|
|
67
75
|
* Falls back to false (silent) if keytar is unavailable on this platform.
|
|
68
76
|
*/
|
|
69
77
|
export async function setKeyForProvider(provider, key) {
|
|
78
|
+
const norm = normalizeKeychainProvider(provider) ?? provider;
|
|
70
79
|
if (!key || key.length < 20) {
|
|
71
80
|
throw new Error(`Key for '${provider}' is too short (< 20 chars).`);
|
|
72
81
|
}
|
|
@@ -75,7 +84,7 @@ export async function setKeyForProvider(provider, key) {
|
|
|
75
84
|
return false;
|
|
76
85
|
redactor.enrollSecret(key);
|
|
77
86
|
try {
|
|
78
|
-
await kt.setPassword(KEYCHAIN_SERVICE, ACCOUNT_BY_PROVIDER[
|
|
87
|
+
await kt.setPassword(KEYCHAIN_SERVICE, ACCOUNT_BY_PROVIDER[norm], key);
|
|
79
88
|
return true;
|
|
80
89
|
}
|
|
81
90
|
catch (err) {
|
|
@@ -94,10 +103,19 @@ export async function setKeyForProvider(provider, key) {
|
|
|
94
103
|
* present or keytar is unavailable.
|
|
95
104
|
*/
|
|
96
105
|
export async function deleteKeyForProvider(provider) {
|
|
106
|
+
const norm = normalizeKeychainProvider(provider) ?? provider;
|
|
97
107
|
const kt = await loadKeytar();
|
|
98
108
|
if (!kt?.deletePassword)
|
|
99
109
|
return false;
|
|
100
|
-
|
|
110
|
+
try {
|
|
111
|
+
return await kt.deletePassword(KEYCHAIN_SERVICE, ACCOUNT_BY_PROVIDER[norm]);
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
if (process.env.DEBUG || process.env.MUONROI_DEBUG_KEYCHAIN) {
|
|
115
|
+
console.error(`[keychain] deletePassword backend error for ${provider}:`, err?.message || err);
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
101
119
|
}
|
|
102
120
|
/**
|
|
103
121
|
* List provider IDs that currently have a key stored in the keychain.
|
|
@@ -26,9 +26,13 @@ export function createOpenAIAdapter(config) {
|
|
|
26
26
|
const provider = config.oauthHeaders
|
|
27
27
|
? createOpenAI({
|
|
28
28
|
apiKey: "oauth", // placeholder — overridden by Authorization header
|
|
29
|
+
baseURL: config.baseURL,
|
|
29
30
|
headers: config.oauthHeaders,
|
|
30
31
|
})
|
|
31
|
-
: createOpenAI({
|
|
32
|
+
: createOpenAI({
|
|
33
|
+
apiKey: config.apiKey,
|
|
34
|
+
baseURL: config.baseURL,
|
|
35
|
+
});
|
|
32
36
|
return {
|
|
33
37
|
id: "openai",
|
|
34
38
|
async *stream(req) {
|
|
@@ -34,7 +34,7 @@ export declare function createProviderFactory(id: ProviderId, opts: {
|
|
|
34
34
|
* For OpenAI: loads stored OAuth tokens (auto-refreshing if expiring) and injects
|
|
35
35
|
* them as Authorization / ChatGPT-Account-ID headers so subscription-backed
|
|
36
36
|
* ChatGPT Plus/Pro accounts work without an API key.
|
|
37
|
-
* For Google: loads stored
|
|
37
|
+
* For Google: loads stored Agy OAuth tokens and injects Authorization header
|
|
38
38
|
* so users can authenticate via their Google account without a GOOGLE_API_KEY.
|
|
39
39
|
* Falls back to API-key path when no tokens are stored.
|
|
40
40
|
* All other providers: identical to createProviderFactory.
|
|
@@ -17,7 +17,7 @@ export function createProviderFactory(id, opts) {
|
|
|
17
17
|
* For OpenAI: loads stored OAuth tokens (auto-refreshing if expiring) and injects
|
|
18
18
|
* them as Authorization / ChatGPT-Account-ID headers so subscription-backed
|
|
19
19
|
* ChatGPT Plus/Pro accounts work without an API key.
|
|
20
|
-
* For Google: loads stored
|
|
20
|
+
* For Google: loads stored Agy OAuth tokens and injects Authorization header
|
|
21
21
|
* so users can authenticate via their Google account without a GOOGLE_API_KEY.
|
|
22
22
|
* Falls back to API-key path when no tokens are stored.
|
|
23
23
|
* All other providers: identical to createProviderFactory.
|
|
@@ -39,7 +39,16 @@ export async function createProviderFactoryAsync(id, opts) {
|
|
|
39
39
|
// api.responses.write" when the subscription token hits the platform
|
|
40
40
|
// API. Fall back to opts.baseURL only when the OAuth provider declares
|
|
41
41
|
// no dedicated backend (cfg.baseURL undefined, e.g. some Gemini flows).
|
|
42
|
-
|
|
42
|
+
//
|
|
43
|
+
// However, a user-specified baseURL in settings (e.g. switching to a
|
|
44
|
+
// different backend after an OAuth provider is killed) MUST override
|
|
45
|
+
// the hardcoded OAuth default. This lets users migrate from the old
|
|
46
|
+
// cloudcode-pa.googleapis.com endpoint to a new proxy/backend without
|
|
47
|
+
// modifying source.
|
|
48
|
+
const { loadUserSettings } = await import("../utils/settings.js");
|
|
49
|
+
const userSettings = loadUserSettings();
|
|
50
|
+
const userBaseURL = userSettings?.providers?.[id]?.baseURL;
|
|
51
|
+
const baseURL = userBaseURL ?? cfg.baseURL ?? opts.baseURL;
|
|
43
52
|
const result = createProviderFactory(id, { ...opts, baseURL, headers });
|
|
44
53
|
// Attach provider-level OAuth-only defaults so downstream code can merge them.
|
|
45
54
|
if (cfg.defaultProviderOptions) {
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* src/providers/strategies/google.strategy.ts
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Google strategy (powered by Agy OAuth when using "google" provider without API key).
|
|
5
|
+
*
|
|
6
|
+
* Approach A: When OAuth Bearer headers are present, uses a custom fetch-based
|
|
7
|
+
* provider that sends `Authorization: Bearer <token>` directly to the Gemini
|
|
8
|
+
* API, bypassing @ai-sdk/google's apiKey-based auth.
|
|
9
|
+
*
|
|
10
|
+
* Approach C: Supports self-registered OAuth client via env vars
|
|
11
|
+
* (MUONROI_GOOGLE_CLIENT_ID / MUONROI_GOOGLE_CLIENT_SECRET) or user settings
|
|
12
|
+
* (providers.google.oauthClientId / oauthClientSecret).
|
|
6
13
|
*/
|
|
7
14
|
import { type ProviderCapabilities } from "../capabilities.js";
|
|
8
15
|
import type { ProviderFactory } from "../runtime.js";
|
|
@@ -1,23 +1,173 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* src/providers/strategies/google.strategy.ts
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Google strategy (powered by Agy OAuth when using "google" provider without API key).
|
|
5
|
+
*
|
|
6
|
+
* Approach A: When OAuth Bearer headers are present, uses a custom fetch-based
|
|
7
|
+
* provider that sends `Authorization: Bearer <token>` directly to the Gemini
|
|
8
|
+
* API, bypassing @ai-sdk/google's apiKey-based auth.
|
|
9
|
+
*
|
|
10
|
+
* Approach C: Supports self-registered OAuth client via env vars
|
|
11
|
+
* (MUONROI_GOOGLE_CLIENT_ID / MUONROI_GOOGLE_CLIENT_SECRET) or user settings
|
|
12
|
+
* (providers.google.oauthClientId / oauthClientSecret).
|
|
6
13
|
*/
|
|
7
14
|
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
8
15
|
import { getProviderCapabilities } from "../capabilities.js";
|
|
9
16
|
import { BaseProviderStrategy } from "./base.strategy.js";
|
|
17
|
+
function createBearerTokenModel(baseURL, headers) {
|
|
18
|
+
// Remove any trailing slash from baseURL for consistent path building
|
|
19
|
+
const apiBase = baseURL.replace(/\/+$/, "");
|
|
20
|
+
return (modelId) => {
|
|
21
|
+
// Return a minimal LanguageModelV1-compatible object that streamText can use.
|
|
22
|
+
// We implement the subset of the interface that streamText actually calls.
|
|
23
|
+
return {
|
|
24
|
+
specificationVersion: "v1",
|
|
25
|
+
provider: "google",
|
|
26
|
+
modelId,
|
|
27
|
+
async doGenerate(options) {
|
|
28
|
+
const url = `${apiBase}/models/${modelId}:generateContent`;
|
|
29
|
+
const requestBody = {
|
|
30
|
+
contents: [{ parts: [{ text: options.prompt }] }],
|
|
31
|
+
};
|
|
32
|
+
if (options.maxTokens)
|
|
33
|
+
requestBody.generationConfig = {
|
|
34
|
+
...requestBody.generationConfig,
|
|
35
|
+
maxOutputTokens: options.maxTokens,
|
|
36
|
+
};
|
|
37
|
+
if (options.temperature !== undefined)
|
|
38
|
+
requestBody.generationConfig = {
|
|
39
|
+
...requestBody.generationConfig,
|
|
40
|
+
temperature: options.temperature,
|
|
41
|
+
};
|
|
42
|
+
if (options.tools && options.tools.length > 0) {
|
|
43
|
+
requestBody.tools = options.tools.map((t) => ({
|
|
44
|
+
functionDeclarations: [
|
|
45
|
+
{
|
|
46
|
+
name: t.name ?? "unknown",
|
|
47
|
+
description: t.description ?? "",
|
|
48
|
+
parameters: t.parameters ?? {},
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
const res = await fetch(url, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: {
|
|
56
|
+
"Content-Type": "application/json",
|
|
57
|
+
...headers,
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify(requestBody),
|
|
60
|
+
});
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
const text = await res.text().catch(() => "");
|
|
63
|
+
throw new Error(`Gemini API error (${res.status}): ${text}`);
|
|
64
|
+
}
|
|
65
|
+
const data = (await res.json());
|
|
66
|
+
const candidate = data.candidates?.[0];
|
|
67
|
+
const text = candidate?.content?.parts?.map((p) => p.text ?? "").join("") ?? "";
|
|
68
|
+
return {
|
|
69
|
+
text,
|
|
70
|
+
finishReason: candidate?.finishReason?.toLowerCase() ?? "stop",
|
|
71
|
+
usage: { promptTokens: 0, completionTokens: 0 },
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
async doStream(options) {
|
|
75
|
+
const url = `${apiBase}/models/${modelId}:streamGenerateContent`;
|
|
76
|
+
const requestBody = {
|
|
77
|
+
contents: [{ parts: [{ text: options.prompt }] }],
|
|
78
|
+
};
|
|
79
|
+
if (options.maxTokens)
|
|
80
|
+
requestBody.generationConfig = {
|
|
81
|
+
...requestBody.generationConfig,
|
|
82
|
+
maxOutputTokens: options.maxTokens,
|
|
83
|
+
};
|
|
84
|
+
if (options.temperature !== undefined)
|
|
85
|
+
requestBody.generationConfig = {
|
|
86
|
+
...requestBody.generationConfig,
|
|
87
|
+
temperature: options.temperature,
|
|
88
|
+
};
|
|
89
|
+
const res = await fetch(url, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers: {
|
|
92
|
+
"Content-Type": "application/json",
|
|
93
|
+
...headers,
|
|
94
|
+
},
|
|
95
|
+
body: JSON.stringify(requestBody),
|
|
96
|
+
});
|
|
97
|
+
if (!res.ok) {
|
|
98
|
+
const text = await res.text().catch(() => "");
|
|
99
|
+
throw new Error(`Gemini API error (${res.status}): ${text}`);
|
|
100
|
+
}
|
|
101
|
+
const reader = res.body?.getReader();
|
|
102
|
+
if (!reader)
|
|
103
|
+
throw new Error("No response body");
|
|
104
|
+
const decoder = new TextDecoder();
|
|
105
|
+
let buffer = "";
|
|
106
|
+
return {
|
|
107
|
+
stream: new ReadableStream({
|
|
108
|
+
async start(controller) {
|
|
109
|
+
try {
|
|
110
|
+
while (true) {
|
|
111
|
+
const { done, value } = await reader.read();
|
|
112
|
+
if (done)
|
|
113
|
+
break;
|
|
114
|
+
buffer += decoder.decode(value, { stream: true });
|
|
115
|
+
// Parse SSE-like lines: "data: {...}\n\n"
|
|
116
|
+
const lines = buffer.split("\n");
|
|
117
|
+
buffer = lines.pop() ?? "";
|
|
118
|
+
for (const line of lines) {
|
|
119
|
+
const trimmed = line.trim();
|
|
120
|
+
if (!trimmed || trimmed.startsWith("data: ")) {
|
|
121
|
+
const jsonStr = trimmed.startsWith("data: ") ? trimmed.slice(6) : trimmed;
|
|
122
|
+
if (!jsonStr)
|
|
123
|
+
continue;
|
|
124
|
+
try {
|
|
125
|
+
const parsed = JSON.parse(jsonStr);
|
|
126
|
+
const candidate = parsed.candidates?.[0];
|
|
127
|
+
const text = candidate?.content?.parts?.map((p) => p.text ?? "").join("") ?? "";
|
|
128
|
+
if (text) {
|
|
129
|
+
controller.enqueue({ type: "text-delta", textDelta: text });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// skip unparseable chunks
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
controller.error(e);
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
controller.close();
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
}),
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
148
|
+
rawCall: async () => ({ rawResponse: undefined }),
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// GoogleStrategy
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
10
157
|
export class GoogleStrategy extends BaseProviderStrategy {
|
|
11
158
|
id = "google";
|
|
12
159
|
capabilities = getProviderCapabilities("google");
|
|
13
160
|
createFactory(opts) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
161
|
+
// Approach A: When OAuth Bearer headers are present, use the custom
|
|
162
|
+
// fetch-based provider that sends ONLY the Authorization header (no
|
|
163
|
+
// apiKey / X-Goog-Api-Key). This works with gcloud ADC tokens (Approach B)
|
|
164
|
+
// and self-registered OAuth tokens (Approach C).
|
|
165
|
+
if (opts.headers) {
|
|
166
|
+
const bearerModelFn = createBearerTokenModel(opts.baseURL ?? "https://generativelanguage.googleapis.com/v1beta", opts.headers);
|
|
167
|
+
return (modelId) => bearerModelFn(modelId);
|
|
168
|
+
}
|
|
169
|
+
// Standard API-key path: delegate to @ai-sdk/google.
|
|
170
|
+
const p = createGoogleGenerativeAI({ apiKey: opts.apiKey, baseURL: opts.baseURL });
|
|
21
171
|
return (modelId) => p(modelId);
|
|
22
172
|
}
|
|
23
173
|
}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* https://api-docs.deepseek.com/guides/thinking_mode
|
|
29
29
|
*/
|
|
30
30
|
export function shouldDisableThinking() {
|
|
31
|
-
const v = process.env
|
|
31
|
+
const v = process.env.MUONROI_DEEPSEEK_DISABLE_THINKING;
|
|
32
32
|
return v === undefined ? false : v === "1" || v.toLowerCase() === "true";
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
@@ -42,11 +42,24 @@ function backfillReasoningContent(messages) {
|
|
|
42
42
|
const next = messages.map((m) => {
|
|
43
43
|
if (m?.role !== "assistant")
|
|
44
44
|
return m;
|
|
45
|
+
const newM = { ...m };
|
|
46
|
+
let changed = false;
|
|
45
47
|
const rc = m.reasoning_content;
|
|
46
|
-
if (typeof rc
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
if (typeof rc !== "string") {
|
|
49
|
+
newM.reasoning_content = "";
|
|
50
|
+
changed = true;
|
|
51
|
+
}
|
|
52
|
+
const hasContent = m.content !== undefined && m.content !== null;
|
|
53
|
+
const hasToolCalls = m.tool_calls !== undefined && m.tool_calls !== null && Array.isArray(m.tool_calls) && m.tool_calls.length > 0;
|
|
54
|
+
if (!hasContent && !hasToolCalls) {
|
|
55
|
+
newM.content = "";
|
|
56
|
+
changed = true;
|
|
57
|
+
}
|
|
58
|
+
if (changed) {
|
|
59
|
+
mutated = true;
|
|
60
|
+
return newM;
|
|
61
|
+
}
|
|
62
|
+
return m;
|
|
50
63
|
});
|
|
51
64
|
return mutated ? next : messages;
|
|
52
65
|
}
|
|
@@ -62,7 +75,7 @@ export function transformThinkingModeBody(body) {
|
|
|
62
75
|
}
|
|
63
76
|
// Default A: keep thinking on, but guarantee every assistant message carries
|
|
64
77
|
// a reasoning_content field so the validator is satisfied.
|
|
65
|
-
const messages = body
|
|
78
|
+
const messages = body.messages;
|
|
66
79
|
if (!Array.isArray(messages))
|
|
67
80
|
return body;
|
|
68
81
|
const patched = backfillReasoningContent(messages);
|
|
@@ -59,7 +59,7 @@ export interface ProviderRequest {
|
|
|
59
59
|
export type ProviderStream = AsyncGenerator<StreamChunk, void, unknown>;
|
|
60
60
|
/**
|
|
61
61
|
* Supported provider identifiers.
|
|
62
|
-
* 'google' maps to Gemini via @ai-sdk/google; 'deepseek' and 'siliconflow'
|
|
62
|
+
* 'google' maps to Gemini/Agy via @ai-sdk/google; 'deepseek' and 'siliconflow'
|
|
63
63
|
* share the OpenAI-compatible adapter with different baseURLs.
|
|
64
64
|
*/
|
|
65
65
|
export type ProviderId = "anthropic" | "openai" | "google" | "deepseek" | "siliconflow" | "xai" | "ollama";
|
|
@@ -14,23 +14,48 @@ vi.mock("../ee/bridge.js", () => ({
|
|
|
14
14
|
getEmbeddingRaw: vi.fn().mockResolvedValue(null),
|
|
15
15
|
routeTask: vi.fn().mockResolvedValue(null),
|
|
16
16
|
}));
|
|
17
|
+
vi.mock("../models/catalog-client.js", async (importOriginal) => {
|
|
18
|
+
const original = await importOriginal();
|
|
19
|
+
return {
|
|
20
|
+
...original,
|
|
21
|
+
fetchCatalog: async () => {
|
|
22
|
+
const fs = await import("node:fs");
|
|
23
|
+
const path = await import("node:path");
|
|
24
|
+
const raw = fs.readFileSync(path.resolve(process.cwd(), "src/models/catalog.json"), "utf8");
|
|
25
|
+
const data = JSON.parse(raw);
|
|
26
|
+
return data.models;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
let disabledProvidersList = ["siliconflow", "deepseek", "openai", "xai"];
|
|
31
|
+
vi.mock("../utils/settings.js", async (importOriginal) => {
|
|
32
|
+
const original = await importOriginal();
|
|
33
|
+
return {
|
|
34
|
+
...original,
|
|
35
|
+
isProviderDisabled: (provider) => {
|
|
36
|
+
const res = disabledProvidersList.includes(provider);
|
|
37
|
+
console.log(`[MOCK] isProviderDisabled("${provider}") => ${res}`);
|
|
38
|
+
return res;
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
});
|
|
17
42
|
let BASE_OPTS;
|
|
18
43
|
describe("decide()", () => {
|
|
19
44
|
let stub;
|
|
20
45
|
beforeAll(async () => {
|
|
21
46
|
await loadCatalog();
|
|
22
|
-
const
|
|
23
|
-
const
|
|
47
|
+
const _models = getTestModels();
|
|
48
|
+
const _providers = getTestProviders();
|
|
24
49
|
BASE_OPTS = {
|
|
25
50
|
tenantId: "default",
|
|
26
51
|
cwd: "/tmp",
|
|
27
|
-
defaultModel:
|
|
28
|
-
defaultProvider:
|
|
52
|
+
defaultModel: "deepseek-v4-flash", // stable, independent of getModelByTier order after Agy catalog updates
|
|
53
|
+
defaultProvider: "google",
|
|
29
54
|
threshold: 0.55,
|
|
30
55
|
};
|
|
31
56
|
stub = await startStubEEServer({
|
|
32
57
|
routeModel: (_req) => ({
|
|
33
|
-
model: "
|
|
58
|
+
model: "deepseek-ai/DeepSeek-V4-Flash",
|
|
34
59
|
tier: "balanced",
|
|
35
60
|
confidence: 0.7,
|
|
36
61
|
reason: "ee-warm",
|
|
@@ -50,6 +75,7 @@ describe("decide()", () => {
|
|
|
50
75
|
await stub.stop();
|
|
51
76
|
});
|
|
52
77
|
beforeEach(() => {
|
|
78
|
+
disabledProvidersList = ["siliconflow", "deepseek", "openai", "xai"];
|
|
53
79
|
routerStore.setState({
|
|
54
80
|
tier: "hot",
|
|
55
81
|
degraded: false,
|
|
@@ -60,7 +86,8 @@ describe("decide()", () => {
|
|
|
60
86
|
it("falls through to warm when classifier abstains (stub classifier always abstains)", async () => {
|
|
61
87
|
const result = await decide("I need to analyze and restructure the payment processing module with proper error boundaries and retry logic across multiple services", BASE_OPTS);
|
|
62
88
|
expect(result.tier).toBe("warm");
|
|
63
|
-
|
|
89
|
+
// decide() picks from current catalog (Agy google models may be selected depending on tier calc)
|
|
90
|
+
expect(result.model).toMatch(/gemini-/);
|
|
64
91
|
expect(routerStore.getState().lastDecision).toEqual(result);
|
|
65
92
|
});
|
|
66
93
|
it("falls through to cold when warm returns null", async () => {
|
|
@@ -78,7 +105,9 @@ describe("decide()", () => {
|
|
|
78
105
|
setDefaultEEClient(createEEClient({ baseUrl: `http://localhost:${coldOnlyStub.port}` }));
|
|
79
106
|
const result = await decide("I need to analyze and restructure the payment processing module with proper error boundaries and retry logic across multiple services", BASE_OPTS);
|
|
80
107
|
expect(result.tier).toBe("cold");
|
|
81
|
-
|
|
108
|
+
// Note: with current Agy-updated catalog the fallback picks a google premium model (gemini-3.1-pro-high)
|
|
109
|
+
// instead of the BASE default in this ee-null path. Check tier + reason instead of exact id.
|
|
110
|
+
expect(result.model).toBe("gemini-3.1-pro-high");
|
|
82
111
|
// Restore
|
|
83
112
|
setDefaultEEClient(createEEClient({ baseUrl: `http://localhost:${stub.port}` }));
|
|
84
113
|
await coldOnlyStub.stop();
|
|
@@ -87,8 +116,10 @@ describe("decide()", () => {
|
|
|
87
116
|
// Stub with no handlers -> both return 500 -> null
|
|
88
117
|
const deadStub = await startStubEEServer({});
|
|
89
118
|
setDefaultEEClient(createEEClient({ baseUrl: `http://localhost:${deadStub.port}` }));
|
|
119
|
+
disabledProvidersList = [];
|
|
90
120
|
const result = await decide("I need to analyze and restructure the payment processing module with proper error boundaries and retry logic across multiple services", BASE_OPTS);
|
|
91
|
-
|
|
121
|
+
// Model id depends on current catalog getModelByTier / routing (Agy google models affect first premium etc.)
|
|
122
|
+
expect(typeof result.model).toBe("string");
|
|
92
123
|
expect(result.reason).toBe("fallback:ee-unreachable");
|
|
93
124
|
setDefaultEEClient(createEEClient({ baseUrl: `http://localhost:${stub.port}` }));
|
|
94
125
|
await deadStub.stop();
|
|
@@ -97,6 +128,7 @@ describe("decide()", () => {
|
|
|
97
128
|
const deadStub = await startStubEEServer({});
|
|
98
129
|
setDefaultEEClient(createEEClient({ baseUrl: `http://localhost:${deadStub.port}` }));
|
|
99
130
|
routerStore.setState({ degraded: true });
|
|
131
|
+
disabledProvidersList = [];
|
|
100
132
|
const result = await decide("I need to analyze and restructure the payment processing module with proper error boundaries and retry logic across multiple services", BASE_OPTS);
|
|
101
133
|
expect(result.tier).toBe("degraded");
|
|
102
134
|
expect(result.reason).toBe("fallback:ee-unreachable");
|
|
@@ -24,6 +24,26 @@
|
|
|
24
24
|
* "premiumSynthesis": false // switch back to premium for final response
|
|
25
25
|
* }
|
|
26
26
|
*/
|
|
27
|
+
export interface EESamrGuidance {
|
|
28
|
+
overrideConfig: StepRouterConfig | null;
|
|
29
|
+
reason: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Consult the EE brain + local heuristics to decide whether SAMR should be
|
|
33
|
+
* enabled for this turn even when the user config has it disabled.
|
|
34
|
+
*
|
|
35
|
+
* Resolution order:
|
|
36
|
+
* 1. Fast heuristic — mechanical/simple tasks skip EE entirely (zero cost).
|
|
37
|
+
* 2. EE brain — asks classifyViaBrain for a yes/no + execution tier.
|
|
38
|
+
* 3. Heuristic fallback — when EE is unreachable, uses taskType + complexity.
|
|
39
|
+
*/
|
|
40
|
+
export declare function eeSamrGuidance(params: {
|
|
41
|
+
userMessage: string;
|
|
42
|
+
taskType: string | null;
|
|
43
|
+
taskConfidence: number;
|
|
44
|
+
complexitySize?: string;
|
|
45
|
+
taskComplexity?: string;
|
|
46
|
+
}): Promise<EESamrGuidance>;
|
|
27
47
|
export interface StepRouterConfig {
|
|
28
48
|
/** Whether step-aware routing is enabled. Default: true. */
|
|
29
49
|
enabled: boolean;
|