@otto-assistant/bridge 0.4.92
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/bin.js +2 -0
- package/dist/agent-model.e2e.test.js +755 -0
- package/dist/ai-tool-to-genai.js +233 -0
- package/dist/ai-tool-to-genai.test.js +267 -0
- package/dist/ai-tool.js +6 -0
- package/dist/anthropic-auth-plugin.js +728 -0
- package/dist/anthropic-auth-plugin.test.js +125 -0
- package/dist/anthropic-auth-state.js +231 -0
- package/dist/bin.js +90 -0
- package/dist/channel-management.js +227 -0
- package/dist/cli-parsing.test.js +137 -0
- package/dist/cli-send-thread.e2e.test.js +356 -0
- package/dist/cli.js +3276 -0
- package/dist/commands/abort.js +65 -0
- package/dist/commands/action-buttons.js +245 -0
- package/dist/commands/add-project.js +113 -0
- package/dist/commands/agent.js +335 -0
- package/dist/commands/ask-question.js +274 -0
- package/dist/commands/btw.js +116 -0
- package/dist/commands/compact.js +120 -0
- package/dist/commands/context-usage.js +140 -0
- package/dist/commands/create-new-project.js +130 -0
- package/dist/commands/diff.js +63 -0
- package/dist/commands/file-upload.js +275 -0
- package/dist/commands/fork.js +220 -0
- package/dist/commands/gemini-apikey.js +70 -0
- package/dist/commands/login.js +885 -0
- package/dist/commands/mcp.js +239 -0
- package/dist/commands/memory-snapshot.js +24 -0
- package/dist/commands/mention-mode.js +44 -0
- package/dist/commands/merge-worktree.js +159 -0
- package/dist/commands/model-variant.js +364 -0
- package/dist/commands/model.js +776 -0
- package/dist/commands/new-worktree.js +366 -0
- package/dist/commands/paginated-select.js +57 -0
- package/dist/commands/permissions.js +274 -0
- package/dist/commands/queue.js +206 -0
- package/dist/commands/remove-project.js +115 -0
- package/dist/commands/restart-opencode-server.js +127 -0
- package/dist/commands/resume.js +149 -0
- package/dist/commands/run-command.js +79 -0
- package/dist/commands/screenshare.js +303 -0
- package/dist/commands/screenshare.test.js +20 -0
- package/dist/commands/session-id.js +78 -0
- package/dist/commands/session.js +176 -0
- package/dist/commands/share.js +80 -0
- package/dist/commands/tasks.js +205 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/undo-redo.js +305 -0
- package/dist/commands/unset-model.js +138 -0
- package/dist/commands/upgrade.js +42 -0
- package/dist/commands/user-command.js +155 -0
- package/dist/commands/verbosity.js +125 -0
- package/dist/commands/worktree-settings.js +43 -0
- package/dist/commands/worktrees.js +410 -0
- package/dist/condense-memory.js +33 -0
- package/dist/config.js +94 -0
- package/dist/context-awareness-plugin.js +363 -0
- package/dist/context-awareness-plugin.test.js +124 -0
- package/dist/critique-utils.js +95 -0
- package/dist/database.js +1310 -0
- package/dist/db.js +251 -0
- package/dist/db.test.js +138 -0
- package/dist/debounce-timeout.js +28 -0
- package/dist/debounced-process-flush.js +77 -0
- package/dist/discord-bot.js +1008 -0
- package/dist/discord-command-registration.js +524 -0
- package/dist/discord-urls.js +81 -0
- package/dist/discord-utils.js +591 -0
- package/dist/discord-utils.test.js +134 -0
- package/dist/errors.js +157 -0
- package/dist/escape-backticks.test.js +429 -0
- package/dist/event-stream-real-capture.e2e.test.js +533 -0
- package/dist/eventsource-parser.test.js +327 -0
- package/dist/exec-async.js +26 -0
- package/dist/external-opencode-sync.js +480 -0
- package/dist/format-tables.js +302 -0
- package/dist/format-tables.test.js +308 -0
- package/dist/forum-sync/config.js +79 -0
- package/dist/forum-sync/discord-operations.js +154 -0
- package/dist/forum-sync/index.js +5 -0
- package/dist/forum-sync/markdown.js +113 -0
- package/dist/forum-sync/sync-to-discord.js +417 -0
- package/dist/forum-sync/sync-to-files.js +190 -0
- package/dist/forum-sync/types.js +53 -0
- package/dist/forum-sync/watchers.js +307 -0
- package/dist/gateway-proxy-reconnect.e2e.test.js +394 -0
- package/dist/gateway-proxy.e2e.test.js +483 -0
- package/dist/genai-worker-wrapper.js +111 -0
- package/dist/genai-worker.js +311 -0
- package/dist/genai.js +232 -0
- package/dist/generated/browser.js +17 -0
- package/dist/generated/client.js +37 -0
- package/dist/generated/commonInputTypes.js +10 -0
- package/dist/generated/enums.js +52 -0
- package/dist/generated/internal/class.js +49 -0
- package/dist/generated/internal/prismaNamespace.js +253 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js +223 -0
- package/dist/generated/models/bot_api_keys.js +1 -0
- package/dist/generated/models/bot_tokens.js +1 -0
- package/dist/generated/models/channel_agents.js +1 -0
- package/dist/generated/models/channel_directories.js +1 -0
- package/dist/generated/models/channel_mention_mode.js +1 -0
- package/dist/generated/models/channel_models.js +1 -0
- package/dist/generated/models/channel_verbosity.js +1 -0
- package/dist/generated/models/channel_worktrees.js +1 -0
- package/dist/generated/models/forum_sync_configs.js +1 -0
- package/dist/generated/models/global_models.js +1 -0
- package/dist/generated/models/ipc_requests.js +1 -0
- package/dist/generated/models/part_messages.js +1 -0
- package/dist/generated/models/scheduled_tasks.js +1 -0
- package/dist/generated/models/session_agents.js +1 -0
- package/dist/generated/models/session_events.js +1 -0
- package/dist/generated/models/session_models.js +1 -0
- package/dist/generated/models/session_start_sources.js +1 -0
- package/dist/generated/models/thread_sessions.js +1 -0
- package/dist/generated/models/thread_worktrees.js +1 -0
- package/dist/generated/models.js +1 -0
- package/dist/heap-monitor.js +122 -0
- package/dist/hrana-server.js +263 -0
- package/dist/hrana-server.test.js +370 -0
- package/dist/html-actions.js +123 -0
- package/dist/html-actions.test.js +70 -0
- package/dist/html-components.js +117 -0
- package/dist/html-components.test.js +34 -0
- package/dist/image-optimizer-plugin.js +153 -0
- package/dist/image-utils.js +112 -0
- package/dist/interaction-handler.js +397 -0
- package/dist/ipc-polling.js +252 -0
- package/dist/ipc-tools-plugin.js +193 -0
- package/dist/kimaki-digital-twin.e2e.test.js +161 -0
- package/dist/kimaki-opencode-plugin-loading.e2e.test.js +87 -0
- package/dist/kimaki-opencode-plugin.js +17 -0
- package/dist/kimaki-opencode-plugin.test.js +98 -0
- package/dist/limit-heading-depth.js +25 -0
- package/dist/limit-heading-depth.test.js +105 -0
- package/dist/logger.js +165 -0
- package/dist/markdown.js +342 -0
- package/dist/markdown.test.js +257 -0
- package/dist/message-finish-field.e2e.test.js +165 -0
- package/dist/message-formatting.js +413 -0
- package/dist/message-formatting.test.js +73 -0
- package/dist/message-preprocessing.js +330 -0
- package/dist/onboarding-tutorial.js +172 -0
- package/dist/onboarding-welcome.js +37 -0
- package/dist/openai-realtime.js +224 -0
- package/dist/opencode-command-detection.js +65 -0
- package/dist/opencode-command-detection.test.js +240 -0
- package/dist/opencode-command.js +129 -0
- package/dist/opencode-command.test.js +48 -0
- package/dist/opencode-interrupt-plugin.js +361 -0
- package/dist/opencode-interrupt-plugin.test.js +458 -0
- package/dist/opencode.js +861 -0
- package/dist/otto/branding.js +22 -0
- package/dist/otto/index.js +21 -0
- package/dist/parse-permission-rules.test.js +117 -0
- package/dist/patch-text-parser.js +97 -0
- package/dist/plugin-logger.js +59 -0
- package/dist/privacy-sanitizer.js +105 -0
- package/dist/queue-advanced-abort.e2e.test.js +293 -0
- package/dist/queue-advanced-action-buttons.e2e.test.js +206 -0
- package/dist/queue-advanced-e2e-setup.js +786 -0
- package/dist/queue-advanced-footer.e2e.test.js +472 -0
- package/dist/queue-advanced-model-switch.e2e.test.js +299 -0
- package/dist/queue-advanced-permissions-typing.e2e.test.js +180 -0
- package/dist/queue-advanced-question.e2e.test.js +261 -0
- package/dist/queue-advanced-typing-interrupt.e2e.test.js +114 -0
- package/dist/queue-advanced-typing.e2e.test.js +153 -0
- package/dist/queue-drain-after-interactive-ui.e2e.test.js +119 -0
- package/dist/queue-interrupt-drain.e2e.test.js +135 -0
- package/dist/queue-question-select-drain.e2e.test.js +120 -0
- package/dist/runtime-idle-sweeper.js +52 -0
- package/dist/runtime-lifecycle.e2e.test.js +508 -0
- package/dist/sentry.js +23 -0
- package/dist/session-handler/agent-utils.js +67 -0
- package/dist/session-handler/event-stream-state.js +420 -0
- package/dist/session-handler/event-stream-state.test.js +563 -0
- package/dist/session-handler/model-utils.js +124 -0
- package/dist/session-handler/opencode-session-event-log.js +94 -0
- package/dist/session-handler/thread-runtime-state.js +104 -0
- package/dist/session-handler/thread-session-runtime.js +3258 -0
- package/dist/session-handler.js +9 -0
- package/dist/session-search.js +100 -0
- package/dist/session-search.test.js +40 -0
- package/dist/session-title-rename.test.js +80 -0
- package/dist/startup-service.js +153 -0
- package/dist/startup-time.e2e.test.js +296 -0
- package/dist/store.js +17 -0
- package/dist/system-message.js +613 -0
- package/dist/system-message.test.js +602 -0
- package/dist/task-runner.js +295 -0
- package/dist/task-schedule.js +209 -0
- package/dist/task-schedule.test.js +71 -0
- package/dist/test-utils.js +299 -0
- package/dist/thinking-utils.js +35 -0
- package/dist/thread-message-queue.e2e.test.js +999 -0
- package/dist/tools.js +357 -0
- package/dist/undo-redo.e2e.test.js +161 -0
- package/dist/unnest-code-blocks.js +146 -0
- package/dist/unnest-code-blocks.test.js +673 -0
- package/dist/upgrade.js +114 -0
- package/dist/utils.js +144 -0
- package/dist/voice-attachment.js +34 -0
- package/dist/voice-handler.js +646 -0
- package/dist/voice-message.e2e.test.js +1021 -0
- package/dist/voice.js +447 -0
- package/dist/voice.test.js +235 -0
- package/dist/wait-session.js +94 -0
- package/dist/websockify.js +69 -0
- package/dist/worker-types.js +4 -0
- package/dist/worktree-lifecycle.e2e.test.js +308 -0
- package/dist/worktree-utils.js +3 -0
- package/dist/worktrees.js +929 -0
- package/dist/worktrees.test.js +189 -0
- package/dist/xml.js +92 -0
- package/dist/xml.test.js +32 -0
- package/package.json +98 -0
- package/schema.prisma +295 -0
- package/skills/batch/SKILL.md +87 -0
- package/skills/critique/SKILL.md +112 -0
- package/skills/egaki/SKILL.md +100 -0
- package/skills/errore/SKILL.md +647 -0
- package/skills/event-sourcing-state/SKILL.md +252 -0
- package/skills/gitchamber/SKILL.md +93 -0
- package/skills/goke/SKILL.md +644 -0
- package/skills/jitter/EDITOR.md +219 -0
- package/skills/jitter/EXPORT-INTERNALS.md +309 -0
- package/skills/jitter/SKILL.md +158 -0
- package/skills/jitter/jitter-clipboard.json +1042 -0
- package/skills/jitter/package.json +14 -0
- package/skills/jitter/tsconfig.json +15 -0
- package/skills/jitter/utils/actions.ts +212 -0
- package/skills/jitter/utils/export.ts +114 -0
- package/skills/jitter/utils/index.ts +141 -0
- package/skills/jitter/utils/snapshot.ts +154 -0
- package/skills/jitter/utils/traverse.ts +246 -0
- package/skills/jitter/utils/types.ts +279 -0
- package/skills/jitter/utils/wait.ts +133 -0
- package/skills/lintcn/SKILL.md +873 -0
- package/skills/new-skill/SKILL.md +211 -0
- package/skills/npm-package/SKILL.md +239 -0
- package/skills/playwriter/SKILL.md +35 -0
- package/skills/proxyman/SKILL.md +215 -0
- package/skills/security-review/SKILL.md +208 -0
- package/skills/simplify/SKILL.md +58 -0
- package/skills/spiceflow/SKILL.md +14 -0
- package/skills/termcast/SKILL.md +945 -0
- package/skills/tuistory/SKILL.md +250 -0
- package/skills/usecomputer/SKILL.md +264 -0
- package/skills/x-articles/SKILL.md +554 -0
- package/skills/zele/SKILL.md +112 -0
- package/skills/zustand-centralized-state/SKILL.md +1004 -0
- package/src/agent-model.e2e.test.ts +976 -0
- package/src/ai-tool-to-genai.test.ts +296 -0
- package/src/ai-tool-to-genai.ts +283 -0
- package/src/ai-tool.ts +39 -0
- package/src/anthropic-auth-plugin.test.ts +159 -0
- package/src/anthropic-auth-plugin.ts +861 -0
- package/src/anthropic-auth-state.ts +282 -0
- package/src/bin.ts +111 -0
- package/src/channel-management.ts +334 -0
- package/src/cli-parsing.test.ts +195 -0
- package/src/cli-send-thread.e2e.test.ts +464 -0
- package/src/cli.ts +4581 -0
- package/src/commands/abort.ts +89 -0
- package/src/commands/action-buttons.ts +364 -0
- package/src/commands/add-project.ts +149 -0
- package/src/commands/agent.ts +473 -0
- package/src/commands/ask-question.ts +390 -0
- package/src/commands/btw.ts +164 -0
- package/src/commands/compact.ts +157 -0
- package/src/commands/context-usage.ts +199 -0
- package/src/commands/create-new-project.ts +190 -0
- package/src/commands/diff.ts +91 -0
- package/src/commands/file-upload.ts +389 -0
- package/src/commands/fork.ts +321 -0
- package/src/commands/gemini-apikey.ts +104 -0
- package/src/commands/login.ts +1173 -0
- package/src/commands/mcp.ts +307 -0
- package/src/commands/memory-snapshot.ts +30 -0
- package/src/commands/mention-mode.ts +68 -0
- package/src/commands/merge-worktree.ts +223 -0
- package/src/commands/model-variant.ts +483 -0
- package/src/commands/model.ts +1053 -0
- package/src/commands/new-worktree.ts +510 -0
- package/src/commands/paginated-select.ts +81 -0
- package/src/commands/permissions.ts +397 -0
- package/src/commands/queue.ts +271 -0
- package/src/commands/remove-project.ts +155 -0
- package/src/commands/restart-opencode-server.ts +162 -0
- package/src/commands/resume.ts +230 -0
- package/src/commands/run-command.ts +123 -0
- package/src/commands/screenshare.test.ts +30 -0
- package/src/commands/screenshare.ts +366 -0
- package/src/commands/session-id.ts +109 -0
- package/src/commands/session.ts +227 -0
- package/src/commands/share.ts +106 -0
- package/src/commands/tasks.ts +293 -0
- package/src/commands/types.ts +25 -0
- package/src/commands/undo-redo.ts +386 -0
- package/src/commands/unset-model.ts +173 -0
- package/src/commands/upgrade.ts +52 -0
- package/src/commands/user-command.ts +198 -0
- package/src/commands/verbosity.ts +173 -0
- package/src/commands/worktree-settings.ts +70 -0
- package/src/commands/worktrees.ts +552 -0
- package/src/condense-memory.ts +36 -0
- package/src/config.ts +111 -0
- package/src/context-awareness-plugin.test.ts +142 -0
- package/src/context-awareness-plugin.ts +510 -0
- package/src/critique-utils.ts +139 -0
- package/src/database.ts +1876 -0
- package/src/db.test.ts +162 -0
- package/src/db.ts +286 -0
- package/src/debounce-timeout.ts +43 -0
- package/src/debounced-process-flush.ts +104 -0
- package/src/discord-bot.ts +1330 -0
- package/src/discord-command-registration.ts +693 -0
- package/src/discord-urls.ts +88 -0
- package/src/discord-utils.test.ts +153 -0
- package/src/discord-utils.ts +800 -0
- package/src/errors.ts +201 -0
- package/src/escape-backticks.test.ts +469 -0
- package/src/event-stream-real-capture.e2e.test.ts +692 -0
- package/src/eventsource-parser.test.ts +351 -0
- package/src/exec-async.ts +35 -0
- package/src/external-opencode-sync.ts +685 -0
- package/src/format-tables.test.ts +335 -0
- package/src/format-tables.ts +445 -0
- package/src/forum-sync/config.ts +92 -0
- package/src/forum-sync/discord-operations.ts +241 -0
- package/src/forum-sync/index.ts +9 -0
- package/src/forum-sync/markdown.ts +172 -0
- package/src/forum-sync/sync-to-discord.ts +595 -0
- package/src/forum-sync/sync-to-files.ts +294 -0
- package/src/forum-sync/types.ts +175 -0
- package/src/forum-sync/watchers.ts +454 -0
- package/src/gateway-proxy-reconnect.e2e.test.ts +523 -0
- package/src/gateway-proxy.e2e.test.ts +640 -0
- package/src/genai-worker-wrapper.ts +164 -0
- package/src/genai-worker.ts +386 -0
- package/src/genai.ts +321 -0
- package/src/generated/browser.ts +114 -0
- package/src/generated/client.ts +138 -0
- package/src/generated/commonInputTypes.ts +736 -0
- package/src/generated/enums.ts +88 -0
- package/src/generated/internal/class.ts +384 -0
- package/src/generated/internal/prismaNamespace.ts +2386 -0
- package/src/generated/internal/prismaNamespaceBrowser.ts +326 -0
- package/src/generated/models/bot_api_keys.ts +1288 -0
- package/src/generated/models/bot_tokens.ts +1656 -0
- package/src/generated/models/channel_agents.ts +1256 -0
- package/src/generated/models/channel_directories.ts +1859 -0
- package/src/generated/models/channel_mention_mode.ts +1300 -0
- package/src/generated/models/channel_models.ts +1288 -0
- package/src/generated/models/channel_verbosity.ts +1228 -0
- package/src/generated/models/channel_worktrees.ts +1300 -0
- package/src/generated/models/forum_sync_configs.ts +1452 -0
- package/src/generated/models/global_models.ts +1288 -0
- package/src/generated/models/ipc_requests.ts +1485 -0
- package/src/generated/models/part_messages.ts +1302 -0
- package/src/generated/models/scheduled_tasks.ts +2320 -0
- package/src/generated/models/session_agents.ts +1086 -0
- package/src/generated/models/session_events.ts +1439 -0
- package/src/generated/models/session_models.ts +1114 -0
- package/src/generated/models/session_start_sources.ts +1408 -0
- package/src/generated/models/thread_sessions.ts +1781 -0
- package/src/generated/models/thread_worktrees.ts +1356 -0
- package/src/generated/models.ts +30 -0
- package/src/heap-monitor.ts +152 -0
- package/src/hrana-server.test.ts +434 -0
- package/src/hrana-server.ts +314 -0
- package/src/html-actions.test.ts +87 -0
- package/src/html-actions.ts +174 -0
- package/src/html-components.test.ts +38 -0
- package/src/html-components.ts +181 -0
- package/src/image-optimizer-plugin.ts +194 -0
- package/src/image-utils.ts +149 -0
- package/src/interaction-handler.ts +576 -0
- package/src/ipc-polling.ts +326 -0
- package/src/ipc-tools-plugin.ts +236 -0
- package/src/kimaki-digital-twin.e2e.test.ts +199 -0
- package/src/kimaki-opencode-plugin-loading.e2e.test.ts +109 -0
- package/src/kimaki-opencode-plugin.test.ts +108 -0
- package/src/kimaki-opencode-plugin.ts +18 -0
- package/src/limit-heading-depth.test.ts +116 -0
- package/src/limit-heading-depth.ts +26 -0
- package/src/logger.ts +208 -0
- package/src/markdown.test.ts +308 -0
- package/src/markdown.ts +410 -0
- package/src/message-finish-field.e2e.test.ts +192 -0
- package/src/message-formatting.test.ts +81 -0
- package/src/message-formatting.ts +533 -0
- package/src/message-preprocessing.ts +455 -0
- package/src/onboarding-tutorial.ts +176 -0
- package/src/onboarding-welcome.ts +49 -0
- package/src/openai-realtime.ts +358 -0
- package/src/opencode-command-detection.test.ts +307 -0
- package/src/opencode-command-detection.ts +76 -0
- package/src/opencode-command.test.ts +70 -0
- package/src/opencode-command.ts +188 -0
- package/src/opencode-interrupt-plugin.test.ts +677 -0
- package/src/opencode-interrupt-plugin.ts +477 -0
- package/src/opencode.ts +1110 -0
- package/src/otto/branding.ts +23 -0
- package/src/otto/index.ts +22 -0
- package/src/parse-permission-rules.test.ts +127 -0
- package/src/patch-text-parser.ts +107 -0
- package/src/plugin-logger.ts +68 -0
- package/src/privacy-sanitizer.ts +142 -0
- package/src/queue-advanced-abort.e2e.test.ts +382 -0
- package/src/queue-advanced-action-buttons.e2e.test.ts +268 -0
- package/src/queue-advanced-e2e-setup.ts +873 -0
- package/src/queue-advanced-footer.e2e.test.ts +576 -0
- package/src/queue-advanced-model-switch.e2e.test.ts +383 -0
- package/src/queue-advanced-permissions-typing.e2e.test.ts +245 -0
- package/src/queue-advanced-question.e2e.test.ts +316 -0
- package/src/queue-advanced-typing-interrupt.e2e.test.ts +146 -0
- package/src/queue-advanced-typing.e2e.test.ts +199 -0
- package/src/queue-drain-after-interactive-ui.e2e.test.ts +151 -0
- package/src/queue-interrupt-drain.e2e.test.ts +166 -0
- package/src/queue-question-select-drain.e2e.test.ts +152 -0
- package/src/runtime-idle-sweeper.ts +76 -0
- package/src/runtime-lifecycle.e2e.test.ts +641 -0
- package/src/schema.sql +173 -0
- package/src/sentry.ts +26 -0
- package/src/session-handler/agent-utils.ts +97 -0
- package/src/session-handler/event-stream-fixtures/real-session-action-buttons.jsonl +45 -0
- package/src/session-handler/event-stream-fixtures/real-session-footer-suppressed-on-pre-idle-interrupt.jsonl +40 -0
- package/src/session-handler/event-stream-fixtures/real-session-permission-external-file.jsonl +23 -0
- package/src/session-handler/event-stream-fixtures/real-session-task-normal.jsonl +22 -0
- package/src/session-handler/event-stream-fixtures/real-session-task-three-parallel-sleeps.jsonl +277 -0
- package/src/session-handler/event-stream-fixtures/real-session-task-user-interruption.jsonl +46 -0
- package/src/session-handler/event-stream-fixtures/session-abort-after-idle-race.jsonl +21 -0
- package/src/session-handler/event-stream-fixtures/session-concurrent-messages-serialized.jsonl +56 -0
- package/src/session-handler/event-stream-fixtures/session-explicit-abort.jsonl +44 -0
- package/src/session-handler/event-stream-fixtures/session-normal-completion.jsonl +29 -0
- package/src/session-handler/event-stream-fixtures/session-tool-call-noisy-stream.jsonl +29 -0
- package/src/session-handler/event-stream-fixtures/session-two-completions-same-session.jsonl +50 -0
- package/src/session-handler/event-stream-fixtures/session-user-interruption.jsonl +59 -0
- package/src/session-handler/event-stream-fixtures/session-voice-queued-followup.jsonl +52 -0
- package/src/session-handler/event-stream-state.test.ts +645 -0
- package/src/session-handler/event-stream-state.ts +608 -0
- package/src/session-handler/model-utils.ts +183 -0
- package/src/session-handler/opencode-session-event-log.ts +130 -0
- package/src/session-handler/thread-runtime-state.ts +212 -0
- package/src/session-handler/thread-session-runtime.ts +4281 -0
- package/src/session-handler.ts +15 -0
- package/src/session-search.test.ts +50 -0
- package/src/session-search.ts +148 -0
- package/src/session-title-rename.test.ts +112 -0
- package/src/startup-service.ts +200 -0
- package/src/startup-time.e2e.test.ts +373 -0
- package/src/store.ts +122 -0
- package/src/system-message.test.ts +612 -0
- package/src/system-message.ts +723 -0
- package/src/task-runner.ts +421 -0
- package/src/task-schedule.test.ts +84 -0
- package/src/task-schedule.ts +311 -0
- package/src/test-utils.ts +435 -0
- package/src/thinking-utils.ts +61 -0
- package/src/thread-message-queue.e2e.test.ts +1219 -0
- package/src/tools.ts +430 -0
- package/src/undici.d.ts +12 -0
- package/src/undo-redo.e2e.test.ts +209 -0
- package/src/unnest-code-blocks.test.ts +713 -0
- package/src/unnest-code-blocks.ts +185 -0
- package/src/upgrade.ts +127 -0
- package/src/utils.ts +212 -0
- package/src/voice-attachment.ts +51 -0
- package/src/voice-handler.ts +908 -0
- package/src/voice-message.e2e.test.ts +1255 -0
- package/src/voice.test.ts +281 -0
- package/src/voice.ts +627 -0
- package/src/wait-session.ts +147 -0
- package/src/websockify.ts +101 -0
- package/src/worker-types.ts +64 -0
- package/src/worktree-lifecycle.e2e.test.ts +391 -0
- package/src/worktree-utils.ts +4 -0
- package/src/worktrees.test.ts +223 -0
- package/src/worktrees.ts +1294 -0
- package/src/xml.test.ts +38 -0
- package/src/xml.ts +121 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
// Shared e2e test utilities for session cleanup, server cleanup, and
|
|
2
|
+
// Discord message polling helpers.
|
|
3
|
+
// Uses directory + start timestamp double-filter to ensure we only
|
|
4
|
+
// delete sessions created by this specific test run, never real user sessions.
|
|
5
|
+
//
|
|
6
|
+
// Prefers using the existing opencode client (already running server) to avoid
|
|
7
|
+
// spawning a new server process during teardown. Falls back to initializing
|
|
8
|
+
// a new server only if no existing client is available.
|
|
9
|
+
|
|
10
|
+
import { execSync } from 'node:child_process'
|
|
11
|
+
import fs from 'node:fs'
|
|
12
|
+
import path from 'node:path'
|
|
13
|
+
import type { APIMessage } from 'discord.js'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Deterministic port from a string key (channel ID, test file name, etc.).
|
|
17
|
+
* Uses a hash to pick a stable port in range 53000-54999, avoiding overlap
|
|
18
|
+
* with queue-advanced tests (51000-52999) and getLockPort (30000-39999).
|
|
19
|
+
* Replaces the old TOCTOU-prone pattern of binding port 0, reading the
|
|
20
|
+
* assigned port, closing, then rebinding — which races under parallel vitest.
|
|
21
|
+
*/
|
|
22
|
+
export function chooseLockPort({ key }: { key: string }): number {
|
|
23
|
+
let hash = 0
|
|
24
|
+
for (let i = 0; i < key.length; i++) {
|
|
25
|
+
const char = key.charCodeAt(i)
|
|
26
|
+
hash = (hash << 5) - hash + char
|
|
27
|
+
hash |= 0
|
|
28
|
+
}
|
|
29
|
+
return 53_000 + (Math.abs(hash) % 2_000)
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Initialize a git repo with a `main` branch and empty initial commit.
|
|
33
|
+
* E2e tests create project directories under tmp/ which inherit the parent
|
|
34
|
+
* repo's git state. On CI (detached HEAD), `git symbolic-ref --short HEAD`
|
|
35
|
+
* returns empty, breaking footer snapshots that expect a branch name.
|
|
36
|
+
* Calling this in each test project directory gives it its own repo on `main`.
|
|
37
|
+
*/
|
|
38
|
+
export function initTestGitRepo(directory: string): void {
|
|
39
|
+
const isRepo = fs.existsSync(path.join(directory, '.git'))
|
|
40
|
+
if (isRepo) {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
execSync('git init -b main', { cwd: directory, stdio: 'pipe' })
|
|
44
|
+
execSync('git config user.email "test@test.com"', { cwd: directory, stdio: 'pipe' })
|
|
45
|
+
execSync('git config user.name "Test"', { cwd: directory, stdio: 'pipe' })
|
|
46
|
+
execSync('git commit --allow-empty -m "init"', { cwd: directory, stdio: 'pipe' })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
import type { DigitalDiscord } from 'discord-digital-twin/src'
|
|
50
|
+
import {
|
|
51
|
+
getOpencodeClient,
|
|
52
|
+
initializeOpencodeForDirectory,
|
|
53
|
+
} from './opencode.js'
|
|
54
|
+
import {
|
|
55
|
+
getThreadState,
|
|
56
|
+
type ThreadRunState,
|
|
57
|
+
} from './session-handler/thread-runtime-state.js'
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Delete all opencode sessions created during a test run.
|
|
61
|
+
* Uses directory + start timestamp to scope strictly to test sessions.
|
|
62
|
+
* Prefers the existing in-memory client to avoid spawning a new server in teardown.
|
|
63
|
+
* Errors are caught silently — cleanup should never fail tests.
|
|
64
|
+
*/
|
|
65
|
+
export async function cleanupTestSessions({
|
|
66
|
+
projectDirectory,
|
|
67
|
+
testStartTime,
|
|
68
|
+
}: {
|
|
69
|
+
projectDirectory: string
|
|
70
|
+
testStartTime: number
|
|
71
|
+
}) {
|
|
72
|
+
// Prefer existing client to avoid spawning a new server during teardown
|
|
73
|
+
const existingClient = getOpencodeClient(projectDirectory)
|
|
74
|
+
const client = existingClient || await (async () => {
|
|
75
|
+
const getClient = await initializeOpencodeForDirectory(projectDirectory).catch(() => {
|
|
76
|
+
return null
|
|
77
|
+
})
|
|
78
|
+
if (!getClient || getClient instanceof Error) return null
|
|
79
|
+
return getClient()
|
|
80
|
+
})()
|
|
81
|
+
if (!client) return
|
|
82
|
+
|
|
83
|
+
const listResult = await client.session.list({
|
|
84
|
+
directory: projectDirectory,
|
|
85
|
+
start: testStartTime,
|
|
86
|
+
limit: 1000,
|
|
87
|
+
}).catch(() => {
|
|
88
|
+
return null
|
|
89
|
+
})
|
|
90
|
+
const sessions = listResult?.data ?? []
|
|
91
|
+
await Promise.all(
|
|
92
|
+
sessions.map((s) => {
|
|
93
|
+
return client.session.delete({
|
|
94
|
+
sessionID: s.id,
|
|
95
|
+
directory: projectDirectory,
|
|
96
|
+
}).catch(() => {
|
|
97
|
+
return
|
|
98
|
+
})
|
|
99
|
+
}),
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ── Discord message polling helpers ──────────────────────────────
|
|
104
|
+
// Used by e2e tests to wait for bot responses. All poll at 100ms
|
|
105
|
+
// intervals with configurable timeouts.
|
|
106
|
+
|
|
107
|
+
/** Poll getMessages until we see at least `count` bot messages. */
|
|
108
|
+
export async function waitForBotMessageCount({
|
|
109
|
+
discord,
|
|
110
|
+
threadId,
|
|
111
|
+
count,
|
|
112
|
+
timeout,
|
|
113
|
+
}: {
|
|
114
|
+
discord: DigitalDiscord
|
|
115
|
+
threadId: string
|
|
116
|
+
count: number
|
|
117
|
+
timeout: number
|
|
118
|
+
}): Promise<APIMessage[]> {
|
|
119
|
+
const start = Date.now()
|
|
120
|
+
while (Date.now() - start < timeout) {
|
|
121
|
+
const messages = await discord.thread(threadId).getMessages()
|
|
122
|
+
const botMessages = messages.filter((m) => {
|
|
123
|
+
return m.author.id === discord.botUserId
|
|
124
|
+
})
|
|
125
|
+
if (botMessages.length >= count) {
|
|
126
|
+
return messages
|
|
127
|
+
}
|
|
128
|
+
await new Promise((r) => {
|
|
129
|
+
setTimeout(r, 100)
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Timed out waiting for ${count} bot messages in thread ${threadId}`,
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Poll until a bot message appears after a user message containing the given text.
|
|
139
|
+
* Content-aware: finds the user message by content, then checks for a bot reply after it.
|
|
140
|
+
*/
|
|
141
|
+
export async function waitForBotReplyAfterUserMessage({
|
|
142
|
+
discord,
|
|
143
|
+
threadId,
|
|
144
|
+
userId,
|
|
145
|
+
userMessageIncludes,
|
|
146
|
+
timeout,
|
|
147
|
+
}: {
|
|
148
|
+
discord: DigitalDiscord
|
|
149
|
+
threadId: string
|
|
150
|
+
userId: string
|
|
151
|
+
userMessageIncludes: string
|
|
152
|
+
timeout: number
|
|
153
|
+
}): Promise<APIMessage[]> {
|
|
154
|
+
const start = Date.now()
|
|
155
|
+
while (Date.now() - start < timeout) {
|
|
156
|
+
const messages = await discord.thread(threadId).getMessages()
|
|
157
|
+
const userMessageIndex = messages.findIndex((message) => {
|
|
158
|
+
return (
|
|
159
|
+
message.author.id === userId &&
|
|
160
|
+
message.content.includes(userMessageIncludes)
|
|
161
|
+
)
|
|
162
|
+
})
|
|
163
|
+
const botReplyIndex = messages.findIndex((message, index) => {
|
|
164
|
+
return index > userMessageIndex && message.author.id === discord.botUserId
|
|
165
|
+
})
|
|
166
|
+
if (userMessageIndex >= 0 && botReplyIndex >= 0) {
|
|
167
|
+
return messages
|
|
168
|
+
}
|
|
169
|
+
await new Promise((resolve) => {
|
|
170
|
+
setTimeout(resolve, 100)
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
throw new Error(
|
|
174
|
+
`Timed out waiting for bot reply after user message containing "${userMessageIncludes}" in thread ${threadId}`,
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Poll until a bot message containing specific text appears.
|
|
180
|
+
* Optionally scoped to appear after a specific user message.
|
|
181
|
+
*/
|
|
182
|
+
export async function waitForBotMessageContaining({
|
|
183
|
+
discord,
|
|
184
|
+
threadId,
|
|
185
|
+
userId,
|
|
186
|
+
text,
|
|
187
|
+
afterUserMessageIncludes,
|
|
188
|
+
afterMessageId,
|
|
189
|
+
timeout,
|
|
190
|
+
}: {
|
|
191
|
+
discord: DigitalDiscord
|
|
192
|
+
threadId: string
|
|
193
|
+
userId?: string
|
|
194
|
+
text: string
|
|
195
|
+
afterUserMessageIncludes?: string
|
|
196
|
+
afterMessageId?: string
|
|
197
|
+
timeout: number
|
|
198
|
+
}): Promise<APIMessage[]> {
|
|
199
|
+
const start = Date.now()
|
|
200
|
+
let lastMessages: APIMessage[] = []
|
|
201
|
+
while (Date.now() - start < timeout) {
|
|
202
|
+
const messages = await discord.thread(threadId).getMessages()
|
|
203
|
+
lastMessages = messages
|
|
204
|
+
const afterIndex = (() => {
|
|
205
|
+
if (afterMessageId) {
|
|
206
|
+
return messages.findLastIndex((message) => {
|
|
207
|
+
return message.id === afterMessageId
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
if (afterUserMessageIncludes && userId) {
|
|
211
|
+
return messages.findLastIndex((message) => {
|
|
212
|
+
return (
|
|
213
|
+
message.author.id === userId &&
|
|
214
|
+
message.content.includes(afterUserMessageIncludes)
|
|
215
|
+
)
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
return -1
|
|
219
|
+
})()
|
|
220
|
+
// If the anchor user message hasn't appeared yet, skip this iteration
|
|
221
|
+
// to avoid false-positives from old bot messages matching `text`.
|
|
222
|
+
if ((afterUserMessageIncludes || afterMessageId) && afterIndex === -1) {
|
|
223
|
+
await new Promise((resolve) => {
|
|
224
|
+
setTimeout(resolve, 100)
|
|
225
|
+
})
|
|
226
|
+
continue
|
|
227
|
+
}
|
|
228
|
+
const match = messages.find((message, index) => {
|
|
229
|
+
if ((afterUserMessageIncludes || afterMessageId) && afterIndex >= 0 && index <= afterIndex) {
|
|
230
|
+
return false
|
|
231
|
+
}
|
|
232
|
+
return (
|
|
233
|
+
message.author.id === discord.botUserId &&
|
|
234
|
+
message.content.includes(text)
|
|
235
|
+
)
|
|
236
|
+
})
|
|
237
|
+
if (match) {
|
|
238
|
+
return messages
|
|
239
|
+
}
|
|
240
|
+
await new Promise((resolve) => {
|
|
241
|
+
setTimeout(resolve, 100)
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
const recent = lastMessages
|
|
245
|
+
.slice(-12)
|
|
246
|
+
.map((message) => {
|
|
247
|
+
const role = message.author.id === discord.botUserId ? 'bot' : 'user'
|
|
248
|
+
return `${role}: ${message.content.slice(0, 120)}`
|
|
249
|
+
})
|
|
250
|
+
.join('\n')
|
|
251
|
+
throw new Error(
|
|
252
|
+
`Timed out waiting for bot message containing "${text}" in thread ${threadId}. Recent messages:\n${recent}`,
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Poll until a specific message id appears in thread history. */
|
|
257
|
+
export async function waitForMessageById({
|
|
258
|
+
discord,
|
|
259
|
+
threadId,
|
|
260
|
+
messageId,
|
|
261
|
+
timeout,
|
|
262
|
+
}: {
|
|
263
|
+
discord: DigitalDiscord
|
|
264
|
+
threadId: string
|
|
265
|
+
messageId: string
|
|
266
|
+
timeout: number
|
|
267
|
+
}): Promise<APIMessage> {
|
|
268
|
+
const start = Date.now()
|
|
269
|
+
while (Date.now() - start < timeout) {
|
|
270
|
+
const messages = await discord.thread(threadId).getMessages()
|
|
271
|
+
const message = messages.find((candidate) => {
|
|
272
|
+
return candidate.id === messageId
|
|
273
|
+
})
|
|
274
|
+
if (message) {
|
|
275
|
+
return message
|
|
276
|
+
}
|
|
277
|
+
await new Promise((resolve) => {
|
|
278
|
+
setTimeout(resolve, 100)
|
|
279
|
+
})
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
throw new Error(
|
|
283
|
+
`Timed out waiting for message ${messageId} in thread ${threadId}`,
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function isFooterMessage({
|
|
288
|
+
message,
|
|
289
|
+
botUserId,
|
|
290
|
+
}: {
|
|
291
|
+
message: APIMessage
|
|
292
|
+
botUserId: string
|
|
293
|
+
}): boolean {
|
|
294
|
+
if (message.author.id !== botUserId) {
|
|
295
|
+
return false
|
|
296
|
+
}
|
|
297
|
+
if (!message.content.startsWith('*')) {
|
|
298
|
+
return false
|
|
299
|
+
}
|
|
300
|
+
return message.content.includes('⋅')
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Poll until a footer message appears, optionally after an anchor message.
|
|
305
|
+
* Useful for stabilizing snapshots by waiting for run completion metadata.
|
|
306
|
+
*/
|
|
307
|
+
export async function waitForFooterMessage({
|
|
308
|
+
discord,
|
|
309
|
+
threadId,
|
|
310
|
+
timeout,
|
|
311
|
+
afterMessageIncludes,
|
|
312
|
+
afterAuthorId,
|
|
313
|
+
}: {
|
|
314
|
+
discord: DigitalDiscord
|
|
315
|
+
threadId: string
|
|
316
|
+
timeout: number
|
|
317
|
+
afterMessageIncludes?: string
|
|
318
|
+
afterAuthorId?: string
|
|
319
|
+
}): Promise<APIMessage[]> {
|
|
320
|
+
const start = Date.now()
|
|
321
|
+
let lastMessages: APIMessage[] = []
|
|
322
|
+
while (Date.now() - start < timeout) {
|
|
323
|
+
const messages = await discord.thread(threadId).getMessages()
|
|
324
|
+
lastMessages = messages
|
|
325
|
+
const afterIndex = afterMessageIncludes
|
|
326
|
+
? messages.findLastIndex((message) => {
|
|
327
|
+
if (!message.content.includes(afterMessageIncludes)) {
|
|
328
|
+
return false
|
|
329
|
+
}
|
|
330
|
+
if (!afterAuthorId) {
|
|
331
|
+
return true
|
|
332
|
+
}
|
|
333
|
+
return message.author.id === afterAuthorId
|
|
334
|
+
})
|
|
335
|
+
: -1
|
|
336
|
+
if (afterMessageIncludes && afterIndex === -1) {
|
|
337
|
+
await new Promise((resolve) => {
|
|
338
|
+
setTimeout(resolve, 100)
|
|
339
|
+
})
|
|
340
|
+
continue
|
|
341
|
+
}
|
|
342
|
+
const footer = messages.find((message, index) => {
|
|
343
|
+
if (afterIndex >= 0 && index <= afterIndex) {
|
|
344
|
+
return false
|
|
345
|
+
}
|
|
346
|
+
return isFooterMessage({ message, botUserId: discord.botUserId })
|
|
347
|
+
})
|
|
348
|
+
if (footer) {
|
|
349
|
+
return messages
|
|
350
|
+
}
|
|
351
|
+
await new Promise((resolve) => {
|
|
352
|
+
setTimeout(resolve, 100)
|
|
353
|
+
})
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const recent = lastMessages
|
|
357
|
+
.slice(-12)
|
|
358
|
+
.map((message) => {
|
|
359
|
+
const role = message.author.id === discord.botUserId ? 'bot' : 'user'
|
|
360
|
+
return `${role}: ${message.content.slice(0, 120)}`
|
|
361
|
+
})
|
|
362
|
+
.join('\n')
|
|
363
|
+
const anchorText = afterMessageIncludes || 'start'
|
|
364
|
+
throw new Error(
|
|
365
|
+
`Timed out waiting for footer after "${anchorText}" in thread ${threadId}. Recent messages:\n${recent}`,
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ── Thread state polling helpers ─────────────────────────────────
|
|
370
|
+
// Used by e2e tests to assert on queue and session-state snapshots.
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Poll until thread has at least `count` items in its queue.
|
|
374
|
+
*/
|
|
375
|
+
export async function waitForThreadQueueLength({
|
|
376
|
+
threadId,
|
|
377
|
+
count,
|
|
378
|
+
timeout,
|
|
379
|
+
}: {
|
|
380
|
+
threadId: string
|
|
381
|
+
count: number
|
|
382
|
+
timeout: number
|
|
383
|
+
}): Promise<ThreadRunState> {
|
|
384
|
+
const start = Date.now()
|
|
385
|
+
while (Date.now() - start < timeout) {
|
|
386
|
+
const state = getThreadState(threadId)
|
|
387
|
+
if (state && state.queueItems.length >= count) {
|
|
388
|
+
return state
|
|
389
|
+
}
|
|
390
|
+
await new Promise((resolve) => {
|
|
391
|
+
setTimeout(resolve, 50)
|
|
392
|
+
})
|
|
393
|
+
}
|
|
394
|
+
const finalState = getThreadState(threadId)
|
|
395
|
+
const currentLength = finalState?.queueItems.length ?? 0
|
|
396
|
+
throw new Error(
|
|
397
|
+
`Timed out waiting for thread ${threadId} queue length >= ${count}. Current length: ${currentLength}`,
|
|
398
|
+
)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Poll until a custom predicate on ThreadRunState returns true.
|
|
403
|
+
* Use this for compound assertions against thread state snapshots.
|
|
404
|
+
*/
|
|
405
|
+
export async function waitForThreadState({
|
|
406
|
+
threadId,
|
|
407
|
+
predicate,
|
|
408
|
+
timeout,
|
|
409
|
+
description,
|
|
410
|
+
}: {
|
|
411
|
+
threadId: string
|
|
412
|
+
predicate: (state: ThreadRunState) => boolean
|
|
413
|
+
timeout: number
|
|
414
|
+
/** Human-readable description for timeout error messages */
|
|
415
|
+
description?: string
|
|
416
|
+
}): Promise<ThreadRunState> {
|
|
417
|
+
const start = Date.now()
|
|
418
|
+
while (Date.now() - start < timeout) {
|
|
419
|
+
const state = getThreadState(threadId)
|
|
420
|
+
if (state && predicate(state)) {
|
|
421
|
+
return state
|
|
422
|
+
}
|
|
423
|
+
await new Promise((resolve) => {
|
|
424
|
+
setTimeout(resolve, 50)
|
|
425
|
+
})
|
|
426
|
+
}
|
|
427
|
+
const finalState = getThreadState(threadId)
|
|
428
|
+
const desc = description ?? 'custom predicate'
|
|
429
|
+
const queueLen = finalState?.queueItems.length ?? 0
|
|
430
|
+
const sessionId = finalState?.sessionId ?? 'none'
|
|
431
|
+
throw new Error(
|
|
432
|
+
`Timed out waiting for thread ${threadId} (${desc}). ` +
|
|
433
|
+
`Current: queue=${queueLen}, sessionId=${sessionId}`,
|
|
434
|
+
)
|
|
435
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Utilities for extracting and matching model variant (thinking level) values
|
|
2
|
+
// from the provider.list() API response. Used by model selector and session handler
|
|
3
|
+
// to validate variant preferences against what the current model actually supports.
|
|
4
|
+
|
|
5
|
+
export type ThinkingProvider = {
|
|
6
|
+
id: string
|
|
7
|
+
models?: Record<string, unknown>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function getModelVariants(model: unknown): Record<string, unknown> | undefined {
|
|
11
|
+
if (!model || typeof model !== 'object') {
|
|
12
|
+
return undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const variants = (model as { variants?: unknown }).variants
|
|
16
|
+
if (!variants || typeof variants !== 'object') {
|
|
17
|
+
return undefined
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return variants as Record<string, unknown>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getThinkingValuesForModel({
|
|
24
|
+
providers,
|
|
25
|
+
providerId,
|
|
26
|
+
modelId,
|
|
27
|
+
}: {
|
|
28
|
+
providers: ThinkingProvider[]
|
|
29
|
+
providerId: string
|
|
30
|
+
modelId: string
|
|
31
|
+
}): string[] {
|
|
32
|
+
const provider = providers.find((candidateProvider) => {
|
|
33
|
+
return candidateProvider.id === providerId
|
|
34
|
+
})
|
|
35
|
+
const model = provider?.models?.[modelId]
|
|
36
|
+
const variants = getModelVariants(model)
|
|
37
|
+
if (!variants) {
|
|
38
|
+
return []
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return Object.keys(variants).filter((variant) => {
|
|
42
|
+
return variant.trim().length > 0
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function matchThinkingValue({
|
|
47
|
+
requestedValue,
|
|
48
|
+
availableValues,
|
|
49
|
+
}: {
|
|
50
|
+
requestedValue: string
|
|
51
|
+
availableValues: string[]
|
|
52
|
+
}): string | undefined {
|
|
53
|
+
const normalizedRequestedValue = requestedValue.trim().toLowerCase()
|
|
54
|
+
if (!normalizedRequestedValue) {
|
|
55
|
+
return undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return availableValues.find((availableValue) => {
|
|
59
|
+
return availableValue.toLowerCase() === normalizedRequestedValue
|
|
60
|
+
})
|
|
61
|
+
}
|