mixdog 0.8.0 → 0.9.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 +47 -23
- package/package.json +33 -27
- package/scripts/_test-folder-dialog.mjs +30 -0
- package/scripts/agent-parallel-smoke.mjs +388 -0
- package/scripts/agent-tag-reuse-smoke.mjs +183 -0
- package/scripts/background-task-meta-smoke.mjs +38 -0
- package/scripts/boot-smoke.mjs +52 -9
- package/scripts/build-runtime-linux.sh +348 -0
- package/scripts/build-runtime-macos.sh +217 -0
- package/scripts/build-runtime-windows.ps1 +242 -0
- package/scripts/compact-active-turn-test.mjs +68 -0
- package/scripts/compact-smoke.mjs +859 -129
- package/scripts/compact-trigger-migration-smoke.mjs +187 -0
- package/scripts/fix-brief-fn.mjs +35 -0
- package/scripts/fix-format-tool-surface.mjs +24 -0
- package/scripts/fix-tool-exec-visible.mjs +42 -0
- package/scripts/generate-runtime-manifest.mjs +166 -0
- package/scripts/hook-bus-test.mjs +330 -0
- package/scripts/lead-workflow-smoke.mjs +33 -39
- package/scripts/live-worker-smoke.mjs +43 -37
- package/scripts/llm-trace-summary.mjs +315 -0
- package/scripts/memory-meta-concurrency-test.mjs +20 -0
- package/scripts/output-style-smoke.mjs +56 -15
- package/scripts/parent-abort-link-test.mjs +44 -0
- package/scripts/patch-agent-brief.mjs +48 -0
- package/scripts/patch-app.mjs +21 -0
- package/scripts/patch-app2.mjs +18 -0
- package/scripts/patch-dist-brief.mjs +96 -0
- package/scripts/patch-tool-exec.mjs +70 -0
- package/scripts/pretool-ask-runtime-test.mjs +54 -0
- package/scripts/provider-toolcall-test.mjs +376 -0
- package/scripts/reactive-compact-persist-smoke.mjs +124 -0
- package/scripts/sanitize-tool-pairs-test.mjs +260 -0
- package/scripts/session-context-bench.mjs +344 -0
- package/scripts/session-ingest-smoke.mjs +177 -0
- package/scripts/set-effort-config-test.mjs +41 -0
- package/scripts/smoke-runtime-negative.ps1 +106 -0
- package/scripts/smoke-runtime-negative.sh +97 -0
- package/scripts/smoke.mjs +25 -0
- package/scripts/tool-result-hook-test.mjs +48 -0
- package/scripts/tool-smoke.mjs +1223 -95
- package/scripts/toolcall-args-test.mjs +150 -0
- package/scripts/tui-background-failure-smoke.mjs +73 -0
- package/scripts/usage-metrics-epoch-smoke.mjs +114 -0
- package/src/agents/debugger/AGENT.md +8 -0
- package/src/agents/explore/AGENT.md +4 -0
- package/src/agents/heavy-worker/AGENT.md +9 -3
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +8 -0
- package/src/agents/scheduler-task/AGENT.md +12 -0
- package/src/agents/scheduler-task/agent.json +6 -0
- package/src/agents/webhook-handler/AGENT.md +12 -0
- package/src/agents/webhook-handler/agent.json +6 -0
- package/src/agents/worker/AGENT.md +9 -3
- package/src/app.mjs +77 -3
- package/src/defaults/hidden-roles.json +17 -12
- package/src/headless-role.mjs +117 -0
- package/src/help.mjs +30 -0
- package/src/hooks/lib/permission-evaluator.cjs +11 -475
- package/src/lib/keychain-cjs.cjs +9 -1
- package/src/lib/mixdog-debug.cjs +0 -7
- package/src/lib/rules-builder.cjs +240 -96
- package/src/lib/text-utils.cjs +1 -1
- package/src/mixdog-session-runtime.mjs +2325 -450
- package/src/output-styles/default.md +12 -28
- package/src/output-styles/extreme-simple.md +9 -6
- package/src/output-styles/simple.md +22 -9
- package/src/repl.mjs +118 -59
- package/src/rules/agent/00-common.md +15 -0
- package/src/rules/{bridge → agent}/20-skip-protocol.md +1 -2
- package/src/rules/agent/30-explorer.md +22 -0
- package/src/rules/{bridge → agent}/40-cycle1-agent.md +7 -0
- package/src/rules/{bridge → agent}/41-cycle2-agent.md +7 -0
- package/src/rules/{bridge → agent}/42-cycle3-agent.md +7 -0
- package/src/rules/lead/01-general.md +9 -5
- package/src/rules/lead/04-workflow.md +51 -12
- package/src/rules/lead/lead-tool.md +6 -0
- package/src/rules/shared/01-tool.md +12 -1
- package/src/runtime/agent/orchestrator/activity-bus.mjs +7 -18
- package/src/runtime/agent/orchestrator/agent-owner.mjs +11 -0
- package/src/runtime/agent/orchestrator/{smart-bridge/bridge-llm.mjs → agent-runtime/agent-dispatch.mjs} +138 -111
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +94 -0
- package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/cache-strategy.mjs +32 -23
- package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/session-builder.mjs +33 -27
- package/src/runtime/agent/orchestrator/{bridge-trace.mjs → agent-trace.mjs} +132 -81
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +0 -21
- package/src/runtime/agent/orchestrator/config.mjs +174 -55
- package/src/runtime/agent/orchestrator/context/collect.mjs +195 -487
- package/src/runtime/agent/orchestrator/dispatch-persist.mjs +1 -1
- package/src/runtime/agent/orchestrator/internal-roles.mjs +77 -29
- package/src/runtime/agent/orchestrator/internal-tools.mjs +5 -6
- package/src/runtime/agent/orchestrator/mcp/client.mjs +15 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +377 -243
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +146 -93
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +236 -4
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +49 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +58 -13
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +67 -149
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +132 -2
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +45 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +61 -116
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +25 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +79 -255
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +221 -70
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +477 -147
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +343 -496
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -6
- package/src/runtime/agent/orchestrator/providers/registry.mjs +88 -51
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +31 -11
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +41 -8
- package/src/runtime/agent/orchestrator/session/cache/post-edit-marks.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact.mjs +1173 -267
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +199 -36
- package/src/runtime/agent/orchestrator/session/loop.mjs +851 -674
- package/src/runtime/agent/orchestrator/session/manager.mjs +1593 -466
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +107 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +291 -46
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -16
- package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +3 -219
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +34 -7
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +19 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +9 -9
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +60 -37
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +21 -2
- package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -7
- package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -3
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +5 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -36
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +2 -40
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +148 -27
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +43 -75
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +90 -20
- package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -5
- package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +86 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +11 -11
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4106 -4019
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -4
- package/src/runtime/agent/orchestrator/tools/patch.mjs +90 -6
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +6 -4
- package/src/runtime/agent/orchestrator/tools/result-compression.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +8 -1
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -4
- package/src/runtime/channels/index.mjs +152 -24
- package/src/runtime/channels/lib/scheduler.mjs +18 -14
- package/src/runtime/channels/lib/session-discovery.mjs +3 -2
- package/src/runtime/channels/lib/tool-format.mjs +0 -2
- package/src/runtime/channels/lib/transcript-discovery.mjs +3 -2
- package/src/runtime/channels/lib/webhook.mjs +1 -1
- package/src/runtime/channels/tool-defs.mjs +29 -29
- package/src/runtime/memory/index.mjs +635 -107
- package/src/runtime/memory/lib/agent-ipc.mjs +29 -12
- package/src/runtime/memory/lib/core-memory-store.mjs +2 -2
- package/src/runtime/memory/lib/embedding-model-config.mjs +55 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +31 -4
- package/src/runtime/memory/lib/embedding-worker.mjs +19 -10
- package/src/runtime/memory/lib/memory-cycle1.mjs +38 -17
- package/src/runtime/memory/lib/memory-cycle2.mjs +6 -7
- package/src/runtime/memory/lib/memory-cycle3.mjs +4 -4
- package/src/runtime/memory/lib/memory-ops-policy.mjs +2 -1
- package/src/runtime/memory/lib/memory-session-merge.mjs +38 -0
- package/src/runtime/memory/lib/memory.mjs +88 -9
- package/src/runtime/memory/lib/model-profile.mjs +1 -1
- package/src/runtime/memory/lib/pg/adapter.mjs +15 -1
- package/src/runtime/memory/lib/pg/supervisor.mjs +12 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +37 -3
- package/src/runtime/memory/lib/session-ingest.mjs +194 -0
- package/src/runtime/memory/lib/trace-store.mjs +96 -51
- package/src/runtime/memory/tool-defs.mjs +46 -37
- package/src/runtime/search/index.mjs +102 -466
- package/src/runtime/search/lib/web-tools.mjs +45 -25
- package/src/runtime/search/tool-defs.mjs +16 -23
- package/src/runtime/shared/abort-controller.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +4 -3
- package/src/runtime/shared/background-tasks.mjs +122 -11
- package/src/runtime/shared/child-spawn-gate.mjs +145 -0
- package/src/runtime/shared/config.mjs +7 -4
- package/src/runtime/shared/err-text.mjs +131 -4
- package/src/runtime/shared/llm/cost.mjs +2 -2
- package/src/runtime/shared/llm/http-agent.mjs +23 -7
- package/src/runtime/shared/llm/index.mjs +34 -11
- package/src/runtime/shared/llm/usage-log.mjs +4 -4
- package/src/runtime/shared/markdown-frontmatter.mjs +56 -0
- package/src/runtime/shared/singleton-owner.mjs +104 -0
- package/src/runtime/shared/tool-execution-contract.mjs +199 -20
- package/src/runtime/shared/tool-execution-contract.test.mjs +183 -0
- package/src/runtime/shared/tool-surface.mjs +624 -98
- package/src/runtime/shared/user-data-guard.mjs +0 -2
- package/src/standalone/agent-task-status.mjs +203 -0
- package/src/standalone/agent-task-status.test.mjs +76 -0
- package/src/standalone/agent-tool.mjs +1913 -0
- package/src/standalone/channel-worker.mjs +370 -14
- package/src/standalone/explore-tool.mjs +165 -70
- package/src/standalone/folder-dialog.mjs +314 -0
- package/src/standalone/hook-bus.mjs +898 -22
- package/src/standalone/memory-runtime-proxy.mjs +320 -0
- package/src/standalone/projects.mjs +226 -0
- package/src/standalone/provider-admin.mjs +41 -24
- package/src/standalone/seeds.mjs +2 -69
- package/src/standalone/usage-dashboard.mjs +96 -8
- package/src/tui/App.jsx +4798 -2153
- package/src/tui/components/AnsiText.jsx +39 -28
- package/src/tui/components/ContextPanel.jsx +87 -29
- package/src/tui/components/Markdown.jsx +43 -77
- package/src/tui/components/MarkdownTable.jsx +9 -184
- package/src/tui/components/Message.jsx +28 -11
- package/src/tui/components/Picker.jsx +95 -56
- package/src/tui/components/PromptInput.jsx +367 -239
- package/src/tui/components/QueuedCommands.jsx +1 -1
- package/src/tui/components/SlashCommandPalette.jsx +27 -21
- package/src/tui/components/Spinner.jsx +67 -38
- package/src/tui/components/StatusLine.jsx +606 -38
- package/src/tui/components/TextEntryPanel.jsx +128 -9
- package/src/tui/components/ToolExecution.jsx +617 -368
- package/src/tui/components/TurnDone.jsx +3 -3
- package/src/tui/components/UsagePanel.jsx +3 -5
- package/src/tui/components/tool-output-format.mjs +365 -0
- package/src/tui/components/tool-output-format.test.mjs +220 -0
- package/src/tui/dist/index.mjs +8915 -2418
- package/src/tui/engine-runtime-notification.test.mjs +115 -0
- package/src/tui/engine-tool-result-text.test.mjs +75 -0
- package/src/tui/engine.mjs +1455 -279
- package/src/tui/figures.mjs +21 -40
- package/src/tui/index.jsx +75 -31
- package/src/tui/input-editing.mjs +25 -0
- package/src/tui/markdown/format-token.mjs +511 -68
- package/src/tui/markdown/format-token.test.mjs +216 -0
- package/src/tui/markdown/render-ansi.mjs +94 -0
- package/src/tui/markdown/render-ansi.test.mjs +108 -0
- package/src/tui/markdown/stream-fence.mjs +34 -0
- package/src/tui/markdown/stream-fence.test.mjs +26 -0
- package/src/tui/markdown/table-layout.mjs +250 -0
- package/src/tui/paste-attachments.mjs +0 -7
- package/src/tui/spinner-verbs.mjs +1 -2
- package/src/tui/statusline-ansi-bridge.mjs +172 -0
- package/src/tui/statusline-ansi-bridge.test.mjs +159 -0
- package/src/tui/theme.mjs +746 -24
- package/src/tui/time-format.mjs +1 -1
- package/src/tui/transcript-tool-failures.mjs +67 -0
- package/src/tui/transcript-tool-failures.test.mjs +111 -0
- package/src/ui/ansi.mjs +1 -2
- package/src/ui/markdown.mjs +85 -26
- package/src/ui/markdown.test.mjs +70 -0
- package/src/ui/model-display.mjs +121 -0
- package/src/ui/session-stats.mjs +44 -0
- package/src/ui/statusline-context-label.test.mjs +15 -0
- package/src/ui/statusline.mjs +386 -178
- package/src/ui/tool-card.mjs +3 -16
- package/src/vendor/statusline/bin/statusline-lib.mjs +8 -4
- package/src/vendor/statusline/bin/statusline-route.mjs +169 -37
- package/src/vendor/statusline/bin/statusline-route.test.mjs +80 -0
- package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +3 -3
- package/src/vendor/statusline/src/gateway/claude-current.mjs +1 -1
- package/src/vendor/statusline/src/gateway/route-meta.mjs +44 -6
- package/src/vendor/statusline/src/gateway/session-routes.mjs +1 -1
- package/src/workflows/default/WORKFLOW.md +12 -5
- package/src/workflows/default/workflow.json +0 -1
- package/src/workflows/solo/WORKFLOW.md +15 -0
- package/src/workflows/solo/workflow.json +7 -0
- package/vendor/ink/build/output.js +6 -1
- package/src/agents/scheduler-task.md +0 -3
- package/src/agents/web-researcher/AGENT.md +0 -3
- package/src/agents/web-researcher/agent.json +0 -6
- package/src/agents/webhook-handler.md +0 -3
- package/src/rules/bridge/00-common.md +0 -5
- package/src/rules/bridge/30-explorer.md +0 -4
- package/src/rules/lead/00-tool-lead.md +0 -5
- package/src/rules/shared/00-language.md +0 -3
- package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
- package/src/runtime/agent/orchestrator/tools/mutation-content-cache.mjs +0 -67
- package/src/runtime/memory/lib/bridge-trace-queries.mjs +0 -120
- package/src/runtime/shared/llm/pid-cleanup.mjs +0 -27
- package/src/standalone/bridge-tool.mjs +0 -1414
- package/src/tui/runtime/shared/process-shutdown.mjs +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenAI Direct API — WebSocket transport via Responses API.
|
|
3
3
|
*
|
|
4
|
-
* Uses the same `sendViaWebSocket` plumbing as openai-oauth
|
|
4
|
+
* Uses the same `sendViaWebSocket` plumbing as openai-oauth, with two
|
|
5
5
|
* differences encoded in the `auth.type === 'openai-direct'` branch inside
|
|
6
6
|
* openai-oauth-ws.mjs:
|
|
7
7
|
* 1. Authorization header: Bearer <OPENAI_API_KEY> (no account_id, no
|
|
@@ -18,7 +18,7 @@ import { enrichModels } from './model-catalog.mjs';
|
|
|
18
18
|
import {
|
|
19
19
|
resolveProviderCacheKey,
|
|
20
20
|
resolveProviderPromptCacheLane,
|
|
21
|
-
} from '../
|
|
21
|
+
} from '../agent-runtime/cache-strategy.mjs';
|
|
22
22
|
|
|
23
23
|
const OPENAI_DIRECT_PRIORITY_MODEL_PATTERNS = Object.freeze([
|
|
24
24
|
/^gpt-5\.5(?:-\d{4}|$)/,
|
|
@@ -67,13 +67,13 @@ export class OpenAIDirectProvider {
|
|
|
67
67
|
promptCacheLane,
|
|
68
68
|
});
|
|
69
69
|
// Public OpenAI API priority support is documented separately from the
|
|
70
|
-
//
|
|
71
|
-
// so gpt-5.4-mini can opt into Priority even when the
|
|
70
|
+
// openai-oauth catalog. Keep this provider's service-tier decision local
|
|
71
|
+
// so gpt-5.4-mini can opt into Priority even when the OAuth catalog does
|
|
72
72
|
// not advertise a Fast tier for its OAuth endpoint.
|
|
73
73
|
applyOpenAIDirectFastTier(body, useModel, opts);
|
|
74
74
|
// Public Responses API supports prompt_cache_retention='24h' at no
|
|
75
75
|
// extra cost (same cached_input_tokens billing as the default 5–10
|
|
76
|
-
// min in-memory cache).
|
|
76
|
+
// min in-memory cache). openai-oauth rejects the parameter, so it's
|
|
77
77
|
// injected only on the direct path. See openai-oauth.mjs:290-294
|
|
78
78
|
// for the rationale.
|
|
79
79
|
body.prompt_cache_retention = '24h';
|
|
@@ -85,7 +85,7 @@ export class OpenAIDirectProvider {
|
|
|
85
85
|
// cacheKey (prompt_cache_key) only groups the server-side prefix-cache
|
|
86
86
|
// shard — safe to share across sessions, unlike the sessionId poolKey
|
|
87
87
|
// above. buildRequestBody derives it from the base namespace plus a
|
|
88
|
-
// model/system/tools hash, mirroring the
|
|
88
|
+
// model/system/tools hash, mirroring the openai-oauth path while keeping
|
|
89
89
|
// public OpenAI's 24h retention below.
|
|
90
90
|
const cacheKey = body.prompt_cache_key || resolveProviderCacheKey(opts, 'openai');
|
|
91
91
|
const iteration = Number.isFinite(Number(opts.iteration)) ? Number(opts.iteration) : null;
|
|
@@ -1,21 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { GrokOAuthProvider, hasGrokOAuthCredentials } from './grok-oauth.mjs';
|
|
8
|
-
import { OpenAIDirectProvider } from './openai-ws.mjs';
|
|
1
|
+
import { OPENAI_COMPAT_PRESETS } from './openai-compat-presets.mjs';
|
|
2
|
+
import {
|
|
3
|
+
hasAnthropicOAuthCredentials,
|
|
4
|
+
hasOpenAIOAuthCredentials,
|
|
5
|
+
hasGrokOAuthCredentials,
|
|
6
|
+
} from './oauth-credential-probes.mjs';
|
|
9
7
|
import { refreshCatalog as refreshMetadataCatalog, warmModelMetadataCatalogs } from './model-catalog.mjs';
|
|
10
|
-
// OpenAI-compat provider names are self-declared by openai-compat.mjs via
|
|
8
|
+
// OpenAI-compat provider names are self-declared by openai-compat-presets.mjs via
|
|
11
9
|
// OPENAI_COMPAT_PRESETS. No parallel list maintained here.
|
|
12
10
|
const providers = new Map();
|
|
11
|
+
const providerCtors = new Map();
|
|
12
|
+
const providerModulePromises = new Map();
|
|
13
13
|
// Parallel map: provider name -> signature of the config it was built from.
|
|
14
14
|
// Lets initProviders() skip reconstructing a provider whose config is byte-for-
|
|
15
15
|
// byte identical to the live one, so lazy-init misses that re-run initProviders
|
|
16
16
|
// don't churn (tear down + rebuild) every live provider instance on every call.
|
|
17
17
|
const signatures = new Map();
|
|
18
18
|
|
|
19
|
+
// Module-level init serialization. agent-tool.mjs's ensureProvider() already
|
|
20
|
+
// serializes inits per provider on a chain promise, but its gateOnPrior() lets
|
|
21
|
+
// a queued init PROCEED once a 120s gate expires even if the prior init has
|
|
22
|
+
// not settled — so a slow-but-still-running init and a newer one can reach
|
|
23
|
+
// initProviders() concurrently. This chain makes the clear()+rebuild section
|
|
24
|
+
// strictly sequential at the registry level, independent of any caller gating,
|
|
25
|
+
// so two different config signatures can never interleave their rebuilds.
|
|
26
|
+
let _initChain = Promise.resolve();
|
|
27
|
+
|
|
19
28
|
// Deterministic structural signature of a provider config. Recursively sorts
|
|
20
29
|
// object keys so signature equality reflects config-value equality regardless
|
|
21
30
|
// of key insertion order. Invariant: same config in -> same string out.
|
|
@@ -37,7 +46,51 @@ function configSignature(cfg) {
|
|
|
37
46
|
return null;
|
|
38
47
|
}
|
|
39
48
|
}
|
|
49
|
+
|
|
50
|
+
async function loadProviderExport(cacheKey, spec, exportName) {
|
|
51
|
+
if (!providerModulePromises.has(cacheKey)) {
|
|
52
|
+
providerModulePromises.set(cacheKey, import(spec));
|
|
53
|
+
}
|
|
54
|
+
const mod = await providerModulePromises.get(cacheKey);
|
|
55
|
+
const value = mod?.[exportName];
|
|
56
|
+
if (typeof value !== 'function') throw new Error(`provider export missing: ${exportName}`);
|
|
57
|
+
providerCtors.set(cacheKey, value);
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function loadProviderCtor(name) {
|
|
62
|
+
if (name === 'anthropic') return loadProviderExport('anthropic', './anthropic.mjs', 'AnthropicProvider');
|
|
63
|
+
if (name === 'gemini') return loadProviderExport('gemini', './gemini.mjs', 'GeminiProvider');
|
|
64
|
+
if (name === 'openai-oauth') return loadProviderExport('openai-oauth', './openai-oauth.mjs', 'OpenAIOAuthProvider');
|
|
65
|
+
if (name === 'anthropic-oauth') return loadProviderExport('anthropic-oauth', './anthropic-oauth.mjs', 'AnthropicOAuthProvider');
|
|
66
|
+
if (name === 'grok-oauth') return loadProviderExport('grok-oauth', './grok-oauth.mjs', 'GrokOAuthProvider');
|
|
67
|
+
if (name === 'openai') return loadProviderExport('openai', './openai-ws.mjs', 'OpenAIDirectProvider');
|
|
68
|
+
if (name === 'opencode-go') return loadProviderExport('opencode-go', './opencode-go.mjs', 'OpenCodeGoProvider');
|
|
69
|
+
if (Object.prototype.hasOwnProperty.call(OPENAI_COMPAT_PRESETS, name)) {
|
|
70
|
+
return loadProviderExport('openai-compat', './openai-compat.mjs', 'OpenAICompatProvider');
|
|
71
|
+
}
|
|
72
|
+
throw new Error(`unknown enabled provider: ${name}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function instantiateProvider(name, Ctor, cfg) {
|
|
76
|
+
if (Object.prototype.hasOwnProperty.call(OPENAI_COMPAT_PRESETS, name) && name !== 'opencode-go') {
|
|
77
|
+
return new Ctor(name, cfg);
|
|
78
|
+
}
|
|
79
|
+
return new Ctor(cfg);
|
|
80
|
+
}
|
|
81
|
+
|
|
40
82
|
export async function initProviders(config) {
|
|
83
|
+
// Serialize ALL inits through a single chain so two different config
|
|
84
|
+
// signatures can never run their clear()+rebuild concurrently, regardless
|
|
85
|
+
// of caller-side gating (agent-tool gateOnPrior may release a queued init
|
|
86
|
+
// before the prior one settled). Errors do not poison the chain.
|
|
87
|
+
const run = () => _initProvidersUnsynchronized(config);
|
|
88
|
+
const next = _initChain.then(run, run);
|
|
89
|
+
_initChain = next.then(() => {}, () => {});
|
|
90
|
+
return next;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function _initProvidersUnsynchronized(config) {
|
|
41
94
|
// Invariant: never wipe the live registry based on an empty / all-disabled
|
|
42
95
|
// config. Without this guard, a stale `loadAgentConfig()` (e.g. mid-reload
|
|
43
96
|
// or a transient FS hiccup) would land here as `{}` or `{...,enabled:false}`,
|
|
@@ -51,54 +104,30 @@ export async function initProviders(config) {
|
|
|
51
104
|
}
|
|
52
105
|
const next = new Map();
|
|
53
106
|
const nextSignatures = new Map();
|
|
54
|
-
|
|
55
|
-
if (!cfg.enabled)
|
|
56
|
-
continue;
|
|
107
|
+
const enabledResults = await Promise.all(entries.map(async ([name, cfg]) => {
|
|
108
|
+
if (!cfg.enabled) return null;
|
|
57
109
|
// Idempotent reuse: an enabled provider whose config signature is
|
|
58
110
|
// unchanged from the live registry is carried forward as-is. Only
|
|
59
111
|
// added or changed providers are (re)constructed below.
|
|
60
112
|
const sig = configSignature(cfg);
|
|
61
113
|
if (sig !== null && providers.has(name) && signatures.get(name) === sig) {
|
|
62
|
-
|
|
63
|
-
nextSignatures.set(name, sig);
|
|
64
|
-
continue;
|
|
114
|
+
return { name, inst: providers.get(name), sig };
|
|
65
115
|
}
|
|
66
116
|
try {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
else if (name === 'gemini') {
|
|
72
|
-
inst = new GeminiProvider(cfg);
|
|
73
|
-
}
|
|
74
|
-
else if (name === 'openai-oauth') {
|
|
75
|
-
inst = new OpenAIOAuthProvider(cfg);
|
|
76
|
-
}
|
|
77
|
-
else if (name === 'anthropic-oauth') {
|
|
78
|
-
inst = new AnthropicOAuthProvider(cfg);
|
|
79
|
-
}
|
|
80
|
-
else if (name === 'grok-oauth') {
|
|
81
|
-
inst = new GrokOAuthProvider(cfg);
|
|
82
|
-
}
|
|
83
|
-
else if (name === 'openai') {
|
|
84
|
-
inst = new OpenAIDirectProvider(cfg);
|
|
85
|
-
}
|
|
86
|
-
else if (name === 'opencode-go') {
|
|
87
|
-
inst = new OpenCodeGoProvider(cfg);
|
|
88
|
-
}
|
|
89
|
-
else if (Object.prototype.hasOwnProperty.call(OPENAI_COMPAT_PRESETS, name)) {
|
|
90
|
-
inst = new OpenAICompatProvider(name, cfg);
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
throw new Error(`unknown enabled provider: ${name}`);
|
|
94
|
-
}
|
|
95
|
-
next.set(name, inst);
|
|
96
|
-
if (sig !== null) nextSignatures.set(name, sig);
|
|
117
|
+
const Ctor = await loadProviderCtor(name);
|
|
118
|
+
const inst = instantiateProvider(name, Ctor, cfg);
|
|
119
|
+
return { name, inst, sig };
|
|
97
120
|
}
|
|
98
121
|
catch (err) {
|
|
99
122
|
const msg = err instanceof Error ? err.message : String(err);
|
|
100
|
-
|
|
123
|
+
return { name, error: new Error(`[provider] Failed to init "${name}": ${msg}`) };
|
|
101
124
|
}
|
|
125
|
+
}));
|
|
126
|
+
for (const result of enabledResults) {
|
|
127
|
+
if (!result) continue;
|
|
128
|
+
if (result.error) throw result.error;
|
|
129
|
+
next.set(result.name, result.inst);
|
|
130
|
+
if (result.sig !== null) nextSignatures.set(result.name, result.sig);
|
|
102
131
|
}
|
|
103
132
|
// Second guard: every entry was disabled. Same reasoning — keep the
|
|
104
133
|
// existing registry rather than going dark.
|
|
@@ -136,19 +165,27 @@ export function getProvider(name) {
|
|
|
136
165
|
// failure) — initProviders then skipped the entry entirely so there is
|
|
137
166
|
// nothing for the preservation guard to carry forward. Re-probe the
|
|
138
167
|
// credential each miss: if the credential is now valid, register the
|
|
139
|
-
// instance on the spot
|
|
168
|
+
// instance on the spot when that provider module has already been loaded,
|
|
169
|
+
// so subsequent calls hit the cached entry without making registry import
|
|
170
|
+
// pay every provider runtime at boot.
|
|
140
171
|
if (name === 'anthropic-oauth' && hasAnthropicOAuthCredentials()) {
|
|
141
|
-
const
|
|
172
|
+
const Ctor = providerCtors.get('anthropic-oauth');
|
|
173
|
+
if (!Ctor) return undefined;
|
|
174
|
+
const inst = new Ctor({});
|
|
142
175
|
providers.set(name, inst);
|
|
143
176
|
return inst;
|
|
144
177
|
}
|
|
145
178
|
if (name === 'openai-oauth' && hasOpenAIOAuthCredentials()) {
|
|
146
|
-
const
|
|
179
|
+
const Ctor = providerCtors.get('openai-oauth');
|
|
180
|
+
if (!Ctor) return undefined;
|
|
181
|
+
const inst = new Ctor({});
|
|
147
182
|
providers.set(name, inst);
|
|
148
183
|
return inst;
|
|
149
184
|
}
|
|
150
185
|
if (name === 'grok-oauth' && hasGrokOAuthCredentials()) {
|
|
151
|
-
const
|
|
186
|
+
const Ctor = providerCtors.get('grok-oauth');
|
|
187
|
+
if (!Ctor) return undefined;
|
|
188
|
+
const inst = new Ctor({});
|
|
152
189
|
providers.set(name, inst);
|
|
153
190
|
return inst;
|
|
154
191
|
}
|
|
@@ -173,7 +210,7 @@ export function getAllProviders() {
|
|
|
173
210
|
}
|
|
174
211
|
// Background catalog warm-up. Each provider's listModels() either hits its
|
|
175
212
|
// own cached model list (no-op) or fires a single HTTP refresh. Called from
|
|
176
|
-
// agent.init() after providers are registered so the first
|
|
213
|
+
// agent.init() after providers are registered so the first agent dispatch call
|
|
177
214
|
// (e.g. cycle1 on session start) does not pay the catalog refresh latency
|
|
178
215
|
// inline. Fire-and-forget: failures are logged inside each provider.
|
|
179
216
|
export function warmupCatalogs() {
|
|
@@ -27,10 +27,10 @@ import {
|
|
|
27
27
|
|
|
28
28
|
// HTTP statuses considered transient — safe to retry with backoff.
|
|
29
29
|
// 408 — request timeout
|
|
30
|
-
// 429 — rate limit (caller may still respect Retry-After, but the kind
|
|
31
|
-
// classification here only signals "retryable"; sub-error in the
|
|
32
|
-
// provider can still treat 429 as permanent for quota-exhausted)
|
|
33
30
|
// 500/502/503/504 — server errors (overload / bad gateway / timeout)
|
|
31
|
+
// 429 is deliberately excluded: rate-limit/quota windows are deterministic
|
|
32
|
+
// for the current call and must surface immediately instead of sleeping until
|
|
33
|
+
// an outer watchdog reports a misleading timeout.
|
|
34
34
|
const TRANSIENT_STATUSES = new Set([408, 500, 502, 503, 504])
|
|
35
35
|
|
|
36
36
|
// HTTP statuses that mean "permanent: stop retrying, surface to caller".
|
|
@@ -44,7 +44,7 @@ const PERMANENT_STATUSES = new Set([400, 404, 405, 410, 415, 422])
|
|
|
44
44
|
// payload but no explicit status code — we sniff the text and assign the most
|
|
45
45
|
// likely HTTP equivalent so the retry layer can use the same rules.
|
|
46
46
|
const MESSAGE_PATTERNS = [
|
|
47
|
-
// OpenAI/
|
|
47
|
+
// OpenAI OAuth/API sometimes surfaces generic backend failures only as
|
|
48
48
|
// message text plus a request ID, with no HTTP status on the WS event.
|
|
49
49
|
{ regex: /(?:an error occurred while processing your request|please include the request id)/i, status: 503 },
|
|
50
50
|
// Overload / transient 5xx — server is asking us to back off. The `\b`
|
|
@@ -58,7 +58,7 @@ const MESSAGE_PATTERNS = [
|
|
|
58
58
|
// by classifyError because per-call retry won't change the answer (the
|
|
59
59
|
// window must elapse). Providers that want time-bounded retry should
|
|
60
60
|
// honor Retry-After on the original response, not loop here.
|
|
61
|
-
{ regex: /(?:rate ?limit|quota)/i, status: 429 },
|
|
61
|
+
{ regex: /(?:rate[_ -]?limit|quota|too many requests|resource exhausted|insufficient_quota|quota_exceeded)/i, status: 429 },
|
|
62
62
|
// Auth — never retryable from our side.
|
|
63
63
|
{ regex: /\b(?:unauthorized|unauthorised|authentication|not authenticated|token expired|access token|invalid api key)\b/i, status: 401 },
|
|
64
64
|
{ regex: /\b(?:forbidden|permission denied|policy violation)\b/i, status: 403 },
|
|
@@ -212,6 +212,13 @@ export function isRetryable(err) {
|
|
|
212
212
|
const DEFAULT_BACKOFF_MS = PROVIDER_RETRY_BACKOFF_MS
|
|
213
213
|
const DEFAULT_MAX_ATTEMPTS = PROVIDER_RETRY_MAX_ATTEMPTS
|
|
214
214
|
|
|
215
|
+
export const MIDSTREAM_BACKOFF_MS = [250, 1000, 2000, 4000]
|
|
216
|
+
|
|
217
|
+
export function midstreamBackoffFor(retryNumber, schedule = MIDSTREAM_BACKOFF_MS) {
|
|
218
|
+
const raw = schedule[Math.min(Math.max(retryNumber, 1), schedule.length) - 1]
|
|
219
|
+
return jitterDelayMs(raw)
|
|
220
|
+
}
|
|
221
|
+
|
|
215
222
|
export function jitterDelayMs(ms, ratio = PROVIDER_RETRY_JITTER_RATIO, mode = 'symmetric') {
|
|
216
223
|
const base = Number(ms) || 0
|
|
217
224
|
if (base <= 0) return 0
|
|
@@ -296,13 +303,17 @@ export async function withRetry(fn, opts = {}) {
|
|
|
296
303
|
}
|
|
297
304
|
lastErr = caught
|
|
298
305
|
populateHttpStatusFromMessage(caught)
|
|
299
|
-
const retryAfterMs = retryAfterMsFromError(caught)
|
|
300
306
|
const status = Number(caught?.httpStatus || caught?.status || caught?.response?.status || 0)
|
|
301
307
|
const kind = classifyError(caught)
|
|
302
|
-
const
|
|
303
|
-
|
|
308
|
+
const unsafeToRetry = caught?.unsafeToRetry === true
|
|
309
|
+
|| caught?.providerQuota === true
|
|
310
|
+
|| caught?.quotaExceeded === true
|
|
311
|
+
if (unsafeToRetry) throw caught
|
|
312
|
+
if (status === 429) throw caught
|
|
313
|
+
if (kind !== 'transient') throw caught
|
|
304
314
|
// Last attempt failed transiently — propagate to caller.
|
|
305
315
|
if (attempt === maxAttempts - 1) throw caught
|
|
316
|
+
const retryAfterMs = retryAfterMsFromError(caught)
|
|
306
317
|
if (retryAfterMs != null) {
|
|
307
318
|
nextDelayMs = Math.max(0, Math.min(retryAfterMs, maxRetryAfterMs))
|
|
308
319
|
nextDelayReason = 'retry-after'
|
|
@@ -316,7 +327,7 @@ export async function withRetry(fn, opts = {}) {
|
|
|
316
327
|
}
|
|
317
328
|
|
|
318
329
|
function _sleepWithAbort(ms, signal) {
|
|
319
|
-
return new Promise((resolve) => {
|
|
330
|
+
return new Promise((resolve, reject) => {
|
|
320
331
|
let onAbort = null
|
|
321
332
|
const t = setTimeout(() => {
|
|
322
333
|
if (signal && onAbort) {
|
|
@@ -325,8 +336,17 @@ function _sleepWithAbort(ms, signal) {
|
|
|
325
336
|
resolve()
|
|
326
337
|
}, ms)
|
|
327
338
|
if (!signal) return
|
|
328
|
-
if (signal.aborted) {
|
|
329
|
-
|
|
339
|
+
if (signal.aborted) {
|
|
340
|
+
clearTimeout(t)
|
|
341
|
+
const reason = signal.reason
|
|
342
|
+
reject(reason instanceof Error ? reason : new Error('sleep aborted'))
|
|
343
|
+
return
|
|
344
|
+
}
|
|
345
|
+
onAbort = () => {
|
|
346
|
+
clearTimeout(t)
|
|
347
|
+
const reason = signal.reason
|
|
348
|
+
reject(reason instanceof Error ? reason : new Error('sleep aborted'))
|
|
349
|
+
}
|
|
330
350
|
signal.addEventListener('abort', onAbort, { once: true })
|
|
331
351
|
})
|
|
332
352
|
}
|
|
@@ -1,11 +1,44 @@
|
|
|
1
|
-
let routeMeta;
|
|
1
|
+
let routeMeta = null;
|
|
2
|
+
let routeMetaPromise = null;
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
routeMeta
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
async function loadRouteMetaModule() {
|
|
5
|
+
if (routeMeta) return routeMeta;
|
|
6
|
+
if (!routeMetaPromise) {
|
|
7
|
+
routeMetaPromise = import('../../../../vendor/statusline/src/gateway/route-meta.mjs').then((mod) => {
|
|
8
|
+
routeMeta = mod;
|
|
9
|
+
return mod;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
return await routeMetaPromise;
|
|
14
|
+
} catch (err) {
|
|
15
|
+
routeMetaPromise = null;
|
|
16
|
+
routeMeta = null;
|
|
17
|
+
throw err;
|
|
18
|
+
}
|
|
7
19
|
}
|
|
8
20
|
|
|
9
|
-
|
|
10
|
-
export
|
|
11
|
-
|
|
21
|
+
/** Await before calling the sync exports when route-meta may not be ready yet. */
|
|
22
|
+
export async function ensureStatuslineRouteMetaLoaded() {
|
|
23
|
+
return loadRouteMetaModule();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function requireRouteMeta() {
|
|
27
|
+
if (!routeMeta) {
|
|
28
|
+
void loadRouteMetaModule().catch(() => {});
|
|
29
|
+
throw new Error('statusline route-meta not loaded');
|
|
30
|
+
}
|
|
31
|
+
return routeMeta;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function buildGatewayLimits(routeInfo, providerOut = null, usageSnapshot = null) {
|
|
35
|
+
return requireRouteMeta().buildGatewayLimits(routeInfo, providerOut, usageSnapshot);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function recordGatewayUsageEvent(summary) {
|
|
39
|
+
return requireRouteMeta().recordGatewayUsageEvent(summary);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function summarizeGatewayUsage(routeInfo, providerOut, compact = null, durationMs = null) {
|
|
43
|
+
return requireRouteMeta().summarizeGatewayUsage(routeInfo, providerOut, compact, durationMs);
|
|
44
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
// Post-
|
|
1
|
+
// Post-patch advisory marks: one-shot sidecar on the next read after a patch.
|
|
2
2
|
import { _normalizeAbs } from './util.mjs';
|
|
3
3
|
|
|
4
4
|
// sessionId -> Map<absPath, { ts, toolName }>
|
|
5
5
|
const _postEditBySession = new Map();
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Mark `path` as just-
|
|
9
|
-
* successful
|
|
8
|
+
* Mark `path` as just-patched for `sessionId`. Caller invokes this after a
|
|
9
|
+
* successful apply_patch so the next read on the same path can
|
|
10
10
|
* receive a one-shot advisory sidecar.
|
|
11
11
|
*/
|
|
12
12
|
export function markPostEdit({ sessionId, path, cwd, toolName }) {
|
|
@@ -15,7 +15,7 @@ export function markPostEdit({ sessionId, path, cwd, toolName }) {
|
|
|
15
15
|
if (!abs) return;
|
|
16
16
|
let m = _postEditBySession.get(sessionId);
|
|
17
17
|
if (!m) { m = new Map(); _postEditBySession.set(sessionId, m); }
|
|
18
|
-
m.set(abs, { ts: Date.now(), toolName: String(toolName || '
|
|
18
|
+
m.set(abs, { ts: Date.now(), toolName: String(toolName || 'apply_patch') });
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -222,7 +222,7 @@ export function setReadCached({ sessionId, args, cwd, content, toolUseId }) {
|
|
|
222
222
|
|
|
223
223
|
/**
|
|
224
224
|
* Invalidate all cache entries for `path` in the given session. Called when
|
|
225
|
-
* a
|
|
225
|
+
* a mutation tool (apply_patch) touches the path.
|
|
226
226
|
*/
|
|
227
227
|
export function invalidatePathForSession(sessionId, path, cwd) {
|
|
228
228
|
if (!sessionId) return;
|