@bastani/atomic 0.9.3-alpha.3 → 0.9.3-alpha.5
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/CHANGELOG.md +22 -0
- package/dist/builtin/cursor/CHANGELOG.md +19 -0
- package/dist/builtin/cursor/README.md +2 -1
- package/dist/builtin/cursor/package.json +4 -3
- package/dist/builtin/cursor/src/auth.ts +1 -1
- package/dist/builtin/cursor/src/config.ts +1 -1
- package/dist/builtin/cursor/src/cursor-models-raw.json +82 -246
- package/dist/builtin/cursor/src/model-mapper.ts +97 -12
- package/dist/builtin/cursor/src/model-reference.ts +282 -0
- package/dist/builtin/cursor/src/proto/README.md +2 -2
- package/dist/builtin/cursor/src/proto/protobuf-codec-request.ts +1 -1
- package/dist/builtin/cursor/src/provider.ts +1 -1
- package/dist/builtin/cursor/src/stream.ts +1 -1
- package/dist/builtin/cursor/src/transport-types.ts +1 -1
- package/dist/builtin/intercom/CHANGELOG.md +12 -0
- package/dist/builtin/intercom/package.json +2 -2
- package/dist/builtin/mcp/CHANGELOG.md +12 -0
- package/dist/builtin/mcp/package.json +3 -3
- package/dist/builtin/mcp/sampling-handler.ts +1 -1
- package/dist/builtin/mcp/types.ts +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +12 -0
- package/dist/builtin/subagents/package.json +4 -4
- package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +1 -1
- package/dist/builtin/subagents/src/runs/background/subagent-runner-types.ts +1 -1
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/structured-output.ts +1 -1
- package/dist/builtin/subagents/src/shared/types-results.ts +1 -1
- package/dist/builtin/subagents/src/shared/utils.ts +1 -1
- package/dist/builtin/subagents/src/slash/slash-live-state.ts +1 -1
- package/dist/builtin/web-access/CHANGELOG.md +12 -0
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/web-access/summary-review.ts +1 -1
- package/dist/builtin/web-access/web-search-summary.ts +1 -1
- package/dist/builtin/web-access/web-search-tool.ts +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +12 -0
- package/dist/builtin/workflows/README.md +2 -0
- package/dist/builtin/workflows/package.json +2 -2
- package/dist/builtin/workflows/src/extension/workflow-prompts.ts +6 -3
- package/dist/bun/register-bedrock.d.ts.map +1 -1
- package/dist/bun/register-bedrock.js +1 -1
- package/dist/bun/register-bedrock.js.map +1 -1
- package/dist/cli/file-processor.d.ts +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/initial-message.d.ts +1 -1
- package/dist/cli/initial-message.d.ts.map +1 -1
- package/dist/cli/initial-message.js.map +1 -1
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js.map +1 -1
- package/dist/core/agent-session-auto-compaction.d.ts +1 -1
- package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-auto-compaction.js +1 -1
- package/dist/core/agent-session-auto-compaction.js.map +1 -1
- package/dist/core/agent-session-events.d.ts +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +1 -1
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-export.d.ts.map +1 -1
- package/dist/core/agent-session-export.js.map +1 -1
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +1 -1
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-message-queue.d.ts +1 -1
- package/dist/core/agent-session-message-queue.d.ts.map +1 -1
- package/dist/core/agent-session-message-queue.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +1 -1
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-models.d.ts +1 -1
- package/dist/core/agent-session-models.d.ts.map +1 -1
- package/dist/core/agent-session-models.js +1 -1
- package/dist/core/agent-session-models.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-retry.d.ts +1 -1
- package/dist/core/agent-session-retry.d.ts.map +1 -1
- package/dist/core/agent-session-retry.js +1 -1
- package/dist/core/agent-session-retry.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session-state.d.ts +1 -1
- package/dist/core/agent-session-state.d.ts.map +1 -1
- package/dist/core/agent-session-state.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +1 -1
- package/dist/core/agent-session-types.d.ts.map +1 -1
- package/dist/core/agent-session-types.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/anthropic-thinking-guard.d.ts +1 -1
- package/dist/core/anthropic-thinking-guard.d.ts.map +1 -1
- package/dist/core/anthropic-thinking-guard.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +15 -11
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/auth-storage.d.ts +2 -2
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +1 -1
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/codex-fast-mode.d.ts +1 -1
- package/dist/core/codex-fast-mode.d.ts.map +1 -1
- package/dist/core/codex-fast-mode.js +1 -1
- package/dist/core/codex-fast-mode.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/context-compaction-runner.d.ts +1 -1
- package/dist/core/compaction/context-compaction-runner.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-runner.js +1 -1
- package/dist/core/compaction/context-compaction-runner.js.map +1 -1
- package/dist/core/compaction/context-deletion-tool-definitions.d.ts.map +1 -1
- package/dist/core/compaction/context-deletion-tool-definitions.js +1 -1
- package/dist/core/compaction/context-deletion-tool-definitions.js.map +1 -1
- package/dist/core/compaction/context-transcript-analysis.d.ts.map +1 -1
- package/dist/core/compaction/context-transcript-analysis.js.map +1 -1
- package/dist/core/compaction/utils.d.ts +1 -1
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/context-window.d.ts +2 -2
- package/dist/core/context-window.d.ts.map +1 -1
- package/dist/core/context-window.js.map +1 -1
- package/dist/core/copilot-gemini-payload-sanitizer.d.ts +1 -1
- package/dist/core/copilot-gemini-payload-sanitizer.d.ts.map +1 -1
- package/dist/core/copilot-gemini-payload-sanitizer.js.map +1 -1
- package/dist/core/copilot-gemini-reasoning.d.ts +1 -1
- package/dist/core/copilot-gemini-reasoning.d.ts.map +1 -1
- package/dist/core/copilot-gemini-reasoning.js.map +1 -1
- package/dist/core/copilot-gemini-tool-arguments.d.ts +1 -1
- package/dist/core/copilot-gemini-tool-arguments.d.ts.map +1 -1
- package/dist/core/copilot-gemini-tool-arguments.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/agent-events.d.ts +1 -1
- package/dist/core/extensions/agent-events.d.ts.map +1 -1
- package/dist/core/extensions/agent-events.js.map +1 -1
- package/dist/core/extensions/api-types.d.ts +1 -1
- package/dist/core/extensions/api-types.d.ts.map +1 -1
- package/dist/core/extensions/api-types.js.map +1 -1
- package/dist/core/extensions/context-types.d.ts +1 -1
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/event-results.d.ts +1 -1
- package/dist/core/extensions/event-results.d.ts.map +1 -1
- package/dist/core/extensions/event-results.js.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.d.ts +12 -0
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +18 -2
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/dist/core/extensions/provider-types.d.ts +1 -1
- package/dist/core/extensions/provider-types.d.ts.map +1 -1
- package/dist/core/extensions/provider-types.js.map +1 -1
- package/dist/core/extensions/runner-context.d.ts +1 -1
- package/dist/core/extensions/runner-context.d.ts.map +1 -1
- package/dist/core/extensions/runner-context.js.map +1 -1
- package/dist/core/extensions/runner-events.d.ts +1 -1
- package/dist/core/extensions/runner-events.d.ts.map +1 -1
- package/dist/core/extensions/runner-events.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/runtime-types.d.ts +1 -1
- package/dist/core/extensions/runtime-types.d.ts.map +1 -1
- package/dist/core/extensions/runtime-types.js.map +1 -1
- package/dist/core/extensions/tool-events.d.ts +1 -1
- package/dist/core/extensions/tool-events.d.ts.map +1 -1
- package/dist/core/extensions/tool-events.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +5 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +46 -12
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts +3 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +4 -1
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/keybindings.d.ts +1 -1
- package/dist/core/messages.d.ts +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry-auth.d.ts +1 -1
- package/dist/core/model-registry-auth.d.ts.map +1 -1
- package/dist/core/model-registry-auth.js.map +1 -1
- package/dist/core/model-registry-builtins.d.ts +1 -1
- package/dist/core/model-registry-builtins.d.ts.map +1 -1
- package/dist/core/model-registry-builtins.js +1 -1
- package/dist/core/model-registry-builtins.js.map +1 -1
- package/dist/core/model-registry-custom-loader.d.ts.map +1 -1
- package/dist/core/model-registry-custom-loader.js +1 -1
- package/dist/core/model-registry-custom-loader.js.map +1 -1
- package/dist/core/model-registry-dynamic.d.ts +1 -1
- package/dist/core/model-registry-dynamic.d.ts.map +1 -1
- package/dist/core/model-registry-dynamic.js +1 -1
- package/dist/core/model-registry-dynamic.js.map +1 -1
- package/dist/core/model-registry-loader.d.ts.map +1 -1
- package/dist/core/model-registry-loader.js.map +1 -1
- package/dist/core/model-registry-types.d.ts +1 -1
- package/dist/core/model-registry-types.d.ts.map +1 -1
- package/dist/core/model-registry-types.js.map +1 -1
- package/dist/core/model-registry.d.ts +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +1 -1
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver-cli.d.ts.map +1 -1
- package/dist/core/model-resolver-cli.js.map +1 -1
- package/dist/core/model-resolver-defaults.d.ts +1 -1
- package/dist/core/model-resolver-defaults.d.ts.map +1 -1
- package/dist/core/model-resolver-defaults.js.map +1 -1
- package/dist/core/model-resolver-initial.d.ts +1 -1
- package/dist/core/model-resolver-initial.d.ts.map +1 -1
- package/dist/core/model-resolver-initial.js.map +1 -1
- package/dist/core/model-resolver-patterns.d.ts +1 -1
- package/dist/core/model-resolver-patterns.d.ts.map +1 -1
- package/dist/core/model-resolver-patterns.js.map +1 -1
- package/dist/core/model-resolver-scope.d.ts.map +1 -1
- package/dist/core/model-resolver-scope.js +1 -1
- package/dist/core/model-resolver-scope.js.map +1 -1
- package/dist/core/model-resolver-types.d.ts +1 -1
- package/dist/core/model-resolver-types.d.ts.map +1 -1
- package/dist/core/model-resolver-types.js.map +1 -1
- package/dist/core/provider-attribution.d.ts +2 -2
- package/dist/core/provider-attribution.d.ts.map +1 -1
- package/dist/core/provider-attribution.js +4 -0
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/sdk-types.d.ts +1 -1
- package/dist/core/sdk-types.d.ts.map +1 -1
- package/dist/core/sdk-types.js.map +1 -1
- package/dist/core/sdk.d.ts +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +2 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager-core.d.ts +1 -1
- package/dist/core/session-manager-core.d.ts.map +1 -1
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/core/session-manager-entries.d.ts +1 -1
- package/dist/core/session-manager-entries.d.ts.map +1 -1
- package/dist/core/session-manager-entries.js.map +1 -1
- package/dist/core/session-manager-list.d.ts.map +1 -1
- package/dist/core/session-manager-list.js.map +1 -1
- package/dist/core/session-manager-types.d.ts +1 -1
- package/dist/core/session-manager-types.d.ts.map +1 -1
- package/dist/core/session-manager-types.js.map +1 -1
- package/dist/core/settings-types.d.ts +1 -1
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/tools/oversized-tool-result.d.ts +1 -1
- package/dist/core/tools/oversized-tool-result.d.ts.map +1 -1
- package/dist/core/tools/oversized-tool-result.js.map +1 -1
- package/dist/core/tools/read-url.d.ts +1 -1
- package/dist/core/tools/read-url.d.ts.map +1 -1
- package/dist/core/tools/read-url.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +1 -1
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/todos-types.d.ts.map +1 -1
- package/dist/core/tools/todos-types.js +1 -1
- package/dist/core/tools/todos-types.js.map +1 -1
- package/dist/main-app-mode.d.ts +1 -1
- package/dist/main-app-mode.d.ts.map +1 -1
- package/dist/main-app-mode.js.map +1 -1
- package/dist/main-session-options.d.ts.map +1 -1
- package/dist/main-session-options.js +1 -1
- package/dist/main-session-options.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +1 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-handlers.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector-handlers.js +2 -2
- package/dist/modes/interactive/components/settings-selector-handlers.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-types.d.ts +1 -1
- package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector-types.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
- package/dist/modes/print-mode.d.ts +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +1 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/changelog.mdx +15 -0
- package/docs/custom-provider.md +2 -1
- package/docs/models.md +2 -0
- package/docs/providers.md +3 -1
- package/docs/quickstart.md +6 -4
- package/docs/sdk.md +2 -2
- package/docs/settings.md +4 -4
- package/docs/workflows.md +11 -6
- package/examples/extensions/custom-footer.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/index.ts +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +1 -1
- package/examples/extensions/handoff.ts +1 -1
- package/examples/extensions/hello.ts +1 -1
- package/examples/extensions/plan-mode/index.ts +1 -1
- package/examples/extensions/preset.ts +1 -1
- package/examples/extensions/qna.ts +1 -1
- package/examples/extensions/subagent/display.ts +1 -1
- package/examples/extensions/subagent/runner.ts +1 -1
- package/examples/extensions/subagent/schemas.ts +1 -1
- package/examples/extensions/subagent/types.ts +1 -1
- package/examples/extensions/summarize.ts +1 -1
- package/examples/extensions/tic-tac-toe.ts +1 -1
- package/examples/extensions/todo.ts +1 -1
- package/examples/extensions/tool-override.ts +1 -1
- package/examples/sdk/02-custom-model.ts +1 -1
- package/examples/sdk/12-full-control.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/package.json +5 -5
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { ThinkingLevel, ThinkingLevelMap } from "@earendil-works/pi-ai";
|
|
1
|
+
import type { ThinkingLevel, ThinkingLevelMap } from "@earendil-works/pi-ai/compat";
|
|
2
2
|
import { CURSOR_API, CURSOR_API_BASE_URL } from "./config.js";
|
|
3
3
|
import rawFallbackModels from "./cursor-models-raw.json" with { type: "json" };
|
|
4
|
+
import { positiveIntOrUndefined, resolveCursorModelReferenceLimits, type CursorModelReferenceCandidate } from "./model-reference.js";
|
|
4
5
|
|
|
5
6
|
export type CursorCatalogSource = "live" | "estimated";
|
|
6
7
|
export type CursorEffort = "none" | "low" | "medium" | "high" | "xhigh" | "max" | "default";
|
|
@@ -60,12 +61,13 @@ interface CursorVariantGroup {
|
|
|
60
61
|
const CURSOR_FALLBACK_RAW_MODELS = rawFallbackModels satisfies readonly CursorUsableModel[];
|
|
61
62
|
const PARSEABLE_EFFORTS: readonly Exclude<CursorEffort, "default">[] = ["none", "low", "medium", "high", "xhigh", "max"];
|
|
62
63
|
const EFFORT_ORDER: readonly CursorEffort[] = ["none", "low", "default", "medium", "high", "xhigh", "max"];
|
|
64
|
+
const THINKING_LEVELS: readonly ThinkingLevel[] = ["minimal", "low", "medium", "high", "xhigh"];
|
|
63
65
|
const THINKING_LEVEL_EFFORT_PREFERENCES: Record<ThinkingLevel, readonly CursorEffort[]> = {
|
|
64
66
|
minimal: ["none", "low", "default"],
|
|
65
67
|
low: ["low", "none", "default"],
|
|
66
68
|
medium: ["medium", "default", "low"],
|
|
67
69
|
high: ["high", "medium", "default"],
|
|
68
|
-
xhigh: ["max", "xhigh"
|
|
70
|
+
xhigh: ["max", "xhigh"],
|
|
69
71
|
};
|
|
70
72
|
|
|
71
73
|
const ESTIMATED_CONTEXT_WINDOW = 200_000;
|
|
@@ -85,38 +87,99 @@ export function createEstimatedCursorCatalog(now = Date.now()): CursorModelCatal
|
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
export function mapCursorCatalogToProviderModels(catalog: CursorModelCatalog): CursorProviderModelDefinition[] {
|
|
88
|
-
|
|
90
|
+
const groups = groupCursorModels(catalog.models);
|
|
91
|
+
const familyReferenceVariants = cursorReferenceVariantsByBaseId(groups);
|
|
92
|
+
return groups.map((group) => {
|
|
89
93
|
const effortVariants = collectEffortVariants(group.variants, group.primaryId);
|
|
90
94
|
const supportsEffort = group.variants.some((variant) => Boolean(variant.effort)) || effortVariants.size >= 2;
|
|
91
95
|
const supportsReasoning = supportsReasoningModelId(group.primaryId);
|
|
92
96
|
const name = catalog.source === "estimated" ? `${group.displayName} (estimated)` : group.displayName;
|
|
97
|
+
// Cursor's private API omits token limits, so when neither a live nor a
|
|
98
|
+
// static explicit limit is present, derive the window/output from the
|
|
99
|
+
// bundled pi-ai model catalog before falling back to a conservative
|
|
100
|
+
// estimate. This never changes which models are registered. One-million
|
|
101
|
+
// labels are tracked across fast/thinking sibling groups for the same
|
|
102
|
+
// family so Cursor's mode suffixes do not hide the advertised long window.
|
|
103
|
+
const referenceLimits = resolveCursorModelReferenceLimits(cursorModelReferenceCandidates(group, familyReferenceVariants.get(group.baseId) ?? []));
|
|
93
104
|
return {
|
|
94
105
|
id: group.primaryId,
|
|
95
106
|
name,
|
|
96
107
|
api: CURSOR_API,
|
|
97
108
|
baseUrl: CURSOR_API_BASE_URL,
|
|
98
109
|
reasoning: supportsReasoning,
|
|
99
|
-
thinkingLevelMap: supportsEffort ?
|
|
110
|
+
thinkingLevelMap: supportsEffort ? buildCursorThinkingLevelMap(group, effortVariants) : undefined,
|
|
100
111
|
input: cursorModelInput(group.primaryId),
|
|
101
112
|
cost: subscriptionCost(),
|
|
102
|
-
contextWindow: chooseLargestNumber(group.variants.map((variant) => variant.contextWindow)) ?? ESTIMATED_CONTEXT_WINDOW,
|
|
103
|
-
maxTokens: chooseLargestNumber(group.variants.map((variant) => variant.maxTokens)) ?? ESTIMATED_MAX_TOKENS,
|
|
113
|
+
contextWindow: positiveIntLimit(chooseLargestNumber(group.variants.map((variant) => variant.contextWindow)) ?? referenceLimits.contextWindow, ESTIMATED_CONTEXT_WINDOW),
|
|
114
|
+
maxTokens: positiveIntLimit(chooseLargestNumber(group.variants.map((variant) => variant.maxTokens)) ?? referenceLimits.maxTokens, ESTIMATED_MAX_TOKENS),
|
|
104
115
|
};
|
|
105
116
|
});
|
|
106
117
|
}
|
|
107
118
|
|
|
119
|
+
function cursorModelReferenceCandidates(group: CursorVariantGroup, familyVariants: readonly CursorVariant[]): CursorModelReferenceCandidate[] {
|
|
120
|
+
return [
|
|
121
|
+
{ id: group.primaryId, displayName: group.displayName },
|
|
122
|
+
...group.variants.map((variant) => ({ id: variant.id, displayName: variant.displayName })),
|
|
123
|
+
...familyVariants.map((variant) => ({ id: variant.id, displayName: variant.displayName })),
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function cursorReferenceVariantsByBaseId(groups: readonly CursorVariantGroup[]): ReadonlyMap<string, readonly CursorVariant[]> {
|
|
128
|
+
const variantsByBaseId = new Map<string, CursorVariant[]>();
|
|
129
|
+
for (const group of groups) {
|
|
130
|
+
const variants = variantsByBaseId.get(group.baseId) ?? [];
|
|
131
|
+
variants.push(...group.variants);
|
|
132
|
+
variantsByBaseId.set(group.baseId, variants);
|
|
133
|
+
}
|
|
134
|
+
return variantsByBaseId;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function positiveIntLimit(value: number | undefined, fallback: number): number {
|
|
138
|
+
// Provider registration rejects non-positive/non-integer windows and would
|
|
139
|
+
// drop the whole catalog; guarantee a valid positive integer here so limit
|
|
140
|
+
// values can never affect which Cursor models are listed.
|
|
141
|
+
return positiveIntOrUndefined(value) ?? fallback;
|
|
142
|
+
}
|
|
143
|
+
|
|
108
144
|
export function resolveCursorModelVariant(
|
|
109
145
|
baseModelId: string,
|
|
110
146
|
thinkingLevelMap: ThinkingLevelMap | undefined,
|
|
111
147
|
thinkingLevel: ThinkingLevel | undefined,
|
|
112
148
|
): string {
|
|
113
|
-
if (!
|
|
114
|
-
|
|
115
|
-
|
|
149
|
+
if (!thinkingLevelMap) return baseModelId;
|
|
150
|
+
// With no explicit thinking level, fall back to the `off` default variant.
|
|
151
|
+
// Effort-only Cursor models have no real base id (Cursor lists only
|
|
152
|
+
// `gpt-5.5-medium`, never a bare `gpt-5.5`), so sending the synthesized base
|
|
153
|
+
// id makes Cursor reject the run with `not_found`; the `off` entry carries a
|
|
154
|
+
// concrete variant id to send instead.
|
|
155
|
+
const mapped = thinkingLevel ? thinkingLevelMap[thinkingLevel] : thinkingLevelMap.off;
|
|
156
|
+
if (mapped === null) {
|
|
157
|
+
const fallbackLevel = nearestSupportedThinkingLevel(thinkingLevelMap, thinkingLevel);
|
|
158
|
+
return fallbackLevel ? resolveCursorModelVariant(baseModelId, thinkingLevelMap, fallbackLevel) : baseModelId;
|
|
159
|
+
}
|
|
160
|
+
if (mapped === undefined || mapped === "default") return baseModelId;
|
|
116
161
|
if (isCursorEffort(mapped)) return replaceEffortBeforeCursorSuffix(baseModelId, mapped);
|
|
117
162
|
return mapped;
|
|
118
163
|
}
|
|
119
164
|
|
|
165
|
+
function nearestSupportedThinkingLevel(
|
|
166
|
+
thinkingLevelMap: ThinkingLevelMap,
|
|
167
|
+
thinkingLevel: ThinkingLevel | undefined,
|
|
168
|
+
): ThinkingLevel | undefined {
|
|
169
|
+
if (!thinkingLevel) return undefined;
|
|
170
|
+
const requestedIndex = THINKING_LEVELS.indexOf(thinkingLevel);
|
|
171
|
+
if (requestedIndex === -1) return undefined;
|
|
172
|
+
for (let index = requestedIndex - 1; index >= 0; index--) {
|
|
173
|
+
const level = THINKING_LEVELS[index];
|
|
174
|
+
if (level && thinkingLevelMap[level] !== null && thinkingLevelMap[level] !== undefined) return level;
|
|
175
|
+
}
|
|
176
|
+
for (let index = requestedIndex + 1; index < THINKING_LEVELS.length; index++) {
|
|
177
|
+
const level = THINKING_LEVELS[index];
|
|
178
|
+
if (level && thinkingLevelMap[level] !== null && thinkingLevelMap[level] !== undefined) return level;
|
|
179
|
+
}
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
|
|
120
183
|
export function insertEffortBeforeCursorSuffix(modelId: string, effort: CursorEffort): string {
|
|
121
184
|
if (effort === "default") return modelId;
|
|
122
185
|
let base = modelId;
|
|
@@ -205,16 +268,36 @@ function collectEffortVariants(variants: readonly CursorVariant[], primaryId: st
|
|
|
205
268
|
return byEffort;
|
|
206
269
|
}
|
|
207
270
|
|
|
271
|
+
function buildCursorThinkingLevelMap(group: CursorVariantGroup, effortVariants: ReadonlyMap<CursorEffort, string>): ThinkingLevelMap {
|
|
272
|
+
const map = buildThinkingLevelMap(effortVariants, group.primaryId);
|
|
273
|
+
// When the group has no real base id (every Cursor variant carries an effort
|
|
274
|
+
// suffix), the synthesized primary id is not a sendable Cursor model. Record
|
|
275
|
+
// an `off` default so a no-thinking request maps to a concrete variant instead
|
|
276
|
+
// of the base id, which Cursor would reject with `not_found`. Prefer the
|
|
277
|
+
// minimal/least-effort variant because `off` means minimum reasoning.
|
|
278
|
+
const hasRealBaseId = group.variants.some((variant) => variant.id === group.primaryId);
|
|
279
|
+
if (!hasRealBaseId) {
|
|
280
|
+
const defaultVariant = map.minimal ?? map.low ?? map.medium ?? map.high ?? map.xhigh ?? null;
|
|
281
|
+
if (defaultVariant) map.off = defaultVariant;
|
|
282
|
+
}
|
|
283
|
+
return map;
|
|
284
|
+
}
|
|
285
|
+
|
|
208
286
|
function buildThinkingLevelMap(effortVariants: ReadonlyMap<CursorEffort, string>, primaryId: string): ThinkingLevelMap {
|
|
209
287
|
return {
|
|
210
288
|
minimal: chooseEffortVariant(effortVariants, THINKING_LEVEL_EFFORT_PREFERENCES.minimal, primaryId),
|
|
211
289
|
low: chooseEffortVariant(effortVariants, THINKING_LEVEL_EFFORT_PREFERENCES.low, primaryId),
|
|
212
290
|
medium: chooseEffortVariant(effortVariants, THINKING_LEVEL_EFFORT_PREFERENCES.medium, primaryId),
|
|
213
291
|
high: chooseEffortVariant(effortVariants, THINKING_LEVEL_EFFORT_PREFERENCES.high, primaryId),
|
|
214
|
-
xhigh
|
|
292
|
+
// `xhigh` should only be offered when Cursor advertises a true xhigh/max variant.
|
|
293
|
+
xhigh: chooseCursorXhighVariant(effortVariants),
|
|
215
294
|
};
|
|
216
295
|
}
|
|
217
296
|
|
|
297
|
+
function chooseCursorXhighVariant(effortVariants: ReadonlyMap<CursorEffort, string>): string | null {
|
|
298
|
+
return effortVariants.get("max") ?? effortVariants.get("xhigh") ?? null;
|
|
299
|
+
}
|
|
300
|
+
|
|
218
301
|
function chooseEffortVariant(effortVariants: ReadonlyMap<CursorEffort, string>, preferences: readonly CursorEffort[], _primaryId: string): string | null {
|
|
219
302
|
for (const effort of preferences) {
|
|
220
303
|
const variantId = effortVariants.get(effort);
|
|
@@ -232,8 +315,10 @@ function isCursorEffort(value: string): value is CursorEffort {
|
|
|
232
315
|
}
|
|
233
316
|
|
|
234
317
|
function chooseLargestNumber(values: readonly (number | undefined)[]): number | undefined {
|
|
235
|
-
|
|
236
|
-
|
|
318
|
+
// Cursor's private API omits token limits; treat any non-positive value as
|
|
319
|
+
// bogus so a stray 0/negative never becomes an invalid context window.
|
|
320
|
+
const positiveValues = values.filter((value): value is number => typeof value === "number" && Number.isFinite(value) && value > 0);
|
|
321
|
+
return positiveValues.length > 0 ? Math.max(...positiveValues) : undefined;
|
|
237
322
|
}
|
|
238
323
|
|
|
239
324
|
function choosePrimaryId(variants: readonly CursorVariant[], baseId: string): string {
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { getModels, getProviders } from "@earendil-works/pi-ai/compat";
|
|
2
|
+
|
|
3
|
+
export interface CursorModelReferenceCandidate {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly displayName?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface CursorModelReferenceLimits {
|
|
9
|
+
readonly contextWindow?: number;
|
|
10
|
+
readonly maxTokens?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CursorModelReferenceCatalogEntry {
|
|
14
|
+
readonly provider: string;
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly contextWindow: number;
|
|
18
|
+
readonly maxTokens: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ReferenceModelLimits {
|
|
22
|
+
readonly provider: string;
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly contextWindow: number;
|
|
26
|
+
readonly maxTokens: number;
|
|
27
|
+
readonly priority: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const ONE_MILLION_MODEL_NAME_CONTEXT_WINDOW = 1_000_000;
|
|
31
|
+
const UNRANKED_PROVIDER_PRIORITY = 10_000;
|
|
32
|
+
const CURSOR_EFFORT_SUFFIXES = ["none", "low", "medium", "high", "xhigh", "max", "default"] as const;
|
|
33
|
+
const CLAUDE_FAMILY_NAMES = new Set(["fable", "haiku", "opus", "sonnet"]);
|
|
34
|
+
|
|
35
|
+
const REFERENCE_PROVIDER_PRIORITY = new Map<string, number>([
|
|
36
|
+
["opencode", 0],
|
|
37
|
+
["opencode-go", 1],
|
|
38
|
+
["anthropic", 10],
|
|
39
|
+
["google", 20],
|
|
40
|
+
["google-vertex", 21],
|
|
41
|
+
["openai", 30],
|
|
42
|
+
["openai-codex", 31],
|
|
43
|
+
["azure-openai-responses", 32],
|
|
44
|
+
["xai", 40],
|
|
45
|
+
["moonshotai", 50],
|
|
46
|
+
["moonshotai-cn", 51],
|
|
47
|
+
["kimi-coding", 52],
|
|
48
|
+
["zai", 60],
|
|
49
|
+
["zai-coding-cn", 61],
|
|
50
|
+
["openrouter", 100],
|
|
51
|
+
["vercel-ai-gateway", 101],
|
|
52
|
+
["cloudflare-ai-gateway", 102],
|
|
53
|
+
["cloudflare-workers-ai", 103],
|
|
54
|
+
["github-copilot", 200],
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
const EMPTY_REFERENCE_MODEL_INDEX: ReadonlyMap<string, readonly ReferenceModelLimits[]> = new Map();
|
|
58
|
+
let referenceModelIndex: ReadonlyMap<string, readonly ReferenceModelLimits[]> | undefined;
|
|
59
|
+
let referenceModelCatalogOverride: readonly CursorModelReferenceCatalogEntry[] | undefined;
|
|
60
|
+
|
|
61
|
+
export function setCursorModelReferenceCatalogForTesting(models: readonly CursorModelReferenceCatalogEntry[] | undefined): void {
|
|
62
|
+
referenceModelCatalogOverride = models;
|
|
63
|
+
resetCursorModelReferenceIndex();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function resetCursorModelReferenceIndex(): void {
|
|
67
|
+
referenceModelIndex = undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function resolveCursorModelReferenceLimits(candidates: readonly CursorModelReferenceCandidate[]): CursorModelReferenceLimits {
|
|
71
|
+
// Limit resolution must never affect which Cursor models register. Any failure
|
|
72
|
+
// (e.g. an unexpected pi-ai catalog shape at runtime) degrades to "no reference
|
|
73
|
+
// limits" so the caller keeps the model with its estimate.
|
|
74
|
+
try {
|
|
75
|
+
const explicitOneMillion = candidates.some((candidate) => hasOneMillionMarker(candidate.id) || hasOneMillionMarker(candidate.displayName ?? ""));
|
|
76
|
+
const match = findReferenceModel(cursorCandidateAliases(candidates), explicitOneMillion);
|
|
77
|
+
if (!match) {
|
|
78
|
+
return explicitOneMillion ? { contextWindow: ONE_MILLION_MODEL_NAME_CONTEXT_WINDOW } : {};
|
|
79
|
+
}
|
|
80
|
+
const contextWindow = positiveIntOrUndefined(match.contextWindow);
|
|
81
|
+
return {
|
|
82
|
+
contextWindow: explicitOneMillion && contextWindow !== undefined
|
|
83
|
+
? Math.max(contextWindow, ONE_MILLION_MODEL_NAME_CONTEXT_WINDOW)
|
|
84
|
+
: contextWindow,
|
|
85
|
+
maxTokens: positiveIntOrUndefined(match.maxTokens),
|
|
86
|
+
};
|
|
87
|
+
} catch {
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function positiveIntOrUndefined(value: number | undefined): number | undefined {
|
|
93
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function findReferenceModel(aliases: readonly string[], preferOneMillion: boolean): ReferenceModelLimits | undefined {
|
|
97
|
+
const index = getReferenceModelIndex();
|
|
98
|
+
for (const alias of aliases) {
|
|
99
|
+
const matches = index.get(alias);
|
|
100
|
+
if (!matches?.[0]) continue;
|
|
101
|
+
if (preferOneMillion) {
|
|
102
|
+
const oneMillionMatch = matches.find((candidate) => candidate.contextWindow >= ONE_MILLION_MODEL_NAME_CONTEXT_WINDOW);
|
|
103
|
+
if (oneMillionMatch) return oneMillionMatch;
|
|
104
|
+
}
|
|
105
|
+
return matches[0];
|
|
106
|
+
}
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getReferenceModelIndex(): ReadonlyMap<string, readonly ReferenceModelLimits[]> {
|
|
111
|
+
if (referenceModelIndex) return referenceModelIndex;
|
|
112
|
+
try {
|
|
113
|
+
const mutableIndex = new Map<string, ReferenceModelLimits[]>();
|
|
114
|
+
for (const model of getReferenceModels()) {
|
|
115
|
+
for (const alias of referenceModelAliases(model)) {
|
|
116
|
+
const existing = mutableIndex.get(alias) ?? [];
|
|
117
|
+
existing.push(model);
|
|
118
|
+
mutableIndex.set(alias, existing);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
for (const matches of mutableIndex.values()) {
|
|
122
|
+
matches.sort(compareReferenceModels);
|
|
123
|
+
}
|
|
124
|
+
referenceModelIndex = mutableIndex;
|
|
125
|
+
} catch {
|
|
126
|
+
referenceModelIndex = EMPTY_REFERENCE_MODEL_INDEX;
|
|
127
|
+
}
|
|
128
|
+
return referenceModelIndex;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function getReferenceModels(): ReferenceModelLimits[] {
|
|
132
|
+
return referenceModelCatalogOverride
|
|
133
|
+
? referenceModelCatalogOverride.map((model) => toReferenceModelLimits(model, referenceProviderPriority(model.provider)))
|
|
134
|
+
: getPiAiReferenceModels();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function getPiAiReferenceModels(): ReferenceModelLimits[] {
|
|
138
|
+
const models: ReferenceModelLimits[] = [];
|
|
139
|
+
for (const provider of getProviders()) {
|
|
140
|
+
const priority = referenceProviderPriority(provider);
|
|
141
|
+
for (const model of getModels(provider)) {
|
|
142
|
+
if (!isPositiveFiniteNumber(model.contextWindow) || !isPositiveFiniteNumber(model.maxTokens)) continue;
|
|
143
|
+
models.push(toReferenceModelLimits(model, priority));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return models;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function referenceProviderPriority(provider: string): number {
|
|
150
|
+
return REFERENCE_PROVIDER_PRIORITY.get(provider) ?? UNRANKED_PROVIDER_PRIORITY;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function toReferenceModelLimits(model: CursorModelReferenceCatalogEntry, priority: number): ReferenceModelLimits {
|
|
154
|
+
return {
|
|
155
|
+
provider: model.provider,
|
|
156
|
+
id: model.id,
|
|
157
|
+
name: model.name,
|
|
158
|
+
contextWindow: model.contextWindow,
|
|
159
|
+
maxTokens: model.maxTokens,
|
|
160
|
+
priority,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function compareReferenceModels(left: ReferenceModelLimits, right: ReferenceModelLimits): number {
|
|
165
|
+
return left.priority - right.priority
|
|
166
|
+
|| left.provider.localeCompare(right.provider)
|
|
167
|
+
|| left.id.localeCompare(right.id)
|
|
168
|
+
|| left.name.localeCompare(right.name);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function referenceModelAliases(model: ReferenceModelLimits): string[] {
|
|
172
|
+
const aliases = new Set<string>();
|
|
173
|
+
addNormalizedAlias(aliases, model.id);
|
|
174
|
+
addNormalizedAlias(aliases, lastPathSegment(model.id));
|
|
175
|
+
addNormalizedAlias(aliases, model.name);
|
|
176
|
+
addNormalizedAlias(aliases, displayNameTail(model.name));
|
|
177
|
+
return [...aliases];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function cursorCandidateAliases(candidates: readonly CursorModelReferenceCandidate[]): string[] {
|
|
181
|
+
// Match strictly on Cursor model IDs, which already carry the family and
|
|
182
|
+
// version (e.g. `gpt-5.5-high`, `claude-4.6-sonnet-medium`). Cursor display
|
|
183
|
+
// names are short and generic ("Auto", "Sonnet 4") and would produce false
|
|
184
|
+
// matches against unrelated pi-ai models, so they are not used as match keys.
|
|
185
|
+
const aliases = new Set<string>();
|
|
186
|
+
for (const candidate of candidates) {
|
|
187
|
+
for (const alias of cursorIdAliases(candidate.id)) {
|
|
188
|
+
addNormalizedAlias(aliases, alias);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return [...aliases];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function cursorIdAliases(id: string): string[] {
|
|
195
|
+
const aliases = new Set<string>([id]);
|
|
196
|
+
for (const baseAlias of cursorBaseIdAliases(id)) {
|
|
197
|
+
aliases.add(baseAlias);
|
|
198
|
+
for (const claudeAlias of cursorClaudeIdAliases(baseAlias)) {
|
|
199
|
+
aliases.add(claudeAlias);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return [...aliases];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function cursorBaseIdAliases(id: string): string[] {
|
|
206
|
+
const aliases = new Set<string>([id]);
|
|
207
|
+
const withoutModes = stripTrailingCursorModes(id);
|
|
208
|
+
aliases.add(withoutModes);
|
|
209
|
+
const withoutEffort = stripTrailingEffort(withoutModes);
|
|
210
|
+
aliases.add(withoutEffort);
|
|
211
|
+
for (const alias of [...aliases]) {
|
|
212
|
+
if (alias.endsWith("-1m")) aliases.add(alias.slice(0, -"-1m".length));
|
|
213
|
+
}
|
|
214
|
+
return [...aliases];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function stripTrailingCursorModes(id: string): string {
|
|
218
|
+
let result = id;
|
|
219
|
+
let changed = true;
|
|
220
|
+
while (changed) {
|
|
221
|
+
changed = false;
|
|
222
|
+
if (result.endsWith("-fast")) {
|
|
223
|
+
result = result.slice(0, -"-fast".length);
|
|
224
|
+
changed = true;
|
|
225
|
+
}
|
|
226
|
+
if (result.endsWith("-thinking")) {
|
|
227
|
+
result = result.slice(0, -"-thinking".length);
|
|
228
|
+
changed = true;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function stripTrailingEffort(id: string): string {
|
|
235
|
+
for (const effort of CURSOR_EFFORT_SUFFIXES) {
|
|
236
|
+
const suffix = `-${effort}`;
|
|
237
|
+
if (id.endsWith(suffix)) return id.slice(0, -suffix.length);
|
|
238
|
+
}
|
|
239
|
+
return id;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function cursorClaudeIdAliases(id: string): string[] {
|
|
243
|
+
const match = /^claude-(\d+(?:[.-]\d+)*)-([a-z]+)(?:$|-)/iu.exec(id);
|
|
244
|
+
if (!match) return [];
|
|
245
|
+
const [, version, family] = match;
|
|
246
|
+
if (!version || !family || !CLAUDE_FAMILY_NAMES.has(family.toLowerCase())) return [];
|
|
247
|
+
return [`claude-${family.toLowerCase()}-${version.replace(/[.-]/gu, "-")}`];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function addNormalizedAlias(aliases: Set<string>, value: string): void {
|
|
251
|
+
const normalized = normalizeReferenceKey(value);
|
|
252
|
+
if (normalized) aliases.add(normalized);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function normalizeReferenceKey(value: string): string | undefined {
|
|
256
|
+
const normalized = value
|
|
257
|
+
.normalize("NFKD")
|
|
258
|
+
.replace(/[\u0300-\u036f]/gu, "")
|
|
259
|
+
.toLowerCase()
|
|
260
|
+
.replace(/&/gu, " and ")
|
|
261
|
+
.replace(/[^a-z0-9]+/gu, " ")
|
|
262
|
+
.trim();
|
|
263
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function lastPathSegment(id: string): string {
|
|
267
|
+
const parts = id.split("/");
|
|
268
|
+
return parts[parts.length - 1] ?? id;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function displayNameTail(name: string): string {
|
|
272
|
+
const parts = name.split(":");
|
|
273
|
+
return parts[parts.length - 1]?.trim() ?? name;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function hasOneMillionMarker(value: string): boolean {
|
|
277
|
+
return /(?:^|[^a-z0-9])1\s*m(?:$|[^a-z0-9])/iu.test(value);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function isPositiveFiniteNumber(value: number): boolean {
|
|
281
|
+
return Number.isFinite(value) && value > 0;
|
|
282
|
+
}
|
|
@@ -18,7 +18,7 @@ Protocol behavior intentionally copied from the reference provider:
|
|
|
18
18
|
- Run requests use generated `AgentRunRequest`, `ConversationStateStructure`, `ConversationAction`, `UserMessage`, and `ModelDetails` messages.
|
|
19
19
|
- `UserMessage.message_id`, `UserMessage.correlation_id`, and reconstructed historical turn request ids are UUIDs generated the same way as the reference provider.
|
|
20
20
|
- Conversation ids are deterministic UUIDs derived from the hashed conversation key (`conv:<session-or-first-user-text>`), matching the reference provider rather than sending raw Atomic session ids to Cursor.
|
|
21
|
-
- Static fallback models are the reference `cursor-models-raw.json`; live model discovery is opportunistic and only replaces the registered catalog when Cursor returns usable models.
|
|
21
|
+
- Static fallback models are the reference `cursor-models-raw.json`; live model discovery is opportunistic and only replaces the registered catalog when Cursor returns usable models. Cursor's model-discovery protobuf carries no context-window or output-token fields, so the provider preserves any positive limits and otherwise resolves them from Atomic's bundled `@earendil-works/pi-ai` model catalog by Cursor model ID (with a conservative estimate for unmatched Cursor-only models and a 1,000,000-token floor for explicit `1M` Cursor labels); this only sets limits and does not change catalog membership.
|
|
22
22
|
- Tool definitions are returned in response to `ExecServerMessage.request_context_args = 10`; `McpArgs` messages become Atomic tool calls and active tool results are sent back as generated `ExecClientMessage.mcp_result` frames.
|
|
23
23
|
- Checkpoint and blob-store state is persisted per Cursor conversation id and discarded on Cursor end-stream errors such as `not_found`.
|
|
24
24
|
- `InteractionUpdate.turn_ended` is non-terminal; the stream closes on the Connect stream ending.
|
|
@@ -27,7 +27,7 @@ Manual smoke-test procedure after Cursor releases:
|
|
|
27
27
|
|
|
28
28
|
1. Sign in to the current Cursor CLI/app and capture a successful `api2.cursor.sh` model discovery or agent `Run` request.
|
|
29
29
|
2. Update `CURSOR_CLIENT_VERSION` in `src/config.ts` from the captured `x-cursor-client-version` header if it changed.
|
|
30
|
-
3. In Atomic, run `/login`, select **Cursor**, complete browser auth, then confirm `/model` lists `cursor/<model-id>` entries from live discovery.
|
|
30
|
+
3. In Atomic, run `/login`, select **Cursor (Experimental)**, complete browser auth, then confirm `/model` lists `cursor/<model-id>` entries from live discovery.
|
|
31
31
|
4. Select a Cursor model and run one chat turn plus one tool-using turn; verify the process exits cleanly for a one-shot/noninteractive run.
|
|
32
32
|
5. Re-run the Cursor unit tests and update these notes for any changed protobuf paths.
|
|
33
33
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { create, fromBinary, toBinary } from "@bufbuild/protobuf";
|
|
3
|
-
import type { ImageContent } from "@earendil-works/pi-ai";
|
|
3
|
+
import type { ImageContent } from "@earendil-works/pi-ai/compat";
|
|
4
4
|
import { parseJsonObject, type JsonObject } from "../config.js";
|
|
5
5
|
import type { CursorRunRequest, CursorToolResultContent } from "../transport.js";
|
|
6
6
|
import {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash, randomUUID as nodeRandomUUID } from "node:crypto";
|
|
2
|
-
import { type Api, type AssistantMessage, type AssistantMessageEventStream, calculateCost, type Context, createAssistantMessageEventStream, type Model, type SimpleStreamOptions } from "@earendil-works/pi-ai";
|
|
2
|
+
import { type Api, type AssistantMessage, type AssistantMessageEventStream, calculateCost, type Context, createAssistantMessageEventStream, type Model, type SimpleStreamOptions } from "@earendil-works/pi-ai/compat";
|
|
3
3
|
import { parseJsonObject, sanitizeDiagnosticText } from "./config.js";
|
|
4
4
|
import { CursorConversationStateStore, type CursorConversationSnapshot } from "./conversation-state.js";
|
|
5
5
|
import { resolveCursorModelVariant } from "./model-mapper.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Context, Model, Api, ThinkingLevel } from "@earendil-works/pi-ai";
|
|
1
|
+
import type { Context, Model, Api, ThinkingLevel } from "@earendil-works/pi-ai/compat";
|
|
2
2
|
import type { CursorUsableModel } from "./model-mapper.js";
|
|
3
3
|
|
|
4
4
|
export interface CursorTransportLifecycleSnapshot {
|
|
@@ -4,6 +4,18 @@ All notable changes to the `pi-intercom` extension will be documented in this fi
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.9.3-alpha.5] - 2026-06-28
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Aligned the intercom extension peer dependency with upstream pi TUI `^0.80.2`; no intercom extension source changes were needed for this metadata sync.
|
|
12
|
+
|
|
13
|
+
## [0.9.3-alpha.4] - 2026-06-28
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Published a synchronized Atomic 0.9.3-alpha.4 prerelease for the intercom extension; no intercom extension changes were made after 0.9.3-alpha.3.
|
|
18
|
+
|
|
7
19
|
## [0.9.3-alpha.3] - 2026-06-27
|
|
8
20
|
|
|
9
21
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/intercom",
|
|
3
|
-
"version": "0.9.3-alpha.
|
|
3
|
+
"version": "0.9.3-alpha.5",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
|
|
6
6
|
"contributors": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@bastani/atomic": "*",
|
|
42
|
-
"@earendil-works/pi-tui": "^0.
|
|
42
|
+
"@earendil-works/pi-tui": "^0.80.2"
|
|
43
43
|
},
|
|
44
44
|
"peerDependenciesMeta": {
|
|
45
45
|
"@bastani/atomic": {
|
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.9.3-alpha.5] - 2026-06-28
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Aligned the MCP extension peer dependencies with upstream pi `^0.80.2` (`@earendil-works/pi-ai` and `@earendil-works/pi-tui`) and retargeted legacy provider/model imports to `@earendil-works/pi-ai/compat`, preserving the MCP sampling provider behavior under the updated pi-ai package layout.
|
|
15
|
+
|
|
16
|
+
## [0.9.3-alpha.4] - 2026-06-28
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Published a synchronized Atomic 0.9.3-alpha.4 prerelease for the MCP extension; no MCP extension changes were made after 0.9.3-alpha.3.
|
|
21
|
+
|
|
10
22
|
## [0.9.3-alpha.3] - 2026-06-27
|
|
11
23
|
|
|
12
24
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/mcp",
|
|
3
|
-
"version": "0.9.3-alpha.
|
|
3
|
+
"version": "0.9.3-alpha.5",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension that adapts MCP (Model Context Protocol) servers into the coding agent. Fork of: https://github.com/nicobailon/pi-mcp-adapter",
|
|
6
6
|
"contributors": [
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"@bastani/atomic": "*",
|
|
35
|
-
"@earendil-works/pi-ai": "^0.
|
|
36
|
-
"@earendil-works/pi-tui": "^0.
|
|
35
|
+
"@earendil-works/pi-ai": "^0.80.2",
|
|
36
|
+
"@earendil-works/pi-tui": "^0.80.2",
|
|
37
37
|
"zod": "^3.25.0 || ^4.0.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependenciesMeta": {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { complete, type Api, type AssistantMessage, type Message, type Model, type TextContent } from "@earendil-works/pi-ai";
|
|
1
|
+
import { complete, type Api, type AssistantMessage, type Message, type Model, type TextContent } from "@earendil-works/pi-ai/compat";
|
|
2
2
|
import { truncateAtWord } from "./utils.ts";
|
|
3
3
|
import type { ExtensionUIContext, ModelRegistry } from "@bastani/atomic";
|
|
4
4
|
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
3
|
import type { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
4
4
|
import type { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
5
|
-
import type { TextContent, ImageContent } from "@earendil-works/pi-ai";
|
|
5
|
+
import type { TextContent, ImageContent } from "@earendil-works/pi-ai/compat";
|
|
6
6
|
import type { UiStreamMode } from "./ui-stream-types.ts";
|
|
7
7
|
|
|
8
8
|
// Transport type (stdio + HTTP)
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.3-alpha.5] - 2026-06-28
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Aligned the subagents extension peer dependencies with upstream pi `^0.80.2` runtime packages (`@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, and `@earendil-works/pi-tui`) and retargeted legacy provider/model imports to `@earendil-works/pi-ai/compat`, preserving delegated model resolution and streaming behavior under the updated pi-ai package layout.
|
|
10
|
+
|
|
11
|
+
## [0.9.3-alpha.4] - 2026-06-28
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Published a synchronized Atomic 0.9.3-alpha.4 prerelease for the subagents extension; no subagents extension changes were made after 0.9.3-alpha.3.
|
|
16
|
+
|
|
5
17
|
## [0.9.3-alpha.3] - 2026-06-27
|
|
6
18
|
|
|
7
19
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/subagents",
|
|
3
|
-
"version": "0.9.3-alpha.
|
|
3
|
+
"version": "0.9.3-alpha.5",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification. Fork of: https://github.com/nicobailon/pi-subagents",
|
|
6
6
|
"contributors": [
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@bastani/atomic": "*",
|
|
41
|
-
"@earendil-works/pi-agent-core": "^0.
|
|
42
|
-
"@earendil-works/pi-ai": "^0.
|
|
43
|
-
"@earendil-works/pi-tui": "^0.
|
|
41
|
+
"@earendil-works/pi-agent-core": "^0.80.2",
|
|
42
|
+
"@earendil-works/pi-ai": "^0.80.2",
|
|
43
|
+
"@earendil-works/pi-tui": "^0.80.2"
|
|
44
44
|
},
|
|
45
45
|
"peerDependenciesMeta": {
|
|
46
46
|
"@bastani/atomic": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
|
-
import type { Message } from "@earendil-works/pi-ai";
|
|
3
|
+
import type { Message } from "@earendil-works/pi-ai/compat";
|
|
4
4
|
import { appendJsonl } from "../../shared/artifacts.ts";
|
|
5
5
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
6
6
|
import { detectSubagentError, extractTextFromContent, extractToolArgsPreview, getFinalOutput } from "../../shared/utils.ts";
|