mixdog 0.9.63 → 0.9.64
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 +3 -0
- package/package.json +9 -2
- package/scripts/code-graph-description-contract.mjs +3 -3
- package/scripts/verify-release-assets.mjs +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +11 -14
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -6
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -2
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
- package/src/runtime/agent/orchestrator/config.mjs +19 -124
- package/src/runtime/agent/orchestrator/context/collect.mjs +6 -6
- package/src/runtime/agent/orchestrator/dispatch-persist.mjs +2 -2
- package/src/runtime/agent/orchestrator/internal-agents.mjs +2 -2
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +1 -1
- package/src/runtime/agent/orchestrator/mcp/client.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +333 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -277
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +6 -6
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +196 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +460 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +14 -593
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -280
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +357 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-headers.mjs +216 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +2 -188
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/compact/file-reattach.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +17 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +18 -26
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +49 -0
- package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +49 -0
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +260 -0
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +72 -0
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +163 -0
- package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/store.mjs +9 -561
- package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +11 -11
- package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +2 -154
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/cwd-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/path-locks.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +2 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-open.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +519 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +5 -449
- package/src/runtime/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +212 -0
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +1 -7
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +9 -36
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +15 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +429 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +2 -395
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/mutation-planner.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/next-call-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +72 -9
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/result-compression.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +2 -444
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +505 -0
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/backend-dispatch.mjs +20 -5
- package/src/runtime/channels/lib/scheduler.mjs +1 -1
- package/src/runtime/channels/lib/telegram-format.mjs +1 -1
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +4 -4
- package/src/runtime/channels/tool-defs.mjs +2 -3
- package/src/runtime/memory/index.mjs +1 -5
- package/src/runtime/memory/lib/core-memory-candidates.mjs +357 -0
- package/src/runtime/memory/lib/core-memory-store.mjs +7 -357
- package/src/runtime/memory/lib/cycle-signatures.mjs +1 -1
- package/src/runtime/memory/lib/embedding-model-config.mjs +1 -1
- package/src/runtime/memory/lib/embedding-provider.mjs +1 -5
- package/src/runtime/memory/lib/embedding-worker.mjs +1 -5
- package/src/runtime/memory/lib/ko-morph.mjs +2 -2
- package/src/runtime/memory/lib/memory-config-flags.mjs +2 -5
- package/src/runtime/memory/lib/memory-cycle-requests.mjs +3 -7
- package/src/runtime/memory/lib/memory-cycle1.mjs +4 -8
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +2 -5
- package/src/runtime/memory/lib/memory-cycle3.mjs +1 -5
- package/src/runtime/memory/lib/memory-embed.mjs +2 -6
- package/src/runtime/memory/lib/memory-log.mjs +8 -0
- package/src/runtime/memory/lib/memory-ops-policy.mjs +4 -8
- package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +1 -1
- package/src/runtime/memory/lib/memory-recall-store.mjs +2 -193
- package/src/runtime/memory/lib/memory-score.mjs +1 -1
- package/src/runtime/memory/lib/memory-text-utils.mjs +2 -2
- package/src/runtime/memory/lib/memory.mjs +2 -6
- package/src/runtime/memory/lib/pg/adapter.mjs +2 -6
- package/src/runtime/memory/lib/pg/process.mjs +1 -5
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -5
- package/src/runtime/memory/lib/recall-format.mjs +2 -2
- package/src/runtime/memory/lib/recall-scoring.mjs +195 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -5
- package/src/runtime/memory/lib/trace-store.mjs +2 -6
- package/src/runtime/search/lib/http-fetch.mjs +6 -6
- package/src/runtime/search/lib/web-tools.mjs +2 -2
- package/src/runtime/shared/buffered-appender.mjs +1 -1
- package/src/runtime/shared/child-guardian.mjs +1 -1
- package/src/runtime/shared/child-spawn-gate.mjs +1 -1
- package/src/runtime/shared/config.mjs +4 -4
- package/src/runtime/shared/launcher-control.mjs +9 -10
- package/src/runtime/shared/llm/index.mjs +5 -20
- package/src/runtime/shared/markdown-frontmatter.mjs +1 -1
- package/src/runtime/shared/memory-snapshot.mjs +4 -4
- package/src/runtime/shared/pristine-execution.mjs +1 -1
- package/src/runtime/shared/process-lifecycle.mjs +0 -1
- package/src/runtime/shared/process-shutdown.mjs +1 -1
- package/src/runtime/shared/resource-admission.mjs +2 -2
- package/src/runtime/shared/schedule-session-run.mjs +1 -1
- package/src/runtime/shared/schedules-db.mjs +1 -1
- package/src/runtime/shared/service-discovery.mjs +2 -2
- package/src/runtime/shared/staged-update.mjs +6 -6
- package/src/runtime/shared/tool-execution-contract.mjs +1 -1
- package/src/runtime/shared/tool-primitives.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +2 -2
- package/src/runtime/shared/update-checker.mjs +1 -1
- package/src/runtime/shared/user-cwd.mjs +3 -3
- package/src/runtime/shared/user-data-guard.mjs +4 -4
- package/src/runtime/shared/workspace-router.mjs +5 -5
- package/src/session-runtime/config-helpers.mjs +2 -3
- package/src/session-runtime/config-lifecycle.mjs +1 -1
- package/src/session-runtime/hitch-profile.mjs +1 -1
- package/src/session-runtime/lifecycle-api.mjs +6 -4
- package/src/session-runtime/model-recency.mjs +5 -5
- package/src/session-runtime/plugin-mcp.mjs +1 -1
- package/src/session-runtime/provider-request-snapshot.mjs +338 -0
- package/src/session-runtime/session-text.mjs +4 -4
- package/src/session-runtime/tool-catalog-schema.mjs +197 -0
- package/src/session-runtime/tool-catalog.mjs +6 -483
- package/src/session-runtime/tool-defs.mjs +1 -1
- package/src/standalone/agent-task-status.mjs +1 -1
- package/src/standalone/agent-tool/helpers.mjs +2 -2
- package/src/standalone/channel-admin.mjs +4 -4
- package/src/standalone/explore-tool.mjs +3 -3
- package/src/standalone/hook-bus/config.mjs +1 -1
- package/src/standalone/hook-bus/constants.mjs +1 -1
- package/src/standalone/hook-bus/rules.mjs +1 -1
- package/src/standalone/projects.mjs +1 -1
- package/src/standalone/provider-admin.mjs +2 -2
- package/src/tui/app/app-format.mjs +2 -2
- package/src/tui/app/clipboard.mjs +3 -3
- package/src/tui/app/input-parsers.mjs +1 -1
- package/src/tui/app/text-layout.mjs +1 -1
- package/src/tui/app/transcript-row-estimate.mjs +360 -0
- package/src/tui/app/transcript-window.mjs +5 -347
- package/src/tui/app/use-mouse-input.mjs +1 -1
- package/src/tui/app/use-prompt-handlers.mjs +1 -1
- package/src/tui/components/tool-execution/surface-detail.mjs +9 -9
- package/src/tui/components/tool-execution/text-format.mjs +2 -2
- package/src/tui/components/tool-output-format.mjs +7 -7
- package/src/tui/display-width.mjs +4 -4
- package/src/tui/dist/index.mjs +63 -97
- package/src/tui/engine/agent-envelope.mjs +5 -5
- package/src/tui/engine/notification-plan.mjs +1 -1
- package/src/tui/engine/queue-helpers.mjs +3 -3
- package/src/tui/engine/render-timing.mjs +4 -5
- package/src/tui/engine/session-api-ext.mjs +3 -3
- package/src/tui/engine/tool-result-status.mjs +5 -5
- package/src/tui/engine/tool-result-text.mjs +1 -1
- package/src/tui/engine/transcript-spill.mjs +585 -0
- package/src/tui/engine/tui-steering-persist.mjs +1 -1
- package/src/tui/engine.mjs +2 -474
- package/src/tui/lib/voice-setup.mjs +1 -1
- package/src/tui/markdown/format-token.mjs +1 -1
- package/src/tui/markdown/render-ansi.mjs +1 -1
- package/src/tui/markdown/table-layout.mjs +4 -4
- package/src/tui/paste-attachments.mjs +1 -1
- package/src/tui/prompt-history-store.mjs +3 -3
- package/src/tui/statusline-ansi-bridge.mjs +2 -2
- package/src/tui/theme.mjs +1 -1
- package/src/tui/transcript-tool-failures.mjs +2 -2
- package/src/ui/ansi.mjs +0 -9
- package/src/ui/statusline-format.mjs +2 -4
- package/scripts/_bench-cwc.json +0 -20
- package/scripts/_jitter-fuzz.jsx +0 -42
- package/scripts/_jitter-fuzz2.jsx +0 -30
- package/scripts/_jitter-probe.jsx +0 -35
- package/scripts/_jp2.jsx +0 -16
- package/scripts/_smoke_wd.mjs +0 -7
- package/scripts/abort-recovery-test.mjs +0 -175
- package/scripts/agent-dispatch-abort-compose-test.mjs +0 -31
- package/scripts/agent-live-arg-guard-smoke.mjs +0 -20
- package/scripts/agent-live-path-suffix-smoke.mjs +0 -34
- package/scripts/agent-live-toolcall-args-smoke.mjs +0 -45
- package/scripts/agent-loop-policy-test.mjs +0 -37
- package/scripts/agent-model-liveness-test.mjs +0 -763
- package/scripts/agent-parallel-smoke.mjs +0 -435
- package/scripts/agent-route-batch-test.mjs +0 -40
- package/scripts/agent-tag-reuse-smoke.mjs +0 -441
- package/scripts/agent-terminal-reap-test.mjs +0 -252
- package/scripts/agent-trace-io-test.mjs +0 -133
- package/scripts/ansi-color-capability-test.mjs +0 -90
- package/scripts/anthropic-admission-retry-integration-test.mjs +0 -119
- package/scripts/anthropic-maxtokens-test.mjs +0 -119
- package/scripts/anthropic-oauth-refresh-race-test.mjs +0 -397
- package/scripts/anthropic-transport-policy-test.mjs +0 -726
- package/scripts/apply-patch-edit-smoke.mjs +0 -71
- package/scripts/arg-guard-test.mjs +0 -93
- package/scripts/async-notify-settlement-test.mjs +0 -99
- package/scripts/atomic-lock-tryonce-test.mjs +0 -125
- package/scripts/background-task-meta-smoke.mjs +0 -38
- package/scripts/bench-run.mjs +0 -508
- package/scripts/boot-smoke.mjs +0 -137
- package/scripts/build-runtime-windows.ps1 +0 -242
- package/scripts/channel-daemon-smoke.mjs +0 -1103
- package/scripts/code-graph-aggregate-cwd-test.mjs +0 -158
- package/scripts/code-graph-disk-hit-test.mjs +0 -290
- package/scripts/code-graph-dispatch-test.mjs +0 -96
- package/scripts/code-graph-root-federation-test.mjs +0 -273
- package/scripts/compact-active-turn-test.mjs +0 -68
- package/scripts/compact-file-reattach-test.mjs +0 -88
- package/scripts/compact-pressure-test.mjs +0 -788
- package/scripts/compact-prior-context-flatten-test.mjs +0 -252
- package/scripts/compact-recall-digest-test.mjs +0 -57
- package/scripts/compact-smoke.mjs +0 -1017
- package/scripts/compact-trigger-migration-smoke.mjs +0 -261
- package/scripts/compacted-placeholder-scrub-test.mjs +0 -63
- package/scripts/context-mcp-metering-test.mjs +0 -1406
- package/scripts/debounced-skills-async-save-test.mjs +0 -57
- package/scripts/deferred-tool-loading-test.mjs +0 -250
- package/scripts/desktop-session-bridge-test.mjs +0 -1013
- package/scripts/dispatch-persist-recovery-test.mjs +0 -141
- package/scripts/embedding-worker-exit-test.mjs +0 -76
- package/scripts/execution-completion-dedup-test.mjs +0 -205
- package/scripts/execution-pending-resume-kick-test.mjs +0 -151
- package/scripts/execution-resume-esc-integration-test.mjs +0 -176
- package/scripts/explore-bench-tmp.mjs +0 -36
- package/scripts/explore-bench.mjs +0 -248
- package/scripts/explore-prompt-policy-test.mjs +0 -256
- package/scripts/explore-timeout-cancel-test.mjs +0 -345
- package/scripts/find-fuzzy-hidden-test.mjs +0 -267
- package/scripts/forwarder-rebind-tail-test.mjs +0 -67
- package/scripts/freevar-smoke.mjs +0 -98
- package/scripts/gemini-provider-test.mjs +0 -1448
- package/scripts/generate-oc-icons.mjs +0 -11
- package/scripts/grok-oauth-refresh-race-test.mjs +0 -273
- package/scripts/headless-pristine-execution-test.mjs +0 -614
- package/scripts/hook-bus-test.mjs +0 -552
- package/scripts/ingest-pure-conversation-smoke.mjs +0 -175
- package/scripts/internal-comms-bench-test.mjs +0 -226
- package/scripts/internal-comms-bench.mjs +0 -853
- package/scripts/internal-comms-smoke.mjs +0 -277
- package/scripts/internal-tools-normalization-test.mjs +0 -62
- package/scripts/interrupted-turn-history-test.mjs +0 -399
- package/scripts/lead-workflow-smoke.mjs +0 -592
- package/scripts/lifecycle-api-test.mjs +0 -137
- package/scripts/live-share-test.mjs +0 -148
- package/scripts/live-worker-smoke.mjs +0 -333
- package/scripts/log-writer-guard-smoke.mjs +0 -131
- package/scripts/maintenance-default-routes-test.mjs +0 -164
- package/scripts/max-output-recovery-persist-test.mjs +0 -81
- package/scripts/max-output-recovery-test.mjs +0 -368
- package/scripts/mcp-client-normalization-test.mjs +0 -45
- package/scripts/mcp-grace-deferred-test.mjs +0 -225
- package/scripts/memory-core-input-test.mjs +0 -167
- package/scripts/memory-cycle-routing-test.mjs +0 -111
- package/scripts/memory-meta-concurrency-test.mjs +0 -20
- package/scripts/memory-pg-recovery-test.mjs +0 -59
- package/scripts/memory-rule-contract-test.mjs +0 -93
- package/scripts/model-list-sanitize-test.mjs +0 -37
- package/scripts/mouse-tracking-restore-smoke.mjs +0 -107
- package/scripts/native-edit-wire-test.mjs +0 -152
- package/scripts/notify-completion-mirror-test.mjs +0 -73
- package/scripts/openai-oauth-refresh-race-test.mjs +0 -120
- package/scripts/openai-oauth-ws-1006-retry-test.mjs +0 -954
- package/scripts/openai-ws-early-settle-test.mjs +0 -216
- package/scripts/output-style-bench.mjs +0 -292
- package/scripts/output-style-smoke.mjs +0 -145
- package/scripts/parent-abort-link-test.mjs +0 -90
- package/scripts/patch-binary-cache-test.mjs +0 -181
- package/scripts/path-suffix-test.mjs +0 -57
- package/scripts/pending-completion-drop-test.mjs +0 -239
- package/scripts/pending-messages-lock-nonblocking-test.mjs +0 -62
- package/scripts/pretool-ask-runtime-test.mjs +0 -54
- package/scripts/process-lifecycle-test.mjs +0 -461
- package/scripts/prompt-immediate-render-test.mjs +0 -89
- package/scripts/prompt-input-parity-test.mjs +0 -145
- package/scripts/provider-admission-scheduler-test.mjs +0 -681
- package/scripts/provider-contract-test.mjs +0 -632
- package/scripts/provider-stream-stall-test.mjs +0 -276
- package/scripts/provider-toolcall-test.mjs +0 -3063
- package/scripts/reactive-compact-persist-smoke.mjs +0 -203
- package/scripts/recall-bench.mjs +0 -359
- package/scripts/resource-admission-test.mjs +0 -792
- package/scripts/result-classification-test.mjs +0 -75
- package/scripts/rg-runner-test.mjs +0 -240
- package/scripts/routing-corpus-test.mjs +0 -349
- package/scripts/sanitize-tool-pairs-test.mjs +0 -260
- package/scripts/session-bench-cache-break-test.mjs +0 -102
- package/scripts/session-bench.mjs +0 -1708
- package/scripts/session-context-bench.mjs +0 -344
- package/scripts/session-heartbeat-lifecycle-test.mjs +0 -68
- package/scripts/session-ingest-compaction-smoke.mjs +0 -279
- package/scripts/session-ingest-smoke.mjs +0 -177
- package/scripts/session-orphan-sweep-test.mjs +0 -109
- package/scripts/set-effort-config-test.mjs +0 -41
- package/scripts/shell-failure-diagnostics-test.mjs +0 -280
- package/scripts/shell-hardening-test.mjs +0 -209
- package/scripts/shell-jobs-windows-hide-test.mjs +0 -164
- package/scripts/ship-mode-test.mjs +0 -98
- package/scripts/smoke-loop-failure-summary-test.mjs +0 -38
- package/scripts/smoke-loop-failure-summary.mjs +0 -16
- package/scripts/smoke-loop-report.mjs +0 -221
- package/scripts/smoke-loop.mjs +0 -202
- package/scripts/smoke-runtime-negative.ps1 +0 -106
- package/scripts/statusline-quota-hysteresis-test.mjs +0 -62
- package/scripts/steering-drain-buckets-test.mjs +0 -467
- package/scripts/steering-fold-provenance-test.mjs +0 -71
- package/scripts/streaming-tail-window-test.mjs +0 -175
- package/scripts/submit-commandbusy-race-test.mjs +0 -257
- package/scripts/task-bench.mjs +0 -205
- package/scripts/terminal-bench-isolation-guards-test.mjs +0 -102
- package/scripts/termio-input-smoke.mjs +0 -199
- package/scripts/tool-result-hook-test.mjs +0 -48
- package/scripts/tool-smoke.mjs +0 -2453
- package/scripts/tool-tui-presentation-test.mjs +0 -286
- package/scripts/toolcall-args-test.mjs +0 -239
- package/scripts/tui-ambiguous-width-test.mjs +0 -113
- package/scripts/tui-background-failure-smoke.mjs +0 -73
- package/scripts/tui-perf-run.ps1 +0 -26
- package/scripts/tui-render-smoke.mjs +0 -90
- package/scripts/tui-runtime-load-bench-entry.jsx +0 -608
- package/scripts/tui-runtime-load-bench.mjs +0 -50
- package/scripts/tui-store-frame-batch-test.mjs +0 -99
- package/scripts/tui-transcript-jitter-harness-entry.jsx +0 -383
- package/scripts/tui-transcript-perf-test.mjs +0 -934
- package/scripts/usage-metrics-epoch-smoke.mjs +0 -114
- package/scripts/verify-release-assets-test.mjs +0 -281
- package/scripts/web-fetch-routing-test.mjs +0 -158
- package/scripts/webhook-smoke.mjs +0 -201
- package/scripts/windows-hide-spawn-options-test.mjs +0 -19
- package/scripts/worker-notify-rejection-test.mjs +0 -51
- package/scripts/write-backpressure-test.mjs +0 -147
|
@@ -31,429 +31,10 @@ import { normalizeGrokToolSchemas } from './lib/grok-tool-schema.mjs';
|
|
|
31
31
|
|
|
32
32
|
// --- Constants ---
|
|
33
33
|
// xAI's shared OAuth client. The consent screen renders this as "Grok Build".
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const SCOPE = 'openid profile email offline_access grok-cli:access api:access';
|
|
38
|
-
const CALLBACK_HOST = '127.0.0.1';
|
|
39
|
-
const CALLBACK_PORT = 56121;
|
|
40
|
-
const CALLBACK_PATH = '/callback';
|
|
41
|
-
const REDIRECT_URI = `http://${CALLBACK_HOST}:${CALLBACK_PORT}${CALLBACK_PATH}`;
|
|
42
|
-
// Primary inference + search target. The OAuth token's `api:access` scope works
|
|
43
|
-
// against the STANDARD xAI API (GET /models 200, POST /responses 200, web_search
|
|
44
|
-
// 200 with citations). grok-4.x and web search live here. Proxy-only models
|
|
45
|
-
// (grok-build, grok-composer-2.5-fast) are NOT published on api.x.ai — they route
|
|
46
|
-
// to PROXY_BASE_URL below, which is version-gated and needs the Grok CLI client
|
|
47
|
-
// headers (see proxyHeaders).
|
|
48
|
-
const INFERENCE_BASE_URL = 'https://api.x.ai/v1';
|
|
49
|
-
const TOKEN_REFRESH_SKEW_MS = 5 * 60_000;
|
|
34
|
+
import { INFERENCE_BASE_URL, TOKEN_REFRESH_SKEW_MS, PROXY_BASE_URL, isProxyOnlyModel, proxyHeaders, resolveGrokOAuthResponsesTransport, normalizeGrokModelId, MODEL_CACHE_TTL_MS, GROK_MODEL_CACHE_SCHEMA_VERSION, getOwnTokenPath, _mtimeMs, _loadOwnTokens, loadTokens, _getRefreshInFlight, _setRefreshInFlight, refreshTokens } from './grok-oauth-tokens.mjs';
|
|
35
|
+
export { hasGrokOAuthCredentials, describeGrokOAuthCredentials, forgetGrokOAuthCredentials, normalizeGrokModelId } from './grok-oauth-tokens.mjs';
|
|
36
|
+
export { beginOAuthLogin, loginOAuth } from './grok-oauth-login.mjs';
|
|
50
37
|
|
|
51
|
-
// --- grok-build CLI proxy (Composer 2.5, grok-build) ---
|
|
52
|
-
// These models live ONLY on the grok-build proxy, not api.x.ai. /models is
|
|
53
|
-
// readable with the bare OAuth bearer; /responses is version-gated (HTTP 426)
|
|
54
|
-
// and requires the Grok CLI's client headers. We route only proxy-only models
|
|
55
|
-
// here and keep grok-4.x + search on api.x.ai.
|
|
56
|
-
const PROXY_BASE_URL = 'https://cli-chat-proxy.grok.com/v1';
|
|
57
|
-
const GROK_CLIENT_IDENTIFIER = 'grok-shell';
|
|
58
|
-
const GROK_CLI_VERSION_FALLBACK = '0.2.16';
|
|
59
|
-
|
|
60
|
-
// Route to the proxy: any grok-composer* model, plus the bare `grok-build`
|
|
61
|
-
// coding agent. NOT grok-build-0.1 — that is a real api.x.ai model and must stay
|
|
62
|
-
// on api.x.ai, so we match grok-build exactly rather than by prefix.
|
|
63
|
-
const PROXY_EXACT_MODELS = new Set(['grok-build']);
|
|
64
|
-
function isProxyOnlyModel(model) {
|
|
65
|
-
const m = String(model || '');
|
|
66
|
-
return /^grok-composer/i.test(m) || PROXY_EXACT_MODELS.has(m);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Use a Mixdog-controlled client version for the proxy version gate.
|
|
70
|
-
let _grokCliVersionCache = null;
|
|
71
|
-
function grokCliVersion() {
|
|
72
|
-
if (_grokCliVersionCache) return _grokCliVersionCache;
|
|
73
|
-
_grokCliVersionCache = String(process.env.MIXDOG_GROK_CLIENT_VERSION || '').trim() || GROK_CLI_VERSION_FALLBACK;
|
|
74
|
-
return _grokCliVersionCache;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Headers the Grok CLI sends to clear the proxy version gate — extracted from
|
|
78
|
-
// the grok binary: x-grok-client-version (the actual 426 gate),
|
|
79
|
-
// x-grok-client-identifier, and a matching User-Agent.
|
|
80
|
-
function proxyHeaders({ model, sendOpts, userId } = {}) {
|
|
81
|
-
const v = grokCliVersion();
|
|
82
|
-
const headers = {
|
|
83
|
-
'x-grok-client-version': v,
|
|
84
|
-
'x-grok-client-identifier': GROK_CLIENT_IDENTIFIER,
|
|
85
|
-
'User-Agent': `xai-grok-build/${v}`,
|
|
86
|
-
};
|
|
87
|
-
const sessionId = String(sendOpts?.sessionId || sendOpts?.session?.id || '').trim();
|
|
88
|
-
const requestId = String(sendOpts?.requestId || '').trim() || (sendOpts ? randomUUID() : '');
|
|
89
|
-
const turnIndex = sendOpts?.iteration;
|
|
90
|
-
// Mixdog has an authoritative session id but no distinct Grok conversation
|
|
91
|
-
// id. Do not synthesize the latter from the former.
|
|
92
|
-
if (sessionId) headers['x-grok-session-id'] = sessionId;
|
|
93
|
-
if (requestId) headers['x-grok-req-id'] = requestId;
|
|
94
|
-
if (model) headers['x-grok-model-override'] = String(model);
|
|
95
|
-
if (turnIndex != null && Number.isFinite(Number(turnIndex))) {
|
|
96
|
-
headers['x-grok-turn-idx'] = String(turnIndex);
|
|
97
|
-
}
|
|
98
|
-
if (userId) headers['x-grok-user-id'] = String(userId);
|
|
99
|
-
return headers;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function resolveGrokOAuthResponsesTransport() {
|
|
103
|
-
// The OAuth bearer is session auth and must never escape to api.x.ai.
|
|
104
|
-
// Mixdog's xAI WebSocket connector has a fixed api.x.ai endpoint, so OAuth
|
|
105
|
-
// inference is pinned to the proxy's reference HTTP/SSE transport.
|
|
106
|
-
return 'http';
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Retired model aliases xAI no longer exposes by their old ids. The live
|
|
110
|
-
// catalog surfaces the coding model as grok-build-0.1; map the legacy ids to
|
|
111
|
-
// it so a stale config selection doesn't hit a model-not-found. Exact table,
|
|
112
|
-
// not a heuristic. Mirrors openclaw extensions/xai/model-definitions.ts.
|
|
113
|
-
const RETIRED_MODEL_ALIASES = Object.freeze({
|
|
114
|
-
'grok-code-fast-1': 'grok-build-0.1',
|
|
115
|
-
'grok-code-fast': 'grok-build-0.1',
|
|
116
|
-
'grok-code-fast-1-0825': 'grok-build-0.1',
|
|
117
|
-
});
|
|
118
|
-
export function normalizeGrokModelId(id) {
|
|
119
|
-
return (id && RETIRED_MODEL_ALIASES[id]) || id;
|
|
120
|
-
}
|
|
121
|
-
const MODEL_CACHE_TTL_MS = 24 * 60 * 60_000;
|
|
122
|
-
// Bump when the on-disk cache shape changes so stale-shape entries are
|
|
123
|
-
// discarded instead of misread.
|
|
124
|
-
const GROK_MODEL_CACHE_SCHEMA_VERSION = 1;
|
|
125
|
-
const DISCOVERY_TIMEOUT_MS = 15_000;
|
|
126
|
-
const TOKEN_TIMEOUT_MS = 30_000;
|
|
127
|
-
const LOGIN_TIMEOUT_MS = 5 * 60_000;
|
|
128
|
-
|
|
129
|
-
// SSRF guard for any endpoint pulled from the discovery document or saved
|
|
130
|
-
// tokens. xAI OAuth endpoints must be https on x.ai / *.x.ai — reject
|
|
131
|
-
// anything else outright so a hostile discovery response can't redirect the
|
|
132
|
-
// token / refresh request. Mirrors openclaw's isTrustedXaiOAuthEndpoint.
|
|
133
|
-
function assertTrustedXaiEndpoint(endpoint, label) {
|
|
134
|
-
let url;
|
|
135
|
-
try {
|
|
136
|
-
url = new URL(String(endpoint));
|
|
137
|
-
} catch {
|
|
138
|
-
throw new Error(`[grok-oauth] invalid ${label}: ${endpoint}`);
|
|
139
|
-
}
|
|
140
|
-
const host = url.hostname.toLowerCase();
|
|
141
|
-
if (url.protocol !== 'https:' || (host !== 'x.ai' && !host.endsWith('.x.ai'))) {
|
|
142
|
-
throw new Error(`[grok-oauth] untrusted ${label}: ${endpoint}`);
|
|
143
|
-
}
|
|
144
|
-
return url.toString();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
let _discoveryCache = null;
|
|
148
|
-
async function fetchDiscovery() {
|
|
149
|
-
if (_discoveryCache) return _discoveryCache;
|
|
150
|
-
const timeout = createTimeoutSignal(null, DISCOVERY_TIMEOUT_MS, 'grok-oauth discovery');
|
|
151
|
-
try {
|
|
152
|
-
const res = await fetch(DISCOVERY_URL, {
|
|
153
|
-
headers: { Accept: 'application/json' },
|
|
154
|
-
// No redirect-following: the discovery doc is a fixed well-known URL.
|
|
155
|
-
// A 3xx could bounce the request to an untrusted host before the
|
|
156
|
-
// endpoint trust checks below ever run.
|
|
157
|
-
redirect: 'error',
|
|
158
|
-
signal: timeout.signal,
|
|
159
|
-
dispatcher: getLlmDispatcher(),
|
|
160
|
-
});
|
|
161
|
-
if (!res.ok) throw new Error(`discovery ${res.status}`);
|
|
162
|
-
const j = await res.json();
|
|
163
|
-
const discovery = {
|
|
164
|
-
authorization_endpoint: assertTrustedXaiEndpoint(j?.authorization_endpoint, 'authorization endpoint'),
|
|
165
|
-
token_endpoint: assertTrustedXaiEndpoint(j?.token_endpoint, 'token endpoint'),
|
|
166
|
-
};
|
|
167
|
-
_discoveryCache = discovery;
|
|
168
|
-
return discovery;
|
|
169
|
-
} finally {
|
|
170
|
-
timeout.cleanup();
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// --- Token store ---
|
|
175
|
-
function getOwnTokenPath() {
|
|
176
|
-
const bound = boundProviderAuthPath('grok-oauth');
|
|
177
|
-
if (bound) return resolve(bound);
|
|
178
|
-
const explicit = process.env.GROK_OAUTH_CREDENTIALS_PATH;
|
|
179
|
-
if (explicit) return resolve(explicit);
|
|
180
|
-
const dir = getPluginData();
|
|
181
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
182
|
-
return join(dir, 'grok-oauth.json');
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function getRefreshLockPath() {
|
|
186
|
-
return `${getOwnTokenPath()}.refresh.lock`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// expires_at may arrive as a unix number or an ISO-8601 string. Normalize both
|
|
190
|
-
// to epoch milliseconds; 0 means unknown.
|
|
191
|
-
function _normalizeExpiresAt(value) {
|
|
192
|
-
if (typeof value === 'string') {
|
|
193
|
-
const ms = Date.parse(value);
|
|
194
|
-
return Number.isFinite(ms) ? ms : 0;
|
|
195
|
-
}
|
|
196
|
-
const n = Number(value || 0);
|
|
197
|
-
if (!Number.isFinite(n) || n <= 0) return 0;
|
|
198
|
-
return n < 1e12 ? n * 1000 : n;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Fallback expiry from the access_token's JWT `exp` claim (epoch ms) when the
|
|
202
|
-
// store carries no explicit expires_at — without it expires_at stays 0, which
|
|
203
|
-
// ensureAuth reads as "never expires", disabling proactive refresh. Returns 0
|
|
204
|
-
// for opaque (non-JWT) tokens. JWT `exp` is epoch SECONDS (RFC 7519).
|
|
205
|
-
function _expiryFromAccessToken(token) {
|
|
206
|
-
try {
|
|
207
|
-
const parts = String(token || '').split('.');
|
|
208
|
-
if (parts.length !== 3) return 0;
|
|
209
|
-
const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString('utf-8'));
|
|
210
|
-
const exp = Number(payload?.exp);
|
|
211
|
-
return Number.isFinite(exp) && exp > 0 ? exp * 1000 : 0;
|
|
212
|
-
} catch { return 0; }
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function _identityFromAccessToken(token) {
|
|
216
|
-
try {
|
|
217
|
-
const parts = String(token || '').split('.');
|
|
218
|
-
if (parts.length !== 3) return {};
|
|
219
|
-
const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString('utf-8'));
|
|
220
|
-
const principalId = payload?.principal_id || payload?.principalId || '';
|
|
221
|
-
const principalType = payload?.principal_type || payload?.principalType || '';
|
|
222
|
-
const userId = payload?.user_id
|
|
223
|
-
|| payload?.userId
|
|
224
|
-
|| principalId
|
|
225
|
-
|| payload?.sub
|
|
226
|
-
|| '';
|
|
227
|
-
return {
|
|
228
|
-
...(userId ? { user_id: String(userId) } : {}),
|
|
229
|
-
...(principalType ? { principal_type: String(principalType) } : {}),
|
|
230
|
-
...(principalId ? { principal_id: String(principalId) } : {}),
|
|
231
|
-
};
|
|
232
|
-
} catch { return {}; }
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function _mtimeMs(path) {
|
|
236
|
-
try { return statSync(path).mtimeMs; } catch { return 0; }
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// mixdog's own login store (grok-oauth.json). Single writer, accurate
|
|
240
|
-
// numeric expires_at from refresh.
|
|
241
|
-
function _loadOwnTokens() {
|
|
242
|
-
const path = getOwnTokenPath();
|
|
243
|
-
if (!existsSync(path)) return null;
|
|
244
|
-
try {
|
|
245
|
-
const raw = JSON.parse(readFileSync(path, 'utf-8'));
|
|
246
|
-
if (!raw?.access_token || !raw?.refresh_token) return null;
|
|
247
|
-
const identity = _identityFromAccessToken(raw.access_token);
|
|
248
|
-
return {
|
|
249
|
-
access_token: raw.access_token,
|
|
250
|
-
refresh_token: raw.refresh_token,
|
|
251
|
-
expires_at: _normalizeExpiresAt(raw.expires_at ?? raw.expiresAt) || _expiryFromAccessToken(raw.access_token),
|
|
252
|
-
token_endpoint: raw.token_endpoint || null,
|
|
253
|
-
user_id: raw.user_id || raw.userId || identity.user_id || '',
|
|
254
|
-
principal_type: raw.principal_type || raw.principalType || identity.principal_type || '',
|
|
255
|
-
principal_id: raw.principal_id || raw.principalId || identity.principal_id || '',
|
|
256
|
-
source: 'own',
|
|
257
|
-
mtimeMs: _mtimeMs(path),
|
|
258
|
-
};
|
|
259
|
-
} catch { return null; }
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Mixdog-owned token store only.
|
|
263
|
-
function loadTokens() {
|
|
264
|
-
return _loadOwnTokens();
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function saveTokens(tokens) {
|
|
268
|
-
const identity = _identityFromAccessToken(tokens.access_token);
|
|
269
|
-
writeJsonAtomicSync(getOwnTokenPath(), {
|
|
270
|
-
access_token: tokens.access_token,
|
|
271
|
-
refresh_token: tokens.refresh_token,
|
|
272
|
-
expires_at: tokens.expires_at || 0,
|
|
273
|
-
token_endpoint: tokens.token_endpoint || null,
|
|
274
|
-
user_id: tokens.user_id || tokens.userId || identity.user_id || undefined,
|
|
275
|
-
principal_type: tokens.principal_type || tokens.principalType || identity.principal_type || undefined,
|
|
276
|
-
principal_id: tokens.principal_id || tokens.principalId || identity.principal_id || undefined,
|
|
277
|
-
}, { lock: true, fsyncDir: true, mode: 0o600, secret: true });
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function _scrubTokens(text) {
|
|
281
|
-
return String(text || '')
|
|
282
|
-
.replace(/Bearer [A-Za-z0-9._\-]+/g, 'Bearer [REDACTED]')
|
|
283
|
-
.replace(/"access_token"\s*:\s*"[^"]+"/g, '"access_token":"[REDACTED]"')
|
|
284
|
-
.replace(/"refresh_token"\s*:\s*"[^"]+"/g, '"refresh_token":"[REDACTED]"')
|
|
285
|
-
.replace(/"key"\s*:\s*"[^"]+"/g, '"key":"[REDACTED]"');
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Public predicate used by config.buildDefaultConfig — enabled when Mixdog's
|
|
289
|
-
// token store carries credentials. Single truth: same loader the runtime uses.
|
|
290
|
-
export function hasGrokOAuthCredentials() {
|
|
291
|
-
try {
|
|
292
|
-
const tokens = loadTokens();
|
|
293
|
-
return !!(tokens?.access_token && tokens?.refresh_token);
|
|
294
|
-
} catch { return false; }
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
export function describeGrokOAuthCredentials() {
|
|
298
|
-
try {
|
|
299
|
-
const tokens = loadTokens();
|
|
300
|
-
if (!tokens?.access_token) {
|
|
301
|
-
return { authenticated: false, status: 'Not Set', detail: 'Mixdog token store' };
|
|
302
|
-
}
|
|
303
|
-
const hasRefresh = Boolean(tokens.refresh_token);
|
|
304
|
-
const expiresAt = _normalizeExpiresAt(tokens.expires_at);
|
|
305
|
-
const expiring = expiresAt > 0 && expiresAt < Date.now() + TOKEN_REFRESH_SKEW_MS;
|
|
306
|
-
const expired = expiresAt > 0 && expiresAt <= Date.now();
|
|
307
|
-
const detail = tokens.source === 'own' ? 'Mixdog token store' : (tokens.source || 'oauth');
|
|
308
|
-
if (!hasRefresh) {
|
|
309
|
-
return {
|
|
310
|
-
authenticated: expiresAt === 0 || !expired,
|
|
311
|
-
status: expired ? 'Reauth Required' : 'Access Only',
|
|
312
|
-
detail: `${detail}; no refresh token`,
|
|
313
|
-
expiresAt,
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
if (expired) return { authenticated: true, status: 'Refresh Required', detail, expiresAt };
|
|
317
|
-
if (expiring) return { authenticated: true, status: 'Refresh Soon', detail, expiresAt };
|
|
318
|
-
return { authenticated: true, status: 'Valid', detail, expiresAt };
|
|
319
|
-
} catch (err) {
|
|
320
|
-
return { authenticated: false, status: 'Error', detail: String(err?.message || err).slice(0, 200) };
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
export function forgetGrokOAuthCredentials() {
|
|
325
|
-
let removed = false;
|
|
326
|
-
const ownPath = getOwnTokenPath();
|
|
327
|
-
if (existsSync(ownPath)) {
|
|
328
|
-
unlinkSync(ownPath);
|
|
329
|
-
removed = true;
|
|
330
|
-
}
|
|
331
|
-
return { removed };
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
let _refreshInFlight = null;
|
|
335
|
-
async function _postRefresh(tokens) {
|
|
336
|
-
const tokenEndpoint = tokens.token_endpoint
|
|
337
|
-
? assertTrustedXaiEndpoint(tokens.token_endpoint, 'token endpoint')
|
|
338
|
-
: (await fetchDiscovery()).token_endpoint;
|
|
339
|
-
const timeout = createTimeoutSignal(null, TOKEN_TIMEOUT_MS, 'grok-oauth refresh');
|
|
340
|
-
try {
|
|
341
|
-
const body = new URLSearchParams({
|
|
342
|
-
grant_type: 'refresh_token',
|
|
343
|
-
client_id: CLIENT_ID,
|
|
344
|
-
refresh_token: tokens.refresh_token,
|
|
345
|
-
});
|
|
346
|
-
if (tokens.principal_type) body.set('principal_type', tokens.principal_type);
|
|
347
|
-
if (tokens.principal_id) body.set('principal_id', tokens.principal_id);
|
|
348
|
-
const res = await fetch(tokenEndpoint, {
|
|
349
|
-
method: 'POST',
|
|
350
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
351
|
-
body,
|
|
352
|
-
// Never follow a redirect on a secret-bearing request: a trusted
|
|
353
|
-
// token endpoint that 307/308-redirects would replay the
|
|
354
|
-
// refresh_token to the redirect target. Fail loud instead.
|
|
355
|
-
redirect: 'error',
|
|
356
|
-
signal: timeout.signal,
|
|
357
|
-
dispatcher: getLlmDispatcher(),
|
|
358
|
-
});
|
|
359
|
-
const text = await res.text();
|
|
360
|
-
let json = null;
|
|
361
|
-
try { json = text ? JSON.parse(text) : null; } catch { /* handled below */ }
|
|
362
|
-
if (!res.ok) {
|
|
363
|
-
const oauthError = String(json?.error || '').toLowerCase();
|
|
364
|
-
const isInvalidGrant = oauthError === 'invalid_grant';
|
|
365
|
-
const isTerminalRefresh = isInvalidGrant || oauthError === 'invalid_client';
|
|
366
|
-
throw Object.assign(
|
|
367
|
-
new Error(`[grok-oauth] token refresh ${res.status}: ${_scrubTokens(text).slice(0, 200)}`),
|
|
368
|
-
{ isInvalidGrant, isTerminalRefresh, oauthError: oauthError || null },
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
const accessToken = json?.access_token;
|
|
372
|
-
if (!accessToken) throw new Error('[grok-oauth] token refresh returned no access token');
|
|
373
|
-
const refreshed = {
|
|
374
|
-
access_token: accessToken,
|
|
375
|
-
// xAI rotates refresh tokens; reuse the prior one only when the
|
|
376
|
-
// response omits it (RFC 6749 permits reuse).
|
|
377
|
-
refresh_token: json?.refresh_token || tokens.refresh_token,
|
|
378
|
-
expires_at: typeof json?.expires_in === 'number'
|
|
379
|
-
? Date.now() + json.expires_in * 1000
|
|
380
|
-
: _normalizeExpiresAt(json?.expires_at),
|
|
381
|
-
token_endpoint: tokenEndpoint,
|
|
382
|
-
user_id: tokens.user_id || tokens.userId || _identityFromAccessToken(accessToken).user_id || '',
|
|
383
|
-
principal_type: json?.principal_type || tokens.principal_type || tokens.principalType || '',
|
|
384
|
-
principal_id: json?.principal_id || tokens.principal_id || tokens.principalId || '',
|
|
385
|
-
};
|
|
386
|
-
saveTokens(refreshed);
|
|
387
|
-
return { ...refreshed, source: 'own', mtimeMs: _mtimeMs(getOwnTokenPath()) };
|
|
388
|
-
} finally {
|
|
389
|
-
timeout.cleanup();
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
async function _postRefreshWithRetry(tokens) {
|
|
394
|
-
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
395
|
-
try {
|
|
396
|
-
return await _postRefresh(tokens);
|
|
397
|
-
} catch (err) {
|
|
398
|
-
if (err?.isTerminalRefresh || attempt === 2) throw err;
|
|
399
|
-
const baseMs = Math.min(2_000, 200 * (2 ** attempt));
|
|
400
|
-
const delayMs = Math.round(baseMs + Math.random() * baseMs);
|
|
401
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
throw new Error('[grok-oauth] unreachable refresh retry state');
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Mixdog processes share one grok-oauth.json and xAI rotates refresh tokens
|
|
408
|
-
// single-use. Hold a cross-process lease across the re-read, exchange, and
|
|
409
|
-
// atomic save so only one process can spend a generation.
|
|
410
|
-
async function refreshTokens(tokens, { force = false } = {}) {
|
|
411
|
-
if (!tokens?.refresh_token) {
|
|
412
|
-
throw new Error('[grok-oauth] refresh token not available — open /providers in mixdog to sign in again');
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
return withFileLock(getRefreshLockPath(), async () => {
|
|
416
|
-
const validAfter = Date.now() + (force ? 0 : TOKEN_REFRESH_SKEW_MS);
|
|
417
|
-
const disk = _loadOwnTokens();
|
|
418
|
-
// A waiter that entered with the prior generation adopts the winner's
|
|
419
|
-
// persisted, valid token instead of rotating it again. xAI may rotate
|
|
420
|
-
// only the refresh token while reissuing the same access token.
|
|
421
|
-
const diskGenerationChanged = disk?.access_token
|
|
422
|
-
&& (disk.access_token !== tokens.access_token
|
|
423
|
-
|| disk.refresh_token !== tokens.refresh_token);
|
|
424
|
-
if (diskGenerationChanged
|
|
425
|
-
&& (!disk.expires_at || disk.expires_at >= validAfter)) {
|
|
426
|
-
return disk;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const current = disk || tokens;
|
|
430
|
-
try {
|
|
431
|
-
return await _postRefreshWithRetry(current);
|
|
432
|
-
} catch (err) {
|
|
433
|
-
if (err?.isInvalidGrant) {
|
|
434
|
-
// A writer that does not participate in this lease may still
|
|
435
|
-
// have won the rotation while the request was in flight.
|
|
436
|
-
// Adopt its valid generation; only exchange it when it cannot
|
|
437
|
-
// satisfy this caller.
|
|
438
|
-
const rotated = _loadOwnTokens();
|
|
439
|
-
if (rotated?.refresh_token && rotated.refresh_token !== current.refresh_token) {
|
|
440
|
-
if (rotated.access_token
|
|
441
|
-
&& (!rotated.expires_at || rotated.expires_at >= validAfter)) {
|
|
442
|
-
return rotated;
|
|
443
|
-
}
|
|
444
|
-
return await _postRefreshWithRetry(rotated);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
throw err;
|
|
448
|
-
}
|
|
449
|
-
}, {
|
|
450
|
-
timeoutMs: 120_000,
|
|
451
|
-
staleMs: 120_000,
|
|
452
|
-
secret: true,
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// --- Model catalog cache (24h disk TTL) ---
|
|
457
38
|
const _modelCache = makeModelCache({
|
|
458
39
|
fileName: 'grok-oauth-models.json',
|
|
459
40
|
ttlMs: MODEL_CACHE_TTL_MS,
|
|
@@ -588,7 +169,7 @@ function _markLatestGrok(models) {
|
|
|
588
169
|
// on-disk catalog cache. Deterministic — no version-string guessing (xAI's
|
|
589
170
|
// grok-4.20 actually predates grok-4.3 despite the higher-looking number).
|
|
590
171
|
// Returns null until the catalog is cached; use ensureLatestGrokModel when null.
|
|
591
|
-
|
|
172
|
+
function resolveLatestGrokModel() {
|
|
592
173
|
const cached = _modelCache.loadSync();
|
|
593
174
|
if (!Array.isArray(cached)) return null;
|
|
594
175
|
let best = null;
|
|
@@ -599,7 +180,7 @@ export function resolveLatestGrokModel() {
|
|
|
599
180
|
return best?.id || null;
|
|
600
181
|
}
|
|
601
182
|
|
|
602
|
-
|
|
183
|
+
async function ensureLatestGrokModel(provider) {
|
|
603
184
|
let m = resolveLatestGrokModel();
|
|
604
185
|
if (m) return m;
|
|
605
186
|
await provider._refreshModelCache();
|
|
@@ -650,23 +231,23 @@ export class GrokOAuthProvider {
|
|
|
650
231
|
if (forceRefresh || expiring) {
|
|
651
232
|
const currentToken = this.tokens?.access_token || null;
|
|
652
233
|
try {
|
|
653
|
-
if (
|
|
654
|
-
const shared = await
|
|
234
|
+
if (_getRefreshInFlight()) {
|
|
235
|
+
const shared = await _getRefreshInFlight();
|
|
655
236
|
this.tokens = shared;
|
|
656
237
|
// A forced caller must not accept a shared non-force result
|
|
657
238
|
// that merely handed back its own prior token: start a fresh
|
|
658
239
|
// forced refresh instead (mirror openai-oauth).
|
|
659
240
|
if (forceRefresh && shared?.access_token === currentToken) {
|
|
660
|
-
if (!
|
|
661
|
-
|
|
662
|
-
.finally(() => {
|
|
241
|
+
if (!_getRefreshInFlight()) {
|
|
242
|
+
_setRefreshInFlight(refreshTokens(this.tokens, { force: true })
|
|
243
|
+
.finally(() => { _setRefreshInFlight(null); }));
|
|
663
244
|
}
|
|
664
|
-
this.tokens = await
|
|
245
|
+
this.tokens = await _getRefreshInFlight();
|
|
665
246
|
}
|
|
666
247
|
} else {
|
|
667
|
-
|
|
668
|
-
.finally(() => {
|
|
669
|
-
this.tokens = await
|
|
248
|
+
const inFlight = _setRefreshInFlight(refreshTokens(this.tokens, { force: forceRefresh })
|
|
249
|
+
.finally(() => { _setRefreshInFlight(null); }));
|
|
250
|
+
this.tokens = await inFlight;
|
|
670
251
|
}
|
|
671
252
|
this._refreshFallbackUntil = 0;
|
|
672
253
|
} catch (err) {
|
|
@@ -863,163 +444,3 @@ export class GrokOAuthProvider {
|
|
|
863
444
|
}
|
|
864
445
|
|
|
865
446
|
// --- Login flow (PKCE, export for CLI / setup use) ---
|
|
866
|
-
function generatePKCE() {
|
|
867
|
-
const verifier = randomBytes(32).toString('base64url');
|
|
868
|
-
const challenge = createHash('sha256').update(verifier).digest('base64url');
|
|
869
|
-
return { verifier, challenge };
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
async function exchangeAuthorizationCode({ discovery, pkce, code }) {
|
|
873
|
-
const cleanCode = String(code || '').trim();
|
|
874
|
-
if (!cleanCode) throw new Error('[grok-oauth] authorization code is required');
|
|
875
|
-
const tokenRes = await fetch(discovery.token_endpoint, {
|
|
876
|
-
method: 'POST',
|
|
877
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
878
|
-
body: new URLSearchParams({
|
|
879
|
-
grant_type: 'authorization_code',
|
|
880
|
-
client_id: CLIENT_ID,
|
|
881
|
-
code: cleanCode,
|
|
882
|
-
code_verifier: pkce.verifier,
|
|
883
|
-
redirect_uri: REDIRECT_URI,
|
|
884
|
-
// xAI re-validates the PKCE challenge at token exchange
|
|
885
|
-
// (not just the verifier), so echo it back. Omitting
|
|
886
|
-
// these makes the exchange fail. Matches the Grok CLI.
|
|
887
|
-
code_challenge: pkce.challenge,
|
|
888
|
-
code_challenge_method: 'S256',
|
|
889
|
-
}),
|
|
890
|
-
// Secret-bearing (authorization code + verifier): refuse
|
|
891
|
-
// redirects so they can't be replayed to an untrusted host.
|
|
892
|
-
redirect: 'error',
|
|
893
|
-
signal: AbortSignal.timeout(TOKEN_TIMEOUT_MS),
|
|
894
|
-
});
|
|
895
|
-
if (!tokenRes.ok) {
|
|
896
|
-
const text = await tokenRes.text().catch(() => '');
|
|
897
|
-
throw new Error(`[grok-oauth] token exchange ${tokenRes.status}: ${_scrubTokens(text).slice(0, 500)}`);
|
|
898
|
-
}
|
|
899
|
-
const json = await tokenRes.json();
|
|
900
|
-
if (!json.access_token || !json.refresh_token) {
|
|
901
|
-
throw new Error('[grok-oauth] token exchange response missing access_token or refresh_token');
|
|
902
|
-
}
|
|
903
|
-
const identity = _identityFromAccessToken(json.access_token);
|
|
904
|
-
const tokens = {
|
|
905
|
-
access_token: json.access_token,
|
|
906
|
-
refresh_token: json.refresh_token,
|
|
907
|
-
expires_at: typeof json.expires_in === 'number'
|
|
908
|
-
? Date.now() + json.expires_in * 1000
|
|
909
|
-
: _normalizeExpiresAt(json.expires_at),
|
|
910
|
-
token_endpoint: discovery.token_endpoint,
|
|
911
|
-
user_id: identity.user_id || '',
|
|
912
|
-
principal_type: json.principal_type || identity.principal_type || '',
|
|
913
|
-
principal_id: json.principal_id || identity.principal_id || '',
|
|
914
|
-
};
|
|
915
|
-
saveTokens(tokens);
|
|
916
|
-
return tokens;
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
function parseOAuthCodeInput(input) {
|
|
920
|
-
const value = String(input || '').trim();
|
|
921
|
-
if (!value) return { code: '', state: '' };
|
|
922
|
-
try {
|
|
923
|
-
const url = new URL(value);
|
|
924
|
-
const code = url.searchParams.get('code') || '';
|
|
925
|
-
const state = url.searchParams.get('state') || '';
|
|
926
|
-
if (code || state) return { code, state };
|
|
927
|
-
} catch { /* not a URL */ }
|
|
928
|
-
if (value.includes('#')) {
|
|
929
|
-
const [code, state] = value.split('#', 2);
|
|
930
|
-
return { code: String(code || '').trim(), state: String(state || '').trim() };
|
|
931
|
-
}
|
|
932
|
-
if (value.includes('code=')) {
|
|
933
|
-
const params = new URLSearchParams(value.startsWith('?') ? value.slice(1) : value);
|
|
934
|
-
return { code: params.get('code') || '', state: params.get('state') || '' };
|
|
935
|
-
}
|
|
936
|
-
return { code: value, state: '' };
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
export async function beginOAuthLogin() {
|
|
940
|
-
const discovery = await fetchDiscovery();
|
|
941
|
-
const pkce = generatePKCE();
|
|
942
|
-
const state = randomBytes(16).toString('hex');
|
|
943
|
-
const nonce = randomBytes(16).toString('hex');
|
|
944
|
-
const url = new URL(discovery.authorization_endpoint);
|
|
945
|
-
url.searchParams.set('response_type', 'code');
|
|
946
|
-
url.searchParams.set('client_id', CLIENT_ID);
|
|
947
|
-
url.searchParams.set('redirect_uri', REDIRECT_URI);
|
|
948
|
-
url.searchParams.set('scope', SCOPE);
|
|
949
|
-
url.searchParams.set('state', state);
|
|
950
|
-
url.searchParams.set('nonce', nonce);
|
|
951
|
-
url.searchParams.set('code_challenge', pkce.challenge);
|
|
952
|
-
url.searchParams.set('code_challenge_method', 'S256');
|
|
953
|
-
url.searchParams.set('plan', 'generic');
|
|
954
|
-
url.searchParams.set('referrer', 'mixdog');
|
|
955
|
-
|
|
956
|
-
let server = null;
|
|
957
|
-
let timeout = null;
|
|
958
|
-
let finish = null;
|
|
959
|
-
const waitForCallback = new Promise((resolve, reject) => {
|
|
960
|
-
let settled = false;
|
|
961
|
-
finish = (value, error = null) => {
|
|
962
|
-
if (settled) return;
|
|
963
|
-
settled = true;
|
|
964
|
-
if (timeout) clearTimeout(timeout);
|
|
965
|
-
try { server?.close(); } catch { /* already closed */ }
|
|
966
|
-
if (error) reject(error);
|
|
967
|
-
else resolve(value);
|
|
968
|
-
};
|
|
969
|
-
server = createServer(async (req, res) => {
|
|
970
|
-
const u = new URL(req.url || '/', `http://${CALLBACK_HOST}:${CALLBACK_PORT}`);
|
|
971
|
-
if (u.pathname !== CALLBACK_PATH) {
|
|
972
|
-
res.writeHead(404);
|
|
973
|
-
res.end();
|
|
974
|
-
return;
|
|
975
|
-
}
|
|
976
|
-
const code = u.searchParams.get('code');
|
|
977
|
-
if (!code || u.searchParams.get('state') !== state) {
|
|
978
|
-
res.writeHead(400);
|
|
979
|
-
res.end('Invalid');
|
|
980
|
-
finish(null);
|
|
981
|
-
return;
|
|
982
|
-
}
|
|
983
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
984
|
-
res.end('<html><body><h2>Grok login successful! You can close this tab.</h2></body></html>');
|
|
985
|
-
try {
|
|
986
|
-
const tokens = await exchangeAuthorizationCode({ discovery, pkce, code });
|
|
987
|
-
finish(tokens);
|
|
988
|
-
} catch (err) {
|
|
989
|
-
finish(null, err instanceof Error ? err : new Error(String(err)));
|
|
990
|
-
}
|
|
991
|
-
});
|
|
992
|
-
timeout = setTimeout(() => finish(null), LOGIN_TIMEOUT_MS);
|
|
993
|
-
server.listen(CALLBACK_PORT, CALLBACK_HOST, async () => {
|
|
994
|
-
process.stderr.write(`\n[grok-oauth] Open this URL to log in (consent shows as "Grok Build"):\n${url.toString()}\n\n`);
|
|
995
|
-
try {
|
|
996
|
-
const { openInBrowser } = await import('../../../shared/open-url.mjs');
|
|
997
|
-
openInBrowser(url.toString());
|
|
998
|
-
} catch (err) {
|
|
999
|
-
process.stderr.write(`[grok-oauth] browser open failed: ${String(err?.message || err).slice(0, 200)}\n`);
|
|
1000
|
-
}
|
|
1001
|
-
});
|
|
1002
|
-
server.on('error', (err) => finish(null, new Error(`[grok-oauth] callback server failed on ${CALLBACK_HOST}:${CALLBACK_PORT}: ${err?.message || err}`)));
|
|
1003
|
-
});
|
|
1004
|
-
|
|
1005
|
-
return {
|
|
1006
|
-
provider: 'grok-oauth',
|
|
1007
|
-
url: url.toString(),
|
|
1008
|
-
waitForCallback,
|
|
1009
|
-
completeCode: async (input) => {
|
|
1010
|
-
const parsed = parseOAuthCodeInput(input);
|
|
1011
|
-
if (parsed.state && parsed.state !== state) throw new Error('[grok-oauth] OAuth state mismatch');
|
|
1012
|
-
const tokens = await exchangeAuthorizationCode({ discovery, pkce, code: parsed.code });
|
|
1013
|
-
finish?.(tokens);
|
|
1014
|
-
return tokens;
|
|
1015
|
-
},
|
|
1016
|
-
cancel: () => {
|
|
1017
|
-
finish?.(null);
|
|
1018
|
-
},
|
|
1019
|
-
};
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
export async function loginOAuth() {
|
|
1023
|
-
const login = await beginOAuthLogin();
|
|
1024
|
-
return await login.waitForCallback;
|
|
1025
|
-
}
|
|
@@ -3,7 +3,7 @@ import { providerNativeToolPrefixCount } from '../../../../../session-runtime/pr
|
|
|
3
3
|
export const ANTHROPIC_CACHE_TTL_STABLE = { type: 'ephemeral', ttl: '1h' };
|
|
4
4
|
export const ANTHROPIC_CACHE_TTL_VOLATILE = { type: 'ephemeral' };
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
function appendAnthropicCacheControl(content, ttl = ANTHROPIC_CACHE_TTL_VOLATILE) {
|
|
7
7
|
const withCacheControl = (block) => {
|
|
8
8
|
if (!block || typeof block !== 'object' || block.cache_control) return block;
|
|
9
9
|
return { ...block, cache_control: ttl };
|
|
@@ -279,7 +279,7 @@ export function collectCompatResponseSearchSources(response) {
|
|
|
279
279
|
return { citations, webSearchCalls };
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
|
|
282
|
+
function toResponsesInputMessage(m, pendingToolMedia = null, customToolCallNameById = null) {
|
|
283
283
|
if (m.role === 'tool') {
|
|
284
284
|
const { output, mediaContent } = splitToolContentForOpenAIResponses(m.content);
|
|
285
285
|
// xai path: never emit `custom_tool_call_output` (the `custom` variant
|