mixdog 1.1.2 → 1.2.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 +1 -0
- package/package.json +2 -1
- package/scripts/lib/run-node-tests.mjs +9 -0
- package/scripts/prune-desktop-runtime.mjs +3 -0
- package/scripts/test-direct.mjs +4 -2
- package/src/defaults/shipped-history.json +6 -0
- package/src/defaults/skills/setup/references/actions.md +5 -2
- package/src/defaults/skills/visualize/SKILL.md +157 -0
- package/src/defaults/skills/visualize/references/craft.md +88 -0
- package/src/headless-command.mjs +56 -3
- package/src/headless-exec.mjs +17 -1
- package/src/help.mjs +2 -0
- package/src/rules/shared/70-delivery.md +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +17 -11
- package/src/runtime/agent/orchestrator/config-storage.mjs +1 -0
- package/src/runtime/agent/orchestrator/config.mjs +9 -1
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +47 -7
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +51 -8
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +25 -5
- package/src/runtime/agent/orchestrator/mcp/client.mjs +348 -122
- package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +13 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +12 -4
- package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +8 -40
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +51 -12
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +37 -2
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +67 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +521 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +251 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +69 -102
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +15 -1
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +10 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +36 -3
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +12 -11
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +11 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -3
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +18 -11
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +362 -0
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +253 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/diagnostic.mjs +1 -7
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/refusal.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +17 -7
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -16
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +123 -0
- package/src/runtime/agent/orchestrator/session/message-ids.test-support.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +29 -19
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +23 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +28 -8
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +140 -0
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +52 -2
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +32 -22
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +10 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +31 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +11 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +9 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +80 -66
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +5 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +16 -7
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +137 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +11 -10
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +21 -11
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +56 -13
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +58 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
- package/src/runtime/attachments/store.mjs +38 -0
- package/src/runtime/shared/atomic-file.mjs +8 -2
- package/src/runtime/shared/best-effort-stderr.mjs +8 -0
- package/src/runtime/shared/code-graph-state.mjs +12 -0
- package/src/runtime/shared/defaults-separation.mjs +2 -0
- package/src/runtime/shared/git-runner-worker.mjs +3 -2
- package/src/runtime/shared/git-runner.mjs +4 -3
- package/src/runtime/shared/llm/default-context-window.mjs +6 -0
- package/src/runtime/shared/llm/model-catalog-projection.mjs +5 -4
- package/src/runtime/shared/llm/model-catalog.mjs +240 -118
- package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +156 -0
- package/src/runtime/shared/llm/provider-seed-rosters.mjs +130 -0
- package/src/runtime/shared/model-proxy-mode.d.mts +3 -0
- package/src/runtime/shared/model-proxy-mode.mjs +19 -0
- package/src/runtime/shared/provider-accounts.mjs +2 -2
- package/src/runtime/shared/provider-api-key.mjs +21 -1
- package/src/runtime/shared/schedule-model-ref.mjs +11 -2
- package/src/runtime/shared/session-usage-totals.mjs +19 -0
- package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
- package/src/runtime/shared/singleton-owner.mjs +36 -6
- package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
- package/src/runtime/shared/tool-surface.mjs +7 -0
- package/src/runtime/shared/tool-work-units.mjs +14 -0
- package/src/runtime/shared/transcript-metadata.mjs +17 -1
- package/src/runtime/shared/turn-changes.mjs +791 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +346 -12
- package/src/runtime/shared/turn-snapshot.mjs +690 -81
- package/src/runtime/shared/turn-worktree-snapshot.mjs +161 -11
- package/src/runtime/shared/worktrees/cache-cleanups.mjs +52 -0
- package/src/runtime/shared/worktrees/contract.mjs +551 -0
- package/src/runtime/shared/worktrees/disk.mjs +57 -0
- package/src/runtime/shared/worktrees/git.mjs +247 -0
- package/src/runtime/shared/worktrees/include.mjs +480 -0
- package/src/runtime/shared/worktrees/lock.mjs +46 -0
- package/src/runtime/shared/worktrees/maintenance.mjs +96 -0
- package/src/runtime/shared/worktrees/manager.mjs +1215 -0
- package/src/runtime/shared/worktrees/merge.mjs +182 -0
- package/src/runtime/shared/worktrees/owner.mjs +46 -0
- package/src/runtime/shared/worktrees/paths.mjs +62 -0
- package/src/runtime/shared/worktrees/registry.mjs +110 -0
- package/src/runtime/shared/worktrees/settings.mjs +84 -0
- package/src/runtime/shared/worktrees/slug.mjs +62 -0
- package/src/runtime/shared/worktrees/test-support.mjs +96 -0
- package/src/runtime/visualize-bridge/client.mjs +181 -0
- package/src/runtime/visualize-bridge/tool-defs.mjs +97 -0
- package/src/session-runtime/boot/apis.mjs +35 -0
- package/src/session-runtime/boot/config.mjs +2 -0
- package/src/session-runtime/boot/facade.mjs +20 -0
- package/src/session-runtime/boot/infrastructure.mjs +1 -0
- package/src/session-runtime/boot/session.mjs +1 -0
- package/src/session-runtime/boot/tools.mjs +4 -0
- package/src/session-runtime/config-lifecycle.mjs +10 -1
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +11 -1
- package/src/session-runtime/global-extensions.mjs +2 -2
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +27 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +32 -1
- package/src/session-runtime/internal-tool-executor.mjs +9 -1
- package/src/session-runtime/lifecycle/session-catalog.mjs +8 -1
- package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +5 -0
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +21 -3
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +16 -0
- package/src/session-runtime/mcp-glue/connect.mjs +11 -6
- package/src/session-runtime/mcp-glue/server-catalog.mjs +7 -3
- package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
- package/src/session-runtime/model-route/route-persist.mjs +3 -0
- package/src/session-runtime/model-route/set-route.mjs +15 -2
- package/src/session-runtime/notification-bus.mjs +9 -1
- package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
- package/src/session-runtime/resource-extension-sync.mjs +30 -7
- package/src/session-runtime/resource-mcp-api.mjs +20 -7
- package/src/session-runtime/route-resolution.mjs +10 -1
- package/src/session-runtime/route-state.mjs +16 -2
- package/src/session-runtime/runtime-core.mjs +5 -0
- package/src/session-runtime/runtime-facade.mjs +8 -0
- package/src/session-runtime/runtime-review-api.mjs +9 -0
- package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
- package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
- package/src/session-runtime/services/agent-tool.mjs +1 -0
- package/src/session-runtime/services/daemon-candidate.mjs +3 -2
- package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
- package/src/session-runtime/session-create/session-options.mjs +10 -1
- package/src/session-runtime/session-turn-api.mjs +1 -0
- package/src/session-runtime/settings-api.mjs +4 -2
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -2
- package/src/session-runtime/settings-worktree-api.mjs +17 -0
- package/src/session-runtime/setup-tool/executor.mjs +11 -4
- package/src/session-runtime/setup-tool/settings-contract.mjs +3 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +31 -3
- package/src/session-runtime/turn/agent-ops.mjs +12 -1
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +13 -3
- package/src/session-runtime/turn/turn-run/snapshot-tracker.mjs +20 -15
- package/src/session-runtime/turn/turn-run/turn-open.mjs +4 -5
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +29 -1
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +6 -1
- package/src/session-runtime/turn/turn-run.mjs +6 -7
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
- package/src/session-runtime/workspace/admission-gate.mjs +197 -0
- package/src/session-runtime/workspace/project-branches.mjs +136 -0
- package/src/session-runtime/workspace/transition-steps.mjs +364 -0
- package/src/session-runtime/workspace/transitions.mjs +651 -0
- package/src/session-runtime/workspace/workspace-state.mjs +112 -0
- package/src/standalone/channel-transport/http-routes.mjs +17 -0
- package/src/standalone/channel-transport.mjs +13 -1
- package/src/standalone/daemon-desktop-runtime.mjs +1 -0
- package/src/standalone/daemon-owner.mjs +372 -0
- package/src/standalone/daemon-stored-session-views.mjs +4 -1
- package/src/standalone/daemon.mjs +139 -5
- package/src/standalone/desktop-service-registry.mjs +20 -0
- package/src/standalone/local-session-runtime.mjs +11 -0
- package/src/standalone/session-client/attachment-calls.mjs +3 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +62 -0
- package/src/standalone/session-client/lease-heartbeat.mjs +47 -0
- package/src/standalone/session-client.mjs +104 -13
- package/src/standalone/session-protocol.mjs +16 -0
- package/src/standalone/session-proxy.mjs +3 -0
- package/src/standalone/session-service/agent-tree/agent-child.mjs +4 -0
- package/src/standalone/session-service/agent-tree/agent-rehydrate.mjs +1 -0
- package/src/standalone/session-service/entries/lifecycle.mjs +6 -0
- package/src/standalone/session-service/projection/revisions.mjs +2 -0
- package/src/standalone/session-service/projection/transcript-window.mjs +149 -19
- package/src/standalone/session-service/projection/workspace.mjs +14 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +11 -3
- package/src/standalone/session-transport/call-route.mjs +13 -0
- package/src/standalone/session-transport/client-registry/client-grace.mjs +45 -3
- package/src/standalone/session-transport/client-registry/registration-replays.mjs +1 -0
- package/src/standalone/session-transport/client-registry.mjs +20 -3
- package/src/standalone/session-transport/routes.mjs +79 -2
- package/src/standalone/session-transport/stack.mjs +2 -0
- package/src/standalone/session-transport.mjs +24 -1
- package/src/tui/app/message-selector.mjs +2 -1
- package/src/tui/app/model-options.mjs +35 -5
- package/src/tui/app/model-picker/provider-models-list.mjs +2 -2
- package/src/tui/app/model-picker.mjs +4 -2
- package/src/tui/dist/index.mjs +332 -36
- package/src/tui/index.jsx +10 -2
- package/src/tui/session/compaction-view.mjs +147 -0
- package/src/tui/session/context-state.mjs +6 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/quota-resume.mjs +2 -1
- package/src/tui/session/session-api/commands.mjs +10 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/intake/rewind.mjs +2 -1
- package/src/tui/session/session-api/integrations.mjs +17 -0
- package/src/tui/session/session-api/lifecycle/dispose.mjs +6 -1
- package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -0
- package/src/tui/session/session-api/lifecycle/resume.mjs +3 -0
- package/src/tui/session/session-api/routes.mjs +10 -1
- package/src/tui/session/session-api/settings.mjs +2 -0
- package/src/tui/session/session-api/worktree.mjs +60 -0
- package/src/tui/session/session-api.mjs +2 -0
- package/src/tui/session/session-flow/auto-clear.mjs +31 -12
- package/src/tui/session/session-flow/drain.mjs +15 -1
- package/src/tui/session/session-flow/session-reset.mjs +4 -3
- package/src/tui/session/session-flow.mjs +2 -0
- package/src/tui/session/transcript-intake.mjs +14 -5
- package/src/tui/session/turn-stream.mjs +32 -4
- package/src/tui/session/turn-tool-cards.mjs +10 -2
- package/src/tui/session/turn.mjs +39 -10
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
// Daemon-side stand-in for a provider in remote mode (MIXDOG_MODEL_PROXY=client).
|
|
2
|
+
// It holds no credentials and opens no model connection: every call is handed
|
|
3
|
+
// to the desktop client through the model proxy channel and executed by the
|
|
4
|
+
// PC's own provider of the same name. The registry still wraps it with
|
|
5
|
+
// admission scheduling and usage accounting, which then see the PC's result.
|
|
6
|
+
import {
|
|
7
|
+
getModelProxyChannel,
|
|
8
|
+
ModelProxyUnavailableError,
|
|
9
|
+
splitModelProxySendOptions,
|
|
10
|
+
stripModelProxySecrets,
|
|
11
|
+
} from './model-proxy-channel.mjs';
|
|
12
|
+
import {
|
|
13
|
+
isFinalizedProviderRequestTools,
|
|
14
|
+
providerNativeToolPrefixCount,
|
|
15
|
+
} from '../runtime-core/provider-request-tools.mjs';
|
|
16
|
+
import {
|
|
17
|
+
attachmentTextForPart,
|
|
18
|
+
isAttachmentReference,
|
|
19
|
+
isPermanentAttachmentError,
|
|
20
|
+
readAttachmentBuffer,
|
|
21
|
+
} from '../../../attachments/store.mjs';
|
|
22
|
+
import { MAX_ATTACHMENT_BLOB_BYTES } from '../../../attachments/limits.mjs';
|
|
23
|
+
|
|
24
|
+
/** Stored-attachment bytes one proxied call may carry inline: the store's own
|
|
25
|
+
* single-blob ceiling. Newer messages are inlined first; a reference past the
|
|
26
|
+
* budget stays a reference, which the PC lowers to its "unavailable" text. */
|
|
27
|
+
export const MODEL_PROXY_INLINE_MAX_BYTES = MAX_ATTACHMENT_BLOB_BYTES;
|
|
28
|
+
|
|
29
|
+
// A stored reference names a blob in THIS daemon's attachment store, which the
|
|
30
|
+
// PC running the call cannot read. Each one travels as the inline part it was
|
|
31
|
+
// stored from (image/file → base64 `data`, text → its text), so the PC lowers
|
|
32
|
+
// exactly what a local daemon would.
|
|
33
|
+
function inlineReference(part, budget) {
|
|
34
|
+
let buffer;
|
|
35
|
+
try {
|
|
36
|
+
buffer = readAttachmentBuffer(part);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if (isPermanentAttachmentError(error)) return part;
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
if (buffer.length > budget.remaining) return part;
|
|
42
|
+
budget.remaining -= buffer.length;
|
|
43
|
+
const { attachmentRef: _ref, sizeBytes: _size, ...rest } = part;
|
|
44
|
+
if (part.type === 'text') {
|
|
45
|
+
const { textKind: _kind, filename: _name, mimeType: _mime, ...textRest } = rest;
|
|
46
|
+
return { ...textRest, type: 'text', text: attachmentTextForPart(part) };
|
|
47
|
+
}
|
|
48
|
+
return { ...rest, data: buffer.toString('base64') };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function inlinePart(part, budget) {
|
|
52
|
+
if (!part || typeof part !== 'object' || Array.isArray(part)) return part;
|
|
53
|
+
if (isAttachmentReference(part)) return inlineReference(part, budget);
|
|
54
|
+
if (Array.isArray(part.content)) {
|
|
55
|
+
const content = inlineParts(part.content, budget);
|
|
56
|
+
return content === part.content ? part : { ...part, content };
|
|
57
|
+
}
|
|
58
|
+
return part;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Newest last in a content list, so it is walked backwards for the budget. */
|
|
62
|
+
function inlineParts(parts, budget) {
|
|
63
|
+
let out = null;
|
|
64
|
+
for (let index = parts.length - 1; index >= 0; index -= 1) {
|
|
65
|
+
const next = inlinePart(parts[index], budget);
|
|
66
|
+
if (next === parts[index]) continue;
|
|
67
|
+
out ??= [...parts];
|
|
68
|
+
out[index] = next;
|
|
69
|
+
}
|
|
70
|
+
return out ?? parts;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** `messages` with every stored attachment reference replaced by its bytes. */
|
|
74
|
+
export function inlineModelProxyAttachments(messages, { maxBytes = MODEL_PROXY_INLINE_MAX_BYTES } = {}) {
|
|
75
|
+
if (!Array.isArray(messages)) return messages;
|
|
76
|
+
const budget = { remaining: maxBytes };
|
|
77
|
+
let out = null;
|
|
78
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
79
|
+
const message = messages[index];
|
|
80
|
+
if (!message || typeof message !== 'object' || message.content == null || typeof message.content === 'string') {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const content = Array.isArray(message.content)
|
|
84
|
+
? inlineParts(message.content, budget)
|
|
85
|
+
: inlinePart(message.content, budget);
|
|
86
|
+
if (content === message.content) continue;
|
|
87
|
+
out ??= [...messages];
|
|
88
|
+
out[index] = { ...message, content };
|
|
89
|
+
}
|
|
90
|
+
return out ?? messages;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export class ModelProxyProvider {
|
|
94
|
+
#channel;
|
|
95
|
+
#modelInfo = new Map();
|
|
96
|
+
|
|
97
|
+
constructor(name, config = {}, channel = getModelProxyChannel()) {
|
|
98
|
+
this.name = name;
|
|
99
|
+
this.config = config || {};
|
|
100
|
+
this.#channel = channel;
|
|
101
|
+
// Adapter traits are reported by the PC's real instance.
|
|
102
|
+
if (typeof this.config.modelProxy?.nativePdf === 'boolean') this.nativePdf = this.config.modelProxy.nativePdf;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#call(method, fields = {}, options = {}) {
|
|
106
|
+
return this.#channel.request({ provider: this.name, method, ...fields }, options);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async send(messages, model, tools, sendOpts) {
|
|
110
|
+
const { options, callbacks } = splitModelProxySendOptions(sendOpts);
|
|
111
|
+
const finalized = isFinalizedProviderRequestTools(tools);
|
|
112
|
+
return this.#call(
|
|
113
|
+
'send',
|
|
114
|
+
{
|
|
115
|
+
model: model ?? null,
|
|
116
|
+
messages: inlineModelProxyAttachments(messages),
|
|
117
|
+
tools: Array.isArray(tools) ? tools : null,
|
|
118
|
+
toolsFinalized: finalized,
|
|
119
|
+
nativePrefixCount: finalized ? providerNativeToolPrefixCount(tools) : 0,
|
|
120
|
+
options,
|
|
121
|
+
},
|
|
122
|
+
{ signal: sendOpts?.signal || null, callbacks }
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
#adoptCatalog(catalog) {
|
|
127
|
+
const models = Array.isArray(catalog?.models) ? catalog.models : [];
|
|
128
|
+
const info = catalog?.modelInfo && typeof catalog.modelInfo === 'object' ? catalog.modelInfo : {};
|
|
129
|
+
this.#modelInfo = new Map(Object.entries(info));
|
|
130
|
+
return models;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async listModels() {
|
|
134
|
+
return this.#adoptCatalog(await this.#call('listModels'));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async _refreshModelCache() {
|
|
138
|
+
return this.#adoptCatalog(await this.#call('refreshModels'));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async isAvailable() {
|
|
142
|
+
return (await this.#call('isAvailable')) === true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getCachedModelInfo(model) {
|
|
146
|
+
return this.#modelInfo.get(String(model ?? '')) ?? null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** A proxy instance whose class carries the PC adapter's usage convention. */
|
|
151
|
+
export function createModelProxyProvider(name, config = {}, channel = getModelProxyChannel()) {
|
|
152
|
+
const inputExcludesCache = config?.modelProxy?.inputExcludesCache;
|
|
153
|
+
if (typeof inputExcludesCache !== 'boolean') return new ModelProxyProvider(name, config, channel);
|
|
154
|
+
class BoundModelProxyProvider extends ModelProxyProvider {
|
|
155
|
+
static inputExcludesCache = inputExcludesCache;
|
|
156
|
+
}
|
|
157
|
+
return new BoundModelProxyProvider(name, config, channel);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// The PC's provider set (availability + non-secret config) changes only on a
|
|
161
|
+
// PC-side sign-in or settings edit; a short cache keeps registry re-inits from
|
|
162
|
+
// paying a client round trip each. The cache belongs to one client generation:
|
|
163
|
+
// a reconnect (possibly from another PC) always asks again.
|
|
164
|
+
const PROVIDER_CONFIG_TTL_MS = 15_000;
|
|
165
|
+
/** How long a registry init waits for a client to connect and for its PC to
|
|
166
|
+
* answer with a loaded provider registry. */
|
|
167
|
+
export const MODEL_PROXY_CONFIG_WAIT_MS = 15_000;
|
|
168
|
+
const CONFIG_RETRY_FIRST_MS = 250;
|
|
169
|
+
const CONFIG_RETRY_MAX_MS = 2_000;
|
|
170
|
+
let _providerConfigCache = null;
|
|
171
|
+
let _providerConfigInFlight = null;
|
|
172
|
+
|
|
173
|
+
/** A reply the registry may act on: the PC says its registry is loaded, or it
|
|
174
|
+
* already names an enabled provider (a client that predates `ready`). An
|
|
175
|
+
* empty or unready answer is never read as "everything disabled". */
|
|
176
|
+
function providerConfigReady(reply, providers) {
|
|
177
|
+
// A loaded PC registry is authoritative, even when it has nothing enabled.
|
|
178
|
+
if (reply?.ready === true) return true;
|
|
179
|
+
if (reply?.ready === false) return false;
|
|
180
|
+
return Object.keys(providers).some((name) => providers[name]?.enabled === true);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const pause = (ms) =>
|
|
184
|
+
new Promise((resolve) => {
|
|
185
|
+
const timer = setTimeout(resolve, ms);
|
|
186
|
+
timer.unref?.();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
export function loadModelProxyProviderConfig({
|
|
190
|
+
channel = getModelProxyChannel(),
|
|
191
|
+
now = Date.now,
|
|
192
|
+
waitMs = MODEL_PROXY_CONFIG_WAIT_MS,
|
|
193
|
+
} = {}) {
|
|
194
|
+
const generation = channel.generation;
|
|
195
|
+
const cached = _providerConfigCache;
|
|
196
|
+
if (
|
|
197
|
+
cached &&
|
|
198
|
+
cached.channel === channel &&
|
|
199
|
+
cached.generation === generation &&
|
|
200
|
+
now() - cached.at < PROVIDER_CONFIG_TTL_MS
|
|
201
|
+
) {
|
|
202
|
+
return Promise.resolve(cached.value);
|
|
203
|
+
}
|
|
204
|
+
const inFlight = _providerConfigInFlight;
|
|
205
|
+
if (inFlight && inFlight.channel === channel && inFlight.generation === generation) return inFlight.promise;
|
|
206
|
+
const promise = (async () => {
|
|
207
|
+
const deadline = Date.now() + waitMs;
|
|
208
|
+
let delay = CONFIG_RETRY_FIRST_MS;
|
|
209
|
+
let lastError = null;
|
|
210
|
+
for (;;) {
|
|
211
|
+
if (!channel.connected) {
|
|
212
|
+
const remaining = deadline - Date.now();
|
|
213
|
+
if (remaining <= 0 || !(await channel.whenConnected(remaining))) {
|
|
214
|
+
throw lastError ?? new ModelProxyUnavailableError('no desktop client is connected to run model calls');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
const reply = await channel.request({ provider: '', method: 'providerConfig' });
|
|
219
|
+
const raw = reply?.providers && typeof reply.providers === 'object' ? reply.providers : {};
|
|
220
|
+
// Defense in depth: the PC already strips secrets before sending.
|
|
221
|
+
const value = stripModelProxySecrets(raw);
|
|
222
|
+
if (providerConfigReady(reply, value)) {
|
|
223
|
+
if (channel.generation === generation) {
|
|
224
|
+
_providerConfigCache = { at: now(), value, channel, generation };
|
|
225
|
+
}
|
|
226
|
+
return value;
|
|
227
|
+
}
|
|
228
|
+
lastError = new ModelProxyUnavailableError('the desktop client has not loaded its model providers yet');
|
|
229
|
+
} catch (error) {
|
|
230
|
+
// Only "no client / no answer yet" is worth waiting out.
|
|
231
|
+
if (error?.code !== 'MODEL_PROXY_UNAVAILABLE') throw error;
|
|
232
|
+
lastError = error;
|
|
233
|
+
}
|
|
234
|
+
if (Date.now() + delay > deadline) throw lastError;
|
|
235
|
+
await pause(delay);
|
|
236
|
+
delay = Math.min(delay * 2, CONFIG_RETRY_MAX_MS);
|
|
237
|
+
}
|
|
238
|
+
})();
|
|
239
|
+
const entry = { channel, generation, promise };
|
|
240
|
+
_providerConfigInFlight = entry;
|
|
241
|
+
const clear = () => {
|
|
242
|
+
if (_providerConfigInFlight === entry) _providerConfigInFlight = null;
|
|
243
|
+
};
|
|
244
|
+
promise.then(clear, clear);
|
|
245
|
+
return promise;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function _resetModelProxyProviderConfigForTest() {
|
|
249
|
+
_providerConfigCache = null;
|
|
250
|
+
_providerConfigInFlight = null;
|
|
251
|
+
}
|
|
@@ -10,6 +10,7 @@ import { writeJsonAtomicSync } from '../../../shared/atomic-file.mjs';
|
|
|
10
10
|
import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
|
|
11
11
|
import { getLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
|
|
12
12
|
import { accountIdentityFields, scrubOAuthSecrets } from './lib/oauth-token-utils.mjs';
|
|
13
|
+
import { modelProxyClientMode } from '../../../shared/model-proxy-mode.mjs';
|
|
13
14
|
|
|
14
15
|
export const CLIENT_ID = '1031625952748946';
|
|
15
16
|
export const DEVICE_AUTH_URL = 'https://auth.meta.com/oidc/device/authorization/';
|
|
@@ -44,6 +45,7 @@ const nonEmptyString = (value) => typeof value === 'string' && value.trim() !==
|
|
|
44
45
|
export const isValidMuseCredentialDocument = (doc) => nonEmptyString(doc?.accessToken) && nonEmptyString(doc?.apiKey);
|
|
45
46
|
|
|
46
47
|
export function loadTokens() {
|
|
48
|
+
if (modelProxyClientMode()) return null;
|
|
47
49
|
const path = getOwnTokenPath();
|
|
48
50
|
if (!existsSync(path)) return null;
|
|
49
51
|
try {
|
|
@@ -10,6 +10,15 @@ import { createHash } from 'node:crypto';
|
|
|
10
10
|
import { noteErrorUsage } from './lib/note-error-usage.mjs';
|
|
11
11
|
import { fileMtimeMs } from './lib/oauth-token-utils.mjs';
|
|
12
12
|
import { OpenAICompatProvider, preloadOpenAICompatRuntime } from './openai-compat.mjs';
|
|
13
|
+
import { getModelMetadataSync } from './model-catalog.mjs';
|
|
14
|
+
import { makeModelCache } from './model-cache.mjs';
|
|
15
|
+
import { PRICING_RATE_KEYS } from '../../../shared/llm/model-pricing-rates.mjs';
|
|
16
|
+
import {
|
|
17
|
+
MUSE_DEFAULT_EFFORT_LEVELS as FIVE_LEVELS,
|
|
18
|
+
MUSE_SEED_IDS as SEED_MODELS,
|
|
19
|
+
MUSE_SPARK_MAX_OUTPUT as SPARK_MAX_OUTPUT,
|
|
20
|
+
museSeedRow,
|
|
21
|
+
} from '../../../shared/llm/provider-seed-rosters.mjs';
|
|
13
22
|
import {
|
|
14
23
|
API_VERSION,
|
|
15
24
|
MODEL_API_BASE_URL,
|
|
@@ -27,108 +36,63 @@ export {
|
|
|
27
36
|
export { beginOAuthLogin, loginOAuth } from './muse-code-oauth-login.mjs';
|
|
28
37
|
|
|
29
38
|
export const DEFAULT_MODEL = 'muse-spark-1.3';
|
|
30
|
-
const FIVE_LEVELS = Object.freeze(['minimal', 'low', 'medium', 'high', 'xhigh']);
|
|
31
|
-
const SIX_LEVELS = Object.freeze([...FIVE_LEVELS, 'max']);
|
|
32
39
|
const effortOptions = (levels) => [{ type: 'effort', values: [...levels] }];
|
|
33
40
|
// Image and voice generation ids are served by the same catalog but are not chat models.
|
|
34
41
|
const NON_CHAT_MODEL_RE = /^muse-(image|voice)-/i;
|
|
35
|
-
const SPARK_CONTEXT = 1_048_576;
|
|
36
|
-
const SPARK_MAX_OUTPUT = 131_072;
|
|
37
|
-
const STANDARD_COST = { inputCostPerM: 1.25, outputCostPerM: 4.25, cacheReadCostPerM: 0.15, cacheWriteCostPerM: 0 };
|
|
38
|
-
const CONTRIBUTOR_COST = { inputCostPerM: 0.1, outputCostPerM: 0.2, cacheReadCostPerM: 0.002, cacheWriteCostPerM: 0 };
|
|
39
|
-
|
|
40
|
-
// Seeded roster: the discovery endpoint lists bare ids, so limits, pricing and
|
|
41
|
-
// the effort ladder come from here. Only the 1.3 revisions accept `max`.
|
|
42
|
-
const SEEDS = Object.freeze({
|
|
43
|
-
'muse-spark-1.1': { name: 'Muse Spark 1.1', levels: FIVE_LEVELS, cost: STANDARD_COST },
|
|
44
|
-
'muse-spark-1.2': { name: 'Muse Spark 1.2', levels: FIVE_LEVELS, cost: STANDARD_COST },
|
|
45
|
-
'muse-spark-1.2-contributor': { name: 'Muse Spark 1.2 (C)', levels: FIVE_LEVELS, cost: CONTRIBUTOR_COST },
|
|
46
|
-
'muse-spark-1.3': { name: 'Muse Spark 1.3', levels: SIX_LEVELS, cost: STANDARD_COST },
|
|
47
|
-
'muse-spark-1.3-contributor': { name: 'Muse Spark 1.3 (C)', levels: SIX_LEVELS, cost: CONTRIBUTOR_COST },
|
|
48
|
-
});
|
|
49
|
-
const SEED_MODELS = Object.freeze(Object.keys(SEEDS));
|
|
50
|
-
|
|
51
|
-
function seedRow(id) {
|
|
52
|
-
const seed = SEEDS[id];
|
|
53
|
-
return {
|
|
54
|
-
id,
|
|
55
|
-
name: seed.name,
|
|
56
|
-
provider: 'muse-code-oauth',
|
|
57
|
-
family: 'muse-spark',
|
|
58
|
-
contextWindow: SPARK_CONTEXT,
|
|
59
|
-
outputTokens: SPARK_MAX_OUTPUT,
|
|
60
|
-
supportsReasoning: true,
|
|
61
|
-
supportsVision: true,
|
|
62
|
-
supportsFunctionCalling: true,
|
|
63
|
-
reasoningLevels: [...seed.levels],
|
|
64
|
-
reasoningOptions: effortOptions(seed.levels),
|
|
65
|
-
...seed.cost,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Revisioned Spark ids the seed does not list inherit the newest seeded
|
|
70
|
-
// revision of their tier (limits, vision, pricing) but take the five-level
|
|
71
|
-
// ladder: only reviewed seeds advertise `max`.
|
|
72
|
-
const SPARK_ID_RE = /^muse-spark-(\d+)\.(\d+)(?:\.(\d+))?(-contributor)?$/i;
|
|
73
42
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
43
|
+
// The discovered roster (fields /models returned, never seed values) is
|
|
44
|
+
// persisted so the shared sync lookup sees what listModels found.
|
|
45
|
+
const museModelCache = makeModelCache({
|
|
46
|
+
fileName: 'muse-code-oauth-models.json',
|
|
47
|
+
ttlMs: 24 * 60 * 60 * 1000,
|
|
48
|
+
version: 1,
|
|
49
|
+
});
|
|
78
50
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
51
|
+
// Row fields the shared metadata lookup resolves: live roster, models.dev
|
|
52
|
+
// `meta`, verified corrections, then the offline seed (provider-seed-rosters).
|
|
53
|
+
const LOOKUP_FIELDS = Object.freeze([
|
|
54
|
+
'contextWindow',
|
|
55
|
+
'outputTokens',
|
|
56
|
+
...PRICING_RATE_KEYS,
|
|
57
|
+
'supportsReasoning',
|
|
58
|
+
'supportsVision',
|
|
59
|
+
'supportsFunctionCalling',
|
|
60
|
+
'reasoningOptions',
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
// Labels compare without the trailing "(C)" tier marker, case or punctuation.
|
|
64
|
+
const nameKey = (name) =>
|
|
65
|
+
String(name || '')
|
|
66
|
+
.replace(/\s*\(c\)\s*$/i, '')
|
|
67
|
+
.toLowerCase()
|
|
68
|
+
.replace(/[^a-z0-9]+/g, '');
|
|
69
|
+
|
|
70
|
+
// A discovered or catalog label naming the seeded model keeps the seed's "(C)" marker.
|
|
71
|
+
function tierName(candidate, seedName) {
|
|
72
|
+
if (!candidate) return '';
|
|
73
|
+
return seedName && nameKey(candidate) === nameKey(seedName) ? seedName : candidate;
|
|
82
74
|
}
|
|
83
75
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
for (const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (!templateRevision || compareRevision(candidate, templateRevision) > 0) {
|
|
95
|
-
template = seedId;
|
|
96
|
-
templateRevision = candidate;
|
|
97
|
-
}
|
|
76
|
+
// Discovered fields win, then the shared lookup (fetched catalogs before
|
|
77
|
+
// seeds). Rows that report no effort ladder gain the default five levels
|
|
78
|
+
// unless a source declares the model non-reasoning.
|
|
79
|
+
function museRow(id, discovered = null) {
|
|
80
|
+
const meta = getModelMetadataSync(id, 'muse-code-oauth');
|
|
81
|
+
if (!meta && !discovered) return null;
|
|
82
|
+
const row = { id, provider: 'muse-code-oauth', family: 'muse-spark' };
|
|
83
|
+
for (const key of LOOKUP_FIELDS) {
|
|
84
|
+
const value = meta?.[key];
|
|
85
|
+
if (value != null && !(Array.isArray(value) && !value.length)) row[key] = value;
|
|
98
86
|
}
|
|
99
|
-
|
|
100
|
-
const revision =
|
|
101
|
-
match[3] && Number(match[3]) !== 0 ? `${match[1]}.${match[2]}.${match[3]}` : `${match[1]}.${match[2]}`;
|
|
102
|
-
return {
|
|
103
|
-
...seedRow(template),
|
|
104
|
-
id,
|
|
105
|
-
name: `Muse Spark ${revision}${contributor ? ' (C)' : ''}`,
|
|
106
|
-
reasoningLevels: [...FIVE_LEVELS],
|
|
107
|
-
reasoningOptions: effortOptions(FIVE_LEVELS),
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Seeds win for the ids they cover; lineage rows yield to discovered metadata.
|
|
112
|
-
// A name supplied by discovery is always kept. Other discovered rows keep
|
|
113
|
-
// their own metadata and only gain the default effort ladder when they report none.
|
|
114
|
-
function museRow(id, discovered) {
|
|
87
|
+
Object.assign(row, discovered || {});
|
|
115
88
|
const discoveredName =
|
|
116
89
|
typeof discovered?.name === 'string' && discovered.name && discovered.name !== id ? discovered.name : '';
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
family: 'muse-spark',
|
|
124
|
-
...(lineage || {}),
|
|
125
|
-
...(discovered || {}),
|
|
126
|
-
};
|
|
127
|
-
if (discoveredName) row.name = discoveredName;
|
|
128
|
-
else if (lineage) row.name = lineage.name;
|
|
129
|
-
if (!row.reasoningOptions?.length) {
|
|
130
|
-
row.reasoningOptions = effortOptions(FIVE_LEVELS);
|
|
131
|
-
row.reasoningLevels = [...FIVE_LEVELS];
|
|
90
|
+
row.name = tierName(discoveredName || meta?.displayName, museSeedRow(id)?.name) || id;
|
|
91
|
+
// A model declared non-reasoning gets no default ladder.
|
|
92
|
+
if (!row.reasoningOptions?.length)
|
|
93
|
+
row.reasoningOptions = row.supportsReasoning === false ? [] : effortOptions(FIVE_LEVELS);
|
|
94
|
+
if (!row.reasoningLevels?.length) {
|
|
95
|
+
row.reasoningLevels = [...(row.reasoningOptions.find((o) => o?.type === 'effort')?.values || [])];
|
|
132
96
|
}
|
|
133
97
|
return row;
|
|
134
98
|
}
|
|
@@ -414,12 +378,9 @@ export class MuseCodeOAuthProvider {
|
|
|
414
378
|
});
|
|
415
379
|
this._innerKey = apiKey;
|
|
416
380
|
// The Responses builder reads limits and the effort ladder from the inner
|
|
417
|
-
// provider's cached model row;
|
|
381
|
+
// provider's cached model row; the shared lookup fills what discovery omits.
|
|
418
382
|
const innerRow = this._inner.getCachedModelInfo.bind(this._inner);
|
|
419
|
-
this._inner.getCachedModelInfo = (model) =>
|
|
420
|
-
const discovered = innerRow(model);
|
|
421
|
-
return discovered || SEEDS[model] || lineageRow(model) ? museRow(model, discovered) : null;
|
|
422
|
-
};
|
|
383
|
+
this._inner.getCachedModelInfo = (model) => museRow(model, innerRow(model));
|
|
423
384
|
return this._inner;
|
|
424
385
|
}
|
|
425
386
|
|
|
@@ -452,6 +413,8 @@ export class MuseCodeOAuthProvider {
|
|
|
452
413
|
async listModels() {
|
|
453
414
|
const { apiKey } = await this.ensureAuth();
|
|
454
415
|
const inner = this._ensureInner(apiKey);
|
|
416
|
+
// Only the latest request, fetched with the still-active key, publishes.
|
|
417
|
+
const publish = museModelCache.beginSave(() => loadTokens()?.apiKey === apiKey);
|
|
455
418
|
let items;
|
|
456
419
|
try {
|
|
457
420
|
items = await inner._fetchModelItems();
|
|
@@ -460,25 +423,29 @@ export class MuseCodeOAuthProvider {
|
|
|
460
423
|
return SEED_MODELS.map((id) => museRow(id));
|
|
461
424
|
}
|
|
462
425
|
// A successful roster is authoritative, even when it holds no chat models.
|
|
463
|
-
const
|
|
426
|
+
const discovered = (Array.isArray(items) ? items : [])
|
|
464
427
|
.filter((m) => typeof m?.id === 'string' && m.id && !NON_CHAT_MODEL_RE.test(m.id))
|
|
465
428
|
.map((m) => {
|
|
466
429
|
const contextWindow = Number(m.context_window ?? m.max_context_window ?? m.context_length ?? 0);
|
|
467
430
|
const outputTokens = Number(m.max_output_tokens ?? m.output_tokens ?? 0);
|
|
468
431
|
const name = [m.name, m.display_name].find((v) => typeof v === 'string' && v.trim());
|
|
469
|
-
return
|
|
432
|
+
return {
|
|
433
|
+
id: m.id,
|
|
434
|
+
provider: 'muse-code-oauth',
|
|
470
435
|
...(name ? { name: name.trim() } : {}),
|
|
471
436
|
...(contextWindow > 0 ? { contextWindow } : {}),
|
|
472
437
|
...(outputTokens > 0 ? { outputTokens } : {}),
|
|
473
438
|
...(typeof m.created === 'number' ? { created: m.created } : {}),
|
|
474
|
-
}
|
|
439
|
+
};
|
|
475
440
|
});
|
|
476
|
-
|
|
441
|
+
const rows = discovered.map((row) => museRow(row.id, row));
|
|
442
|
+
// A superseded or other-credential response never replaces the live roster either.
|
|
443
|
+
if (publish(discovered)) inner._enrichedModels = rows;
|
|
477
444
|
return rows;
|
|
478
445
|
}
|
|
479
446
|
|
|
480
447
|
getCachedModelInfo(model) {
|
|
481
|
-
return this._inner?.getCachedModelInfo?.(model) ||
|
|
448
|
+
return this._inner?.getCachedModelInfo?.(model) || museRow(model);
|
|
482
449
|
}
|
|
483
450
|
|
|
484
451
|
/** Subscription window/weekly usage, read from the key endpoint without onboarding. */
|
|
@@ -6,6 +6,7 @@ import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs
|
|
|
6
6
|
import { cursorTokenExpiry } from './cursor-auth.mjs';
|
|
7
7
|
import { isValidMuseCredentialDocument } from './muse-code-oauth-tokens.mjs';
|
|
8
8
|
import { developerOptionEnabled, developerOptionForProvider } from '../../../shared/developer-options.mjs';
|
|
9
|
+
import { modelProxyClientMode } from '../../../shared/model-proxy-mode.mjs';
|
|
9
10
|
|
|
10
11
|
const ANTHROPIC_DEFAULT_CREDENTIALS_PATH = join(resolvePluginData(), 'anthropic-oauth-credentials.json');
|
|
11
12
|
|
|
@@ -100,6 +101,8 @@ function credentialPathsSignature(paths, extra = '') {
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
function memoProbe(key, paths, compute, extraSignature = '') {
|
|
104
|
+
// Remote mode: no credential file on this server is ever read.
|
|
105
|
+
if (modelProxyClientMode()) return 'absent';
|
|
103
106
|
const signature = credentialPathsSignature(paths, extraSignature);
|
|
104
107
|
const hit = _probeCache.get(key);
|
|
105
108
|
const now = Date.now();
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
useXaiResponsesWebSocket as preferXaiResponsesWebSocket,
|
|
13
13
|
_shouldFallbackXaiWsToHttp,
|
|
14
14
|
} from './openai-compat-xai.mjs';
|
|
15
|
+
import { withEffortRejectionFallback } from './effort-rejection.mjs';
|
|
15
16
|
import { sendCompatResponses } from './openai-compat-responses.mjs';
|
|
16
17
|
import { sendCompatChat, recoverCompatNonStreaming } from './openai-compat-chat-send.mjs';
|
|
17
18
|
import { sendXaiResponses, sendXaiResponsesWebSocket } from './openai-compat-xai-send.mjs';
|
|
@@ -161,7 +162,11 @@ export class OpenAICompatProvider {
|
|
|
161
162
|
}
|
|
162
163
|
async _doSend(messages, model, tools, sendOpts) {
|
|
163
164
|
const useModel = model || this.defaultModel;
|
|
164
|
-
|
|
165
|
+
return withEffortRejectionFallback(this.name, useModel, sendOpts || {}, this.config?.reasoningEffort, (opts) =>
|
|
166
|
+
this._sendWithOpts(messages, useModel, tools, opts)
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
async _sendWithOpts(messages, useModel, tools, opts) {
|
|
165
170
|
// Re-warm a kept-alive socket to the provider origin before the turn so
|
|
166
171
|
// the request hot path lands on a live socket instead of paying a cold
|
|
167
172
|
// TLS handshake after an idle gap. Fire-and-forget; never awaited.
|
|
@@ -13,7 +13,7 @@ import { formatScheduleModelRef, parseScheduleModelRef } from '../../../shared/s
|
|
|
13
13
|
export const CODEX_MODEL_CACHE_OPTIONS = Object.freeze({
|
|
14
14
|
fileName: 'openai-oauth-models.json',
|
|
15
15
|
ttlMs: 24 * 60 * 60_000,
|
|
16
|
-
version:
|
|
16
|
+
version: 7,
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
export const CODEX_PROVIDER_ID = 'openai-oauth';
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
import { makeModelCache } from './model-cache.mjs';
|
|
14
14
|
import { CODEX_MODEL_CACHE_OPTIONS } from './openai-oauth-catalog-store.mjs';
|
|
15
15
|
import { enrichModels } from './model-catalog.mjs';
|
|
16
|
-
import { sanitizeModelList } from './model-list-sanitize.mjs';
|
|
17
16
|
import { modelSupportsServiceTier } from './model-service-tiers.mjs';
|
|
18
17
|
import { codexClientVersionSync, warmCodexClientVersion } from './codex-client-meta.mjs';
|
|
19
18
|
import { compareSemver, maxSemver } from './npm-cli-version.mjs';
|
|
@@ -131,7 +130,11 @@ async function fetchCodexCatalog(ensureAuth, label) {
|
|
|
131
130
|
const items = Array.isArray(data?.models) ? data.models : [];
|
|
132
131
|
const normalized = items.map((m) => _normalizeCodexModel(m));
|
|
133
132
|
_markLatestCodex(normalized);
|
|
134
|
-
|
|
133
|
+
// The backend curates this list per account and client version and marks
|
|
134
|
+
// each row's picker visibility itself, so every row is kept: generic list
|
|
135
|
+
// pruning (external release dates, id patterns) dropped models it still
|
|
136
|
+
// serves, along with their access programs.
|
|
137
|
+
const enriched = (await enrichModels(normalized)).filter(Boolean);
|
|
135
138
|
_modelCache.save(enriched, { clientVersion });
|
|
136
139
|
return enriched;
|
|
137
140
|
}
|
|
@@ -18,6 +18,7 @@ import { getPluginData } from '../config.mjs';
|
|
|
18
18
|
import { writeJsonAtomicSync, withFileLock } from '../../../shared/atomic-file.mjs';
|
|
19
19
|
import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
|
|
20
20
|
import { getLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
|
|
21
|
+
import { modelProxyClientMode, serverModelCredentialsDisabledError } from '../../../shared/model-proxy-mode.mjs';
|
|
21
22
|
import {
|
|
22
23
|
accountIdentityFields,
|
|
23
24
|
decodeJwtPayload,
|
|
@@ -101,6 +102,7 @@ export function tokensFileMtimeMs() {
|
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
export function loadTokens() {
|
|
105
|
+
if (modelProxyClientMode()) return null;
|
|
104
106
|
const ownPath = getOwnTokenPath();
|
|
105
107
|
if (!existsSync(ownPath)) return null;
|
|
106
108
|
try {
|
|
@@ -322,6 +324,7 @@ async function refreshUnderLock({ currentToken, force, reason, startingTokens })
|
|
|
322
324
|
* the provider can hold off re-attempting until the expiry skew.
|
|
323
325
|
*/
|
|
324
326
|
export async function refreshStoredTokens({ current = null, force = false, reason = 'preemptive' } = {}) {
|
|
327
|
+
if (modelProxyClientMode()) throw serverModelCredentialsDisabledError('OpenAI OAuth credentials');
|
|
325
328
|
const currentToken = current?.access_token || null;
|
|
326
329
|
const disk = loadTokens();
|
|
327
330
|
const adopted = adoptableDiskTokens(currentToken, disk, force);
|
|
@@ -6,6 +6,7 @@ import { createAccountPoolProvider } from './account-pool.mjs';
|
|
|
6
6
|
import { isInclusiveProvider } from '../../../shared/llm/cost.mjs';
|
|
7
7
|
import { noEnabledProvidersError } from './registry-errors.mjs';
|
|
8
8
|
import { providerTakesNativePdf } from '../../../attachments/store.mjs';
|
|
9
|
+
import { modelProxyClientMode } from '../../../shared/model-proxy-mode.mjs';
|
|
9
10
|
// OpenAI-compat provider names are self-declared by openai-compat-presets.mjs via
|
|
10
11
|
// OPENAI_COMPAT_PRESETS. No parallel list maintained here.
|
|
11
12
|
const providers = new Map();
|
|
@@ -176,6 +177,12 @@ async function loadProviderCtor(name, signal = null, cfg = null) {
|
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
async function instantiateProvider(name, Ctor, cfg) {
|
|
180
|
+
// Remote mode: the client PC executes the call with its own credentials.
|
|
181
|
+
// Admission and usage accounting still wrap the daemon-side stand-in.
|
|
182
|
+
if (modelProxyClientMode()) {
|
|
183
|
+
const { createModelProxyProvider } = await import('./model-proxy-provider.mjs');
|
|
184
|
+
return wrapProviderAdmission(createModelProxyProvider(name, cfg), name);
|
|
185
|
+
}
|
|
179
186
|
if (isCustomProviderConfig(name, cfg)) return wrapProviderAdmission(await Ctor(name, cfg), name);
|
|
180
187
|
if (Object.hasOwn(OPENAI_COMPAT_PRESETS, name) && name !== 'opencode-go') {
|
|
181
188
|
return wrapProviderAdmission(new Ctor(name, cfg), name);
|
|
@@ -185,6 +192,12 @@ async function instantiateProvider(name, Ctor, cfg) {
|
|
|
185
192
|
|
|
186
193
|
export async function initProviders(config, { signal = null } = {}) {
|
|
187
194
|
throwIfAborted(signal);
|
|
195
|
+
// Remote mode: provider availability and config are the PC's, never this
|
|
196
|
+
// server's credential probes. No connected client fails here, clearly.
|
|
197
|
+
if (modelProxyClientMode()) {
|
|
198
|
+
const { loadModelProxyProviderConfig } = await import('./model-proxy-provider.mjs');
|
|
199
|
+
config = await awaitWithAbort(loadModelProxyProviderConfig(), signal);
|
|
200
|
+
}
|
|
188
201
|
const sig = configSignature(config);
|
|
189
202
|
// Coalesce: an identical config is already mid-init — attach to it.
|
|
190
203
|
if (sig !== null && _inFlightPromise && _inFlightSig === sig) {
|
|
@@ -254,7 +267,8 @@ async function _initProvidersUnsynchronized(config, signal = null) {
|
|
|
254
267
|
return { name, inst: providers.get(name), sig };
|
|
255
268
|
}
|
|
256
269
|
try {
|
|
257
|
-
|
|
270
|
+
// A proxied provider never loads the real adapter module.
|
|
271
|
+
const Ctor = modelProxyClientMode() ? null : await loadProviderCtor(name, signal, cfg);
|
|
258
272
|
throwIfAborted(signal);
|
|
259
273
|
const inst = await instantiateProvider(name, Ctor, cfg);
|
|
260
274
|
return { name, inst, sig };
|
|
@@ -30,6 +30,7 @@ import { setBuiltinInstalledInConfig, withGrandfatheredBuiltins } from '../../..
|
|
|
30
30
|
const BROWSER_BRIDGE_DISCOVERY_FILE = 'browser-bridge.json';
|
|
31
31
|
const COMPUTER_BRIDGE_DISCOVERY_FILE = 'computer-bridge.json';
|
|
32
32
|
const TERMINAL_BRIDGE_DISCOVERY_FILE = 'terminal-bridge.json';
|
|
33
|
+
const VISUALIZE_BRIDGE_DISCOVERY_FILE = 'visualize-bridge.json';
|
|
33
34
|
|
|
34
35
|
function bridgePresent(file) {
|
|
35
36
|
try {
|
|
@@ -116,6 +117,14 @@ export function builtinFeatureActive(configLike, id) {
|
|
|
116
117
|
if (id === 'terminal') {
|
|
117
118
|
return featureEnvOverride('MIXDOG_FEATURE_TERMINAL') ?? bridgePresent(TERMINAL_BRIDGE_DISCOVERY_FILE);
|
|
118
119
|
}
|
|
120
|
+
// Visualize shows artifacts in the desktop side panel: no install step, on
|
|
121
|
+
// unless the user turns it off, and only while the desktop bridge is up.
|
|
122
|
+
if (id === 'visualize') {
|
|
123
|
+
return (
|
|
124
|
+
featureEnvOverride('MIXDOG_FEATURE_VISUALIZE') ??
|
|
125
|
+
(moduleEnabled(configLike, 'visualize', true) && bridgePresent(VISUALIZE_BRIDGE_DISCOVERY_FILE))
|
|
126
|
+
);
|
|
127
|
+
}
|
|
119
128
|
return false;
|
|
120
129
|
}
|
|
121
130
|
|
|
@@ -148,6 +157,7 @@ export function featureDisallowedToolsFor(
|
|
|
148
157
|
...(builtinFeatureActive(configLike, 'office') ? [] : ['office']),
|
|
149
158
|
...(builtinFeatureActive(configLike, 'media') ? [] : ['media']),
|
|
150
159
|
...(builtinFeatureActive(configLike, 'tidy') ? [] : ['tidy']),
|
|
160
|
+
...(builtinFeatureActive(configLike, 'visualize') ? [] : ['visualize']),
|
|
151
161
|
];
|
|
152
162
|
// Headless exec uses the Lead surface and excludes Skill/MCP tools. When
|
|
153
163
|
// only its eager defaults remain, neither schemas nor loader guidance help.
|