@otto-assistant/otto 0.1.2 → 0.7.16
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-account-identity.js +62 -0
- package/dist/anthropic-account-identity.test.js +38 -0
- package/dist/anthropic-auth-plugin.js +917 -0
- package/dist/anthropic-auth-state.js +303 -0
- package/dist/anthropic-auth-state.test.js +150 -0
- package/dist/bin.js +152 -0
- package/dist/btw-prefix-detection.js +17 -0
- package/dist/btw-prefix-detection.test.js +63 -0
- package/dist/channel-management.js +259 -0
- package/dist/cli-parsing.test.js +142 -0
- package/dist/cli-send-thread.e2e.test.js +353 -0
- package/dist/cli-telegram-options.test.js +99 -0
- package/dist/cli.js +4210 -568
- package/dist/commands/abort.js +65 -0
- package/dist/commands/action-buttons.js +245 -0
- package/dist/commands/add-dir.js +124 -0
- package/dist/commands/add-dir.test.js +126 -0
- package/dist/commands/add-project.js +113 -0
- package/dist/commands/agent.js +355 -0
- package/dist/commands/ask-question.js +320 -0
- package/dist/commands/ask-question.test.js +92 -0
- package/dist/commands/btw.js +121 -0
- package/dist/commands/cli-commands-group-a.test.js +728 -0
- package/dist/commands/cli-commands-group-b.test.js +695 -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/discord-commands-group-a.test.js +655 -0
- package/dist/commands/discord-commands-group-b.test.js +595 -0
- package/dist/commands/discord-commands-group-c.test.js +739 -0
- package/dist/commands/file-upload.js +275 -0
- package/dist/commands/fork-subagent.js +177 -0
- package/dist/commands/fork.js +262 -0
- package/dist/commands/gemini-apikey.js +70 -0
- package/dist/commands/login.js +893 -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 +162 -0
- package/dist/commands/model-variant.js +369 -0
- package/dist/commands/model.js +798 -0
- package/dist/commands/new-worktree.js +465 -0
- package/dist/commands/paginated-select.js +57 -0
- package/dist/commands/permissions.js +274 -0
- package/dist/commands/queue.js +223 -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/thread-deletion-sync.js +50 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/undo-redo.js +305 -0
- package/dist/commands/unset-model.js +139 -0
- package/dist/commands/upgrade.js +48 -0
- package/dist/commands/user-command.js +155 -0
- package/dist/commands/verbosity.js +125 -0
- package/dist/commands/vscode.js +269 -0
- package/dist/commands/worktree-settings.js +43 -0
- package/dist/commands/worktrees.js +468 -0
- package/dist/condense-memory.js +33 -0
- package/dist/config.js +100 -255
- package/dist/context-awareness-plugin.js +340 -0
- package/dist/context-awareness-plugin.test.js +126 -0
- package/dist/critique-utils.js +95 -0
- package/dist/database.js +1355 -0
- package/dist/db.js +260 -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 +1124 -0
- package/dist/discord-command-registration.js +567 -0
- package/dist/discord-urls.js +82 -0
- package/dist/discord-utils.js +616 -0
- package/dist/discord-utils.test.js +134 -0
- package/dist/errors.js +179 -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 +491 -0
- package/dist/format-tables.test.js +478 -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 +485 -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 +58 -0
- package/dist/generated/internal/class.js +49 -0
- package/dist/generated/internal/prismaNamespace.js +254 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js +224 -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 +251 -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 +420 -0
- package/dist/ipc-polling.js +327 -0
- package/dist/ipc-tools-plugin.js +193 -0
- package/dist/ipc-utils.js +18 -0
- package/dist/limit-heading-depth.js +25 -0
- package/dist/limit-heading-depth.test.js +105 -0
- package/dist/logger.js +171 -0
- package/dist/markdown.js +342 -0
- package/dist/markdown.test.js +264 -0
- package/dist/memory-overview-plugin.js +128 -0
- package/dist/message-finish-field.e2e.test.js +168 -0
- package/dist/message-formatting.js +415 -0
- package/dist/message-formatting.test.js +115 -0
- package/dist/message-preprocessing.js +359 -0
- package/dist/onboarding-tutorial.js +163 -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 +131 -0
- package/dist/opencode-command.test.js +48 -0
- package/dist/opencode-interrupt-plugin.js +388 -0
- package/dist/opencode-interrupt-plugin.test.js +463 -0
- package/dist/opencode.js +1124 -0
- package/dist/otto/branding.js +22 -0
- package/dist/otto/index.js +21 -0
- package/dist/otto-digital-twin.e2e.test.js +161 -0
- package/dist/otto-opencode-plugin-loading.e2e.test.js +94 -0
- package/dist/otto-opencode-plugin.js +21 -0
- package/dist/otto-opencode-plugin.test.js +98 -0
- package/dist/parse-permission-rules.test.js +117 -0
- package/dist/patch-text-parser.js +97 -0
- package/dist/plugin-logger.js +68 -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 +790 -0
- package/dist/queue-advanced-footer.e2e.test.js +481 -0
- package/dist/queue-advanced-model-switch.e2e.test.js +299 -0
- package/dist/queue-advanced-permissions-typing.e2e.test.js +179 -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 +256 -0
- package/dist/runtime-idle-sweeper.js +52 -0
- package/dist/runtime-lifecycle.e2e.test.js +514 -0
- package/dist/sentry.js +23 -0
- package/dist/session-handler/agent-utils.js +67 -0
- package/dist/session-handler/event-stream-state.js +475 -0
- package/dist/session-handler/event-stream-state.test.js +632 -0
- package/dist/session-handler/model-utils.js +147 -0
- package/dist/session-handler/opencode-session-event-log.js +94 -0
- package/dist/session-handler/thread-runtime-state.js +131 -0
- package/dist/session-handler/thread-session-runtime.js +3390 -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 +92 -0
- package/dist/skill-filter.js +31 -0
- package/dist/skill-filter.test.js +65 -0
- package/dist/startup-service.js +153 -0
- package/dist/startup-time.e2e.test.js +296 -0
- package/dist/store.js +19 -0
- package/dist/subagent-rate-limit-plugin.js +175 -0
- package/dist/system-message.js +702 -0
- package/dist/system-message.test.js +697 -0
- package/dist/task-runner.js +530 -0
- package/dist/task-schedule.js +213 -0
- package/dist/task-schedule.test.js +71 -0
- package/dist/test-utils.js +313 -0
- package/dist/thinking-utils.js +35 -0
- package/dist/thread-message-queue.e2e.test.js +1111 -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 +156 -0
- package/dist/utils.js +172 -0
- package/dist/utils.test.js +130 -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 +456 -0
- package/dist/voice.test.js +235 -0
- package/dist/wait-session.js +171 -0
- package/dist/websockify.js +69 -0
- package/dist/worker-types.js +4 -0
- package/dist/worktree-lifecycle.e2e.test.js +311 -0
- package/dist/worktree-utils.js +3 -0
- package/dist/worktrees.js +991 -0
- package/dist/worktrees.test.js +415 -0
- package/dist/xml.js +92 -0
- package/dist/xml.test.js +32 -0
- package/package.json +90 -38
- package/schema.prisma +303 -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/goke/SKILL.md +38 -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/manual-kimaki-upstream-adapt/SKILL.md +114 -0
- package/skills/new-skill/SKILL.md +237 -0
- package/skills/npm-package/SKILL.md +617 -0
- package/skills/opensrc/SKILL.md +78 -0
- package/skills/otto-publish/SKILL.md +61 -0
- package/skills/playwriter/SKILL.md +35 -0
- package/skills/profano/SKILL.md +16 -0
- package/skills/proxyman/SKILL.md +215 -0
- package/skills/security-review/SKILL.md +208 -0
- package/skills/sigillo/SKILL.md +101 -0
- package/skills/simplify/SKILL.md +58 -0
- package/skills/spiceflow/SKILL.md +28 -0
- package/skills/termcast/SKILL.md +945 -0
- package/skills/tuistory/SKILL.md +98 -0
- package/skills/usecomputer/SKILL.md +264 -0
- package/skills/x-articles/SKILL.md +554 -0
- package/skills/zele/SKILL.md +49 -0
- package/skills/zustand-centralized-state/SKILL.md +1004 -0
- package/src/agent-model.e2e.test.ts +979 -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-account-identity.test.ts +52 -0
- package/src/anthropic-account-identity.ts +77 -0
- package/src/anthropic-auth-plugin.ts +1139 -0
- package/src/anthropic-auth-state.test.ts +187 -0
- package/src/anthropic-auth-state.ts +386 -0
- package/src/bin.ts +182 -0
- package/src/btw-prefix-detection.test.ts +73 -0
- package/src/btw-prefix-detection.ts +23 -0
- package/src/channel-management.ts +376 -0
- package/src/cli-parsing.test.ts +197 -0
- package/src/cli-send-thread.e2e.test.ts +463 -0
- package/src/cli-telegram-options.test.ts +114 -0
- package/src/cli.ts +5718 -580
- package/src/commands/abort.ts +89 -0
- package/src/commands/action-buttons.ts +364 -0
- package/src/commands/add-dir.test.ts +154 -0
- package/src/commands/add-dir.ts +175 -0
- package/src/commands/add-project.ts +149 -0
- package/src/commands/agent.ts +496 -0
- package/src/commands/ask-question.test.ts +111 -0
- package/src/commands/ask-question.ts +455 -0
- package/src/commands/btw.ts +184 -0
- package/src/commands/cli-commands-group-a.test.ts +837 -0
- package/src/commands/cli-commands-group-b.test.ts +800 -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/discord-commands-group-a.test.ts +789 -0
- package/src/commands/discord-commands-group-b.test.ts +648 -0
- package/src/commands/discord-commands-group-c.test.ts +882 -0
- package/src/commands/file-upload.ts +389 -0
- package/src/commands/fork-subagent.ts +263 -0
- package/src/commands/fork.ts +386 -0
- package/src/commands/gemini-apikey.ts +104 -0
- package/src/commands/login.ts +1181 -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 +226 -0
- package/src/commands/model-variant.ts +488 -0
- package/src/commands/model.ts +1082 -0
- package/src/commands/new-worktree.ts +645 -0
- package/src/commands/paginated-select.ts +81 -0
- package/src/commands/permissions.ts +397 -0
- package/src/commands/queue.ts +293 -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/thread-deletion-sync.ts +80 -0
- package/src/commands/types.ts +25 -0
- package/src/commands/undo-redo.ts +386 -0
- package/src/commands/unset-model.ts +174 -0
- package/src/commands/upgrade.ts +59 -0
- package/src/commands/user-command.ts +198 -0
- package/src/commands/verbosity.ts +173 -0
- package/src/commands/vscode.ts +342 -0
- package/src/commands/worktree-settings.ts +70 -0
- package/src/commands/worktrees.ts +645 -0
- package/src/condense-memory.ts +36 -0
- package/src/config.ts +103 -339
- package/src/context-awareness-plugin.test.ts +144 -0
- package/src/context-awareness-plugin.ts +469 -0
- package/src/critique-utils.ts +139 -0
- package/src/database.ts +1949 -0
- package/src/db.test.ts +162 -0
- package/src/db.ts +295 -0
- package/src/debounce-timeout.ts +43 -0
- package/src/debounced-process-flush.ts +104 -0
- package/src/discord-bot.ts +1507 -0
- package/src/discord-command-registration.ts +752 -0
- package/src/discord-urls.ts +89 -0
- package/src/discord-utils.test.ts +153 -0
- package/src/discord-utils.ts +846 -0
- package/src/errors.ts +232 -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 +515 -0
- package/src/format-tables.ts +718 -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 +644 -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 +770 -0
- package/src/generated/enums.ts +98 -0
- package/src/generated/internal/class.ts +384 -0
- package/src/generated/internal/prismaNamespace.ts +2394 -0
- package/src/generated/internal/prismaNamespaceBrowser.ts +327 -0
- package/src/generated/models/bot_api_keys.ts +1288 -0
- package/src/generated/models/bot_tokens.ts +1700 -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 +299 -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 +610 -0
- package/src/ipc-polling.ts +427 -0
- package/src/ipc-tools-plugin.ts +236 -0
- package/src/ipc-utils.ts +29 -0
- package/src/limit-heading-depth.test.ts +116 -0
- package/src/limit-heading-depth.ts +26 -0
- package/src/logger.ts +215 -0
- package/src/markdown.test.ts +315 -0
- package/src/markdown.ts +410 -0
- package/src/memory-overview-plugin.ts +163 -0
- package/src/message-finish-field.e2e.test.ts +195 -0
- package/src/message-formatting.test.ts +126 -0
- package/src/message-formatting.ts +535 -0
- package/src/message-preprocessing.ts +488 -0
- package/src/onboarding-tutorial.ts +167 -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 +191 -0
- package/src/opencode-interrupt-plugin.test.ts +682 -0
- package/src/opencode-interrupt-plugin.ts +507 -0
- package/src/opencode.ts +1462 -0
- package/src/otto/branding.ts +23 -0
- package/src/otto/index.ts +22 -0
- package/src/otto-digital-twin.e2e.test.ts +199 -0
- package/src/otto-opencode-plugin-loading.e2e.test.ts +117 -0
- package/src/otto-opencode-plugin.test.ts +108 -0
- package/src/otto-opencode-plugin.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 +84 -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 +877 -0
- package/src/queue-advanced-footer.e2e.test.ts +591 -0
- package/src/queue-advanced-model-switch.e2e.test.ts +383 -0
- package/src/queue-advanced-permissions-typing.e2e.test.ts +246 -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 +327 -0
- package/src/runtime-idle-sweeper.ts +76 -0
- package/src/runtime-lifecycle.e2e.test.ts +651 -0
- package/src/schema.sql +174 -0
- package/src/sentry.ts +26 -0
- package/src/session-handler/agent-utils.ts +99 -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 +717 -0
- package/src/session-handler/event-stream-state.ts +706 -0
- package/src/session-handler/model-utils.ts +217 -0
- package/src/session-handler/opencode-session-event-log.ts +130 -0
- package/src/session-handler/thread-runtime-state.ts +247 -0
- package/src/session-handler/thread-session-runtime.ts +4440 -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 +130 -0
- package/src/skill-filter.test.ts +83 -0
- package/src/skill-filter.ts +42 -0
- package/src/startup-service.ts +200 -0
- package/src/startup-time.e2e.test.ts +373 -0
- package/src/store.ts +139 -0
- package/src/subagent-rate-limit-plugin.ts +218 -0
- package/src/system-message.test.ts +710 -0
- package/src/system-message.ts +814 -0
- package/src/task-runner.ts +725 -0
- package/src/task-schedule.test.ts +84 -0
- package/src/task-schedule.ts +317 -0
- package/src/test-utils.ts +451 -0
- package/src/thinking-utils.ts +61 -0
- package/src/thread-message-queue.e2e.test.ts +1350 -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 +185 -0
- package/src/utils.test.ts +155 -0
- package/src/utils.ts +265 -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 +638 -0
- package/src/wait-session.ts +273 -0
- package/src/websockify.ts +101 -0
- package/src/worker-types.ts +64 -0
- package/src/worktree-lifecycle.e2e.test.ts +396 -0
- package/src/worktree-utils.ts +4 -0
- package/src/worktrees.test.ts +489 -0
- package/src/worktrees.ts +1370 -0
- package/src/xml.test.ts +38 -0
- package/src/xml.ts +121 -0
- package/README.md +0 -142
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/config.d.ts +0 -39
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -202
- package/dist/config.test.js.map +0 -1
- package/dist/detect.d.ts +0 -9
- package/dist/detect.d.ts.map +0 -1
- package/dist/detect.js +0 -40
- package/dist/detect.js.map +0 -1
- package/dist/detect.test.d.ts +0 -2
- package/dist/detect.test.d.ts.map +0 -1
- package/dist/detect.test.js +0 -26
- package/dist/detect.test.js.map +0 -1
- package/dist/docker.d.ts +0 -7
- package/dist/docker.d.ts.map +0 -1
- package/dist/docker.js +0 -17
- package/dist/docker.js.map +0 -1
- package/dist/docker.test.d.ts +0 -2
- package/dist/docker.test.d.ts.map +0 -1
- package/dist/docker.test.js +0 -12
- package/dist/docker.test.js.map +0 -1
- package/dist/health.d.ts +0 -31
- package/dist/health.d.ts.map +0 -1
- package/dist/health.js +0 -117
- package/dist/health.js.map +0 -1
- package/dist/health.test.d.ts +0 -2
- package/dist/health.test.d.ts.map +0 -1
- package/dist/health.test.js +0 -52
- package/dist/health.test.js.map +0 -1
- package/dist/index.d.ts +0 -20
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -15
- package/dist/index.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -8
- package/dist/index.test.js.map +0 -1
- package/dist/installer.d.ts +0 -10
- package/dist/installer.d.ts.map +0 -1
- package/dist/installer.js +0 -50
- package/dist/installer.js.map +0 -1
- package/dist/installer.test.d.ts +0 -2
- package/dist/installer.test.d.ts.map +0 -1
- package/dist/installer.test.js +0 -43
- package/dist/installer.test.js.map +0 -1
- package/dist/lifecycle.d.ts +0 -10
- package/dist/lifecycle.d.ts.map +0 -1
- package/dist/lifecycle.js +0 -45
- package/dist/lifecycle.js.map +0 -1
- package/dist/lifecycle.test.d.ts +0 -2
- package/dist/lifecycle.test.d.ts.map +0 -1
- package/dist/lifecycle.test.js +0 -20
- package/dist/lifecycle.test.js.map +0 -1
- package/dist/manifest.d.ts +0 -18
- package/dist/manifest.d.ts.map +0 -1
- package/dist/manifest.js +0 -30
- package/dist/manifest.js.map +0 -1
- package/dist/skills-baseline.d.ts +0 -7
- package/dist/skills-baseline.d.ts.map +0 -1
- package/dist/skills-baseline.js +0 -9
- package/dist/skills-baseline.js.map +0 -1
- package/dist/skills.d.ts +0 -110
- package/dist/skills.d.ts.map +0 -1
- package/dist/skills.js +0 -429
- package/dist/skills.js.map +0 -1
- package/dist/skills.test.d.ts +0 -2
- package/dist/skills.test.d.ts.map +0 -1
- package/dist/skills.test.js +0 -416
- package/dist/skills.test.js.map +0 -1
- package/dist/sync.d.ts +0 -10
- package/dist/sync.d.ts.map +0 -1
- package/dist/sync.js +0 -39
- package/dist/sync.js.map +0 -1
- package/dist/tenant.d.ts +0 -13
- package/dist/tenant.d.ts.map +0 -1
- package/dist/tenant.js +0 -105
- package/dist/tenant.js.map +0 -1
- package/dist/tenant.test.d.ts +0 -2
- package/dist/tenant.test.d.ts.map +0 -1
- package/dist/tenant.test.js +0 -37
- package/dist/tenant.test.js.map +0 -1
- package/src/config.test.ts +0 -237
- package/src/detect.test.ts +0 -29
- package/src/detect.ts +0 -52
- package/src/docker.test.ts +0 -12
- package/src/docker.ts +0 -23
- package/src/health.test.ts +0 -61
- package/src/health.ts +0 -158
- package/src/index.test.ts +0 -8
- package/src/index.ts +0 -62
- package/src/installer.test.ts +0 -52
- package/src/installer.ts +0 -62
- package/src/lifecycle.test.ts +0 -23
- package/src/lifecycle.ts +0 -49
- package/src/manifest.ts +0 -42
- package/src/skills-baseline.ts +0 -14
- package/src/skills.test.ts +0 -503
- package/src/skills.ts +0 -512
- package/src/sync.ts +0 -53
- package/src/tenant.test.ts +0 -49
- package/src/tenant.ts +0 -120
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Model resolution utilities.
|
|
2
|
+
// getDefaultModel resolves the default model from OpenCode when no user preference is set.
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { xdgState } from 'xdg-basedir';
|
|
6
|
+
import * as errore from 'errore';
|
|
7
|
+
import {} from '../opencode.js';
|
|
8
|
+
import { createLogger, LogPrefix } from '../logger.js';
|
|
9
|
+
const sessionLogger = createLogger(LogPrefix.SESSION);
|
|
10
|
+
/**
|
|
11
|
+
* Read user's recent models from OpenCode TUI's state file.
|
|
12
|
+
* Uses same path as OpenCode: path.join(xdgState, "opencode", "model.json")
|
|
13
|
+
* Returns all recent models so we can iterate until finding a valid one.
|
|
14
|
+
* See: opensrc/repos/github.com/sst/opencode/packages/opencode/src/global/index.ts
|
|
15
|
+
*/
|
|
16
|
+
function getRecentModelsFromTuiState() {
|
|
17
|
+
if (!xdgState) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
// Same path as OpenCode TUI: path.join(Global.Path.state, "model.json")
|
|
21
|
+
const modelJsonPath = path.join(xdgState, 'opencode', 'model.json');
|
|
22
|
+
const result = errore.tryFn(() => {
|
|
23
|
+
const content = fs.readFileSync(modelJsonPath, 'utf-8');
|
|
24
|
+
const data = JSON.parse(content);
|
|
25
|
+
return data.recent ?? [];
|
|
26
|
+
});
|
|
27
|
+
if (result instanceof Error) {
|
|
28
|
+
// File doesn't exist or is invalid - this is normal for fresh installs
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Parse a model string in format "provider/model" into providerID and modelID.
|
|
35
|
+
*/
|
|
36
|
+
function parseModelString(model) {
|
|
37
|
+
const [providerID, ...modelParts] = model.split('/');
|
|
38
|
+
const modelID = modelParts.join('/');
|
|
39
|
+
if (!providerID || !modelID) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return { providerID, modelID };
|
|
43
|
+
}
|
|
44
|
+
function getModelFromProjectConfig({ directory, }) {
|
|
45
|
+
if (!directory) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const result = errore.tryFn(() => {
|
|
49
|
+
const configPath = path.join(directory, 'opencode.json');
|
|
50
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
51
|
+
const parsed = JSON.parse(raw);
|
|
52
|
+
if (!parsed.model) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
return parseModelString(parsed.model);
|
|
56
|
+
});
|
|
57
|
+
if (result instanceof Error) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validate that a model is available (provider connected + model exists).
|
|
64
|
+
*/
|
|
65
|
+
function isModelValid(model, connected, providers) {
|
|
66
|
+
const isConnected = connected.includes(model.providerID);
|
|
67
|
+
const provider = providers.find((p) => {
|
|
68
|
+
return p.id === model.providerID;
|
|
69
|
+
});
|
|
70
|
+
const modelExists = provider?.models && model.modelID in provider.models;
|
|
71
|
+
return isConnected && !!modelExists;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the default model from OpenCode when no user preference is set.
|
|
75
|
+
* Priority (matches OpenCode TUI behavior):
|
|
76
|
+
* 1. OpenCode config.model setting
|
|
77
|
+
* 2. User's recent models from TUI state (~/.local/state/opencode/model.json)
|
|
78
|
+
* 3. First connected provider's default model from API
|
|
79
|
+
* Returns the model and its source.
|
|
80
|
+
*/
|
|
81
|
+
export async function getDefaultModel({ getClient, directory, }) {
|
|
82
|
+
if (getClient instanceof Error) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
const configModel = getModelFromProjectConfig({ directory });
|
|
86
|
+
if (configModel) {
|
|
87
|
+
sessionLogger.log(`[MODEL] Using project config model: ${configModel.providerID}/${configModel.modelID}`);
|
|
88
|
+
return { ...configModel, source: 'opencode-config' };
|
|
89
|
+
}
|
|
90
|
+
// Fetch connected providers to validate any model we return
|
|
91
|
+
const providersResponse = await errore.tryAsync(() => {
|
|
92
|
+
return getClient().provider.list({ directory });
|
|
93
|
+
});
|
|
94
|
+
if (providersResponse instanceof Error) {
|
|
95
|
+
sessionLogger.log(`[MODEL] Failed to fetch providers for default model:`, providersResponse.message);
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
if (!providersResponse.data) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
const { connected, default: defaults, all: providers, } = providersResponse.data;
|
|
102
|
+
if (connected.length === 0) {
|
|
103
|
+
sessionLogger.log(`[MODEL] No connected providers found`);
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
// 1. Check OpenCode config.model setting (highest priority after user preference)
|
|
107
|
+
const configResponse = await errore.tryAsync(() => {
|
|
108
|
+
return getClient().config.get({ directory });
|
|
109
|
+
});
|
|
110
|
+
if (!(configResponse instanceof Error) && configResponse.data?.model) {
|
|
111
|
+
const configModel = parseModelString(configResponse.data.model);
|
|
112
|
+
if (configModel && isModelValid(configModel, connected, providers)) {
|
|
113
|
+
sessionLogger.log(`[MODEL] Using config model: ${configModel.providerID}/${configModel.modelID}`);
|
|
114
|
+
return { ...configModel, source: 'opencode-config' };
|
|
115
|
+
}
|
|
116
|
+
if (configModel) {
|
|
117
|
+
sessionLogger.log(`[MODEL] Config model ${configResponse.data.model} not available, checking recent`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// 2. Try to use user's recent models from TUI state (iterate until finding valid one)
|
|
121
|
+
const recentModels = getRecentModelsFromTuiState();
|
|
122
|
+
for (const recentModel of recentModels) {
|
|
123
|
+
if (isModelValid(recentModel, connected, providers)) {
|
|
124
|
+
sessionLogger.log(`[MODEL] Using recent TUI model: ${recentModel.providerID}/${recentModel.modelID}`);
|
|
125
|
+
return { ...recentModel, source: 'opencode-recent' };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (recentModels.length > 0) {
|
|
129
|
+
sessionLogger.log(`[MODEL] No valid recent TUI models found`);
|
|
130
|
+
}
|
|
131
|
+
// 3. Fall back to first connected provider's default model
|
|
132
|
+
const firstConnected = connected[0];
|
|
133
|
+
if (!firstConnected) {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
const defaultModelId = defaults[firstConnected];
|
|
137
|
+
if (!defaultModelId) {
|
|
138
|
+
sessionLogger.log(`[MODEL] No default model for provider ${firstConnected}`);
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
sessionLogger.log(`[MODEL] Using provider default: ${firstConnected}/${defaultModelId}`);
|
|
142
|
+
return {
|
|
143
|
+
providerID: firstConnected,
|
|
144
|
+
modelID: defaultModelId,
|
|
145
|
+
source: 'opencode-provider-default',
|
|
146
|
+
};
|
|
147
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Debug helper for writing raw OpenCode event stream entries as JSONL.
|
|
2
|
+
// When enabled, writes one file per session ID so event ordering and
|
|
3
|
+
// lifecycle behavior can be analyzed with jq.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import * as errore from 'errore';
|
|
7
|
+
import { getDataDir } from '../config.js';
|
|
8
|
+
let eventLogDirPromise = null;
|
|
9
|
+
let eventLogWriteDisabled = false;
|
|
10
|
+
export function isOpencodeSessionEventLogEnabled() {
|
|
11
|
+
return process.env['OTTO_LOG_OPENCODE_SESSION_EVENTS'] === '1';
|
|
12
|
+
}
|
|
13
|
+
export function getOpencodeEventSessionId(event) {
|
|
14
|
+
switch (event.type) {
|
|
15
|
+
case 'message.updated':
|
|
16
|
+
return event.properties.info.sessionID;
|
|
17
|
+
case 'message.part.updated':
|
|
18
|
+
return event.properties.part.sessionID;
|
|
19
|
+
case 'message.part.delta':
|
|
20
|
+
case 'message.part.removed':
|
|
21
|
+
case 'session.status':
|
|
22
|
+
case 'session.idle':
|
|
23
|
+
case 'session.diff':
|
|
24
|
+
case 'permission.asked':
|
|
25
|
+
case 'permission.replied':
|
|
26
|
+
case 'question.asked':
|
|
27
|
+
case 'question.replied':
|
|
28
|
+
case 'question.rejected':
|
|
29
|
+
return event.properties.sessionID;
|
|
30
|
+
case 'session.error':
|
|
31
|
+
return event.properties.sessionID;
|
|
32
|
+
case 'session.created':
|
|
33
|
+
case 'session.updated':
|
|
34
|
+
case 'session.deleted':
|
|
35
|
+
return event.properties.info.id;
|
|
36
|
+
default:
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function sanitizeSessionIdForFilename(sessionId) {
|
|
41
|
+
return sessionId.replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
42
|
+
}
|
|
43
|
+
async function resolveEventLogDirectory() {
|
|
44
|
+
if (!eventLogDirPromise) {
|
|
45
|
+
eventLogDirPromise = (async () => {
|
|
46
|
+
const configuredEventLogDir = process.env['OTTO_OPENCODE_SESSION_EVENTS_DIR'];
|
|
47
|
+
const baseDir = configuredEventLogDir || path.join(getDataDir(), 'opencode-session-events');
|
|
48
|
+
await fs.promises.mkdir(baseDir, { recursive: true });
|
|
49
|
+
return baseDir;
|
|
50
|
+
})();
|
|
51
|
+
}
|
|
52
|
+
return eventLogDirPromise;
|
|
53
|
+
}
|
|
54
|
+
export function buildOpencodeEventLogLine({ timestamp, threadId, projectDirectory, event, }) {
|
|
55
|
+
return {
|
|
56
|
+
timestamp,
|
|
57
|
+
threadId,
|
|
58
|
+
projectDirectory,
|
|
59
|
+
event,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export async function appendOpencodeSessionEventLog(entry) {
|
|
63
|
+
if (!isOpencodeSessionEventLogEnabled() || eventLogWriteDisabled) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const sessionId = getOpencodeEventSessionId(entry.event);
|
|
67
|
+
if (!sessionId) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const logDirResult = await errore.tryAsync(() => {
|
|
71
|
+
return resolveEventLogDirectory();
|
|
72
|
+
});
|
|
73
|
+
if (logDirResult instanceof Error) {
|
|
74
|
+
eventLogWriteDisabled = true;
|
|
75
|
+
return logDirResult;
|
|
76
|
+
}
|
|
77
|
+
const safeSessionId = sanitizeSessionIdForFilename(sessionId);
|
|
78
|
+
const logFilePath = path.join(logDirResult, `${safeSessionId}.jsonl`);
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
const line = `${JSON.stringify(buildOpencodeEventLogLine({
|
|
81
|
+
timestamp: now,
|
|
82
|
+
threadId: entry.threadId,
|
|
83
|
+
projectDirectory: entry.projectDirectory,
|
|
84
|
+
event: entry.event,
|
|
85
|
+
}))}\n`;
|
|
86
|
+
const appendResult = await errore.tryAsync(() => {
|
|
87
|
+
return fs.promises.appendFile(logFilePath, line, 'utf8');
|
|
88
|
+
});
|
|
89
|
+
if (appendResult instanceof Error) {
|
|
90
|
+
eventLogWriteDisabled = true;
|
|
91
|
+
return appendResult;
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Per-thread state type, transition functions, and selectors.
|
|
2
|
+
// All transitions operate on the global store from ../store.js.
|
|
3
|
+
//
|
|
4
|
+
// ThreadRunState is a value-type: one entry per active thread in the
|
|
5
|
+
// global store's `threads` Map. Transition functions produce new Map +
|
|
6
|
+
// new ThreadRunState objects each time (immutable updates).
|
|
7
|
+
//
|
|
8
|
+
// Derived helpers (queue checks) compute from state and are never
|
|
9
|
+
// stored — they are always re-derived from ThreadRunState.
|
|
10
|
+
//
|
|
11
|
+
// STATE DISCIPLINE: keep as little state as possible. Before adding any new
|
|
12
|
+
// state field, ask if it can be derived from existing state instead.
|
|
13
|
+
import { store } from '../store.js';
|
|
14
|
+
// ── Initial state factory ────────────────────────────────────────
|
|
15
|
+
export function initialThreadState() {
|
|
16
|
+
return {
|
|
17
|
+
sessionId: undefined,
|
|
18
|
+
sessionUsername: undefined,
|
|
19
|
+
queueItems: [],
|
|
20
|
+
listenerController: undefined,
|
|
21
|
+
sentPartIds: new Set(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// ── Derived helpers (compute, never store) ───────────────────────
|
|
25
|
+
export function hasQueue(t) {
|
|
26
|
+
return t.queueItems.length > 0;
|
|
27
|
+
}
|
|
28
|
+
// ── Pure transition helpers ──────────────────────────────────────
|
|
29
|
+
// Immutable: produces new Map + new ThreadRunState object each time.
|
|
30
|
+
export function updateThread(threadId, updater) {
|
|
31
|
+
store.setState((s) => {
|
|
32
|
+
const existing = s.threads.get(threadId);
|
|
33
|
+
if (!existing) {
|
|
34
|
+
return s;
|
|
35
|
+
}
|
|
36
|
+
const newThreads = new Map(s.threads);
|
|
37
|
+
newThreads.set(threadId, updater(existing));
|
|
38
|
+
return { threads: newThreads };
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export function ensureThread(threadId) {
|
|
42
|
+
if (store.getState().threads.has(threadId)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
store.setState((s) => {
|
|
46
|
+
const newThreads = new Map(s.threads);
|
|
47
|
+
newThreads.set(threadId, initialThreadState());
|
|
48
|
+
return { threads: newThreads };
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
export function removeThread(threadId) {
|
|
52
|
+
store.setState((s) => {
|
|
53
|
+
if (!s.threads.has(threadId)) {
|
|
54
|
+
return s;
|
|
55
|
+
}
|
|
56
|
+
const newThreads = new Map(s.threads);
|
|
57
|
+
newThreads.delete(threadId);
|
|
58
|
+
return { threads: newThreads };
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export function setSessionId(threadId, sessionId) {
|
|
62
|
+
updateThread(threadId, (t) => ({ ...t, sessionId }));
|
|
63
|
+
}
|
|
64
|
+
export function setSessionUsername(threadId, username) {
|
|
65
|
+
updateThread(threadId, (t) => {
|
|
66
|
+
if (t.sessionUsername) {
|
|
67
|
+
return t;
|
|
68
|
+
}
|
|
69
|
+
return { ...t, sessionUsername: username };
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export function enqueueItem(threadId, item) {
|
|
73
|
+
updateThread(threadId, (t) => ({
|
|
74
|
+
...t,
|
|
75
|
+
queueItems: [...t.queueItems, item],
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
// Atomic dequeue: read + write in one setState call to prevent
|
|
79
|
+
// a concurrent enqueue between read and write from losing items.
|
|
80
|
+
export function dequeueItem(threadId) {
|
|
81
|
+
let next;
|
|
82
|
+
store.setState((s) => {
|
|
83
|
+
const t = s.threads.get(threadId);
|
|
84
|
+
if (!t || t.queueItems.length === 0) {
|
|
85
|
+
return s;
|
|
86
|
+
}
|
|
87
|
+
const [head, ...rest] = t.queueItems;
|
|
88
|
+
next = head;
|
|
89
|
+
const newThreads = new Map(s.threads);
|
|
90
|
+
newThreads.set(threadId, { ...t, queueItems: rest });
|
|
91
|
+
return { threads: newThreads };
|
|
92
|
+
});
|
|
93
|
+
return next;
|
|
94
|
+
}
|
|
95
|
+
export function clearQueueItems(threadId) {
|
|
96
|
+
updateThread(threadId, (t) => ({ ...t, queueItems: [] }));
|
|
97
|
+
}
|
|
98
|
+
export function removeQueueItemAtPosition(threadId, position) {
|
|
99
|
+
if (position < 1) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
let removedItem;
|
|
103
|
+
store.setState((s) => {
|
|
104
|
+
const t = s.threads.get(threadId);
|
|
105
|
+
if (!t) {
|
|
106
|
+
return s;
|
|
107
|
+
}
|
|
108
|
+
const index = position - 1;
|
|
109
|
+
const removed = t.queueItems[index];
|
|
110
|
+
if (!removed) {
|
|
111
|
+
return s;
|
|
112
|
+
}
|
|
113
|
+
removedItem = removed;
|
|
114
|
+
const newThreads = new Map(s.threads);
|
|
115
|
+
newThreads.set(threadId, {
|
|
116
|
+
...t,
|
|
117
|
+
queueItems: t.queueItems.filter((_, itemIndex) => {
|
|
118
|
+
return itemIndex !== index;
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
121
|
+
return { threads: newThreads };
|
|
122
|
+
});
|
|
123
|
+
return removedItem;
|
|
124
|
+
}
|
|
125
|
+
// ── Queries ──────────────────────────────────────────────────────
|
|
126
|
+
export function getThreadState(threadId) {
|
|
127
|
+
return store.getState().threads.get(threadId);
|
|
128
|
+
}
|
|
129
|
+
export function getThreadIds() {
|
|
130
|
+
return [...store.getState().threads.keys()];
|
|
131
|
+
}
|