@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,164 @@
|
|
|
1
|
+
// Main thread interface for the GenAI worker.
|
|
2
|
+
// Spawns and manages the worker thread, handling message passing for
|
|
3
|
+
// audio input/output, tool call completions, and graceful shutdown.
|
|
4
|
+
|
|
5
|
+
import { Worker } from 'node:worker_threads'
|
|
6
|
+
import type { WorkerInMessage, WorkerOutMessage } from './worker-types.js'
|
|
7
|
+
import { createLogger, LogPrefix } from './logger.js'
|
|
8
|
+
import { notifyError } from './sentry.js'
|
|
9
|
+
|
|
10
|
+
const genaiWorkerLogger = createLogger(LogPrefix.GENAI_WORKER)
|
|
11
|
+
const genaiWrapperLogger = createLogger(LogPrefix.GENAI_WORKER)
|
|
12
|
+
|
|
13
|
+
export interface GenAIWorkerOptions {
|
|
14
|
+
directory: string
|
|
15
|
+
systemMessage?: string
|
|
16
|
+
guildId: string
|
|
17
|
+
channelId: string
|
|
18
|
+
appId: string
|
|
19
|
+
geminiApiKey?: string | null
|
|
20
|
+
onAssistantOpusPacket: (packet: ArrayBuffer) => void
|
|
21
|
+
onAssistantStartSpeaking?: () => void
|
|
22
|
+
onAssistantStopSpeaking?: () => void
|
|
23
|
+
onAssistantInterruptSpeaking?: () => void
|
|
24
|
+
onToolCallCompleted?: (params: {
|
|
25
|
+
sessionId: string
|
|
26
|
+
messageId: string
|
|
27
|
+
data?: unknown
|
|
28
|
+
error?: unknown
|
|
29
|
+
markdown?: string
|
|
30
|
+
}) => void
|
|
31
|
+
onError?: (error: string) => void
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface GenAIWorker {
|
|
35
|
+
sendRealtimeInput(params: {
|
|
36
|
+
audio?: { mimeType: string; data: string }
|
|
37
|
+
audioStreamEnd?: boolean
|
|
38
|
+
}): void
|
|
39
|
+
sendTextInput(text: string): void
|
|
40
|
+
interrupt(): void
|
|
41
|
+
stop(): Promise<void>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function createGenAIWorker(
|
|
45
|
+
options: GenAIWorkerOptions,
|
|
46
|
+
): Promise<GenAIWorker> {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
const worker = new Worker(
|
|
49
|
+
new URL('../dist/genai-worker.js', import.meta.url),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
// Handle messages from worker
|
|
53
|
+
worker.on('message', (message: WorkerOutMessage) => {
|
|
54
|
+
switch (message.type) {
|
|
55
|
+
case 'assistantOpusPacket':
|
|
56
|
+
options.onAssistantOpusPacket(message.packet)
|
|
57
|
+
break
|
|
58
|
+
case 'assistantStartSpeaking':
|
|
59
|
+
options.onAssistantStartSpeaking?.()
|
|
60
|
+
break
|
|
61
|
+
case 'assistantStopSpeaking':
|
|
62
|
+
options.onAssistantStopSpeaking?.()
|
|
63
|
+
break
|
|
64
|
+
case 'assistantInterruptSpeaking':
|
|
65
|
+
options.onAssistantInterruptSpeaking?.()
|
|
66
|
+
break
|
|
67
|
+
case 'toolCallCompleted':
|
|
68
|
+
options.onToolCallCompleted?.(message)
|
|
69
|
+
break
|
|
70
|
+
case 'error':
|
|
71
|
+
genaiWorkerLogger.error('Error:', message.error)
|
|
72
|
+
options.onError?.(message.error)
|
|
73
|
+
break
|
|
74
|
+
case 'ready':
|
|
75
|
+
genaiWorkerLogger.log('Ready')
|
|
76
|
+
// Resolve with the worker interface
|
|
77
|
+
resolve({
|
|
78
|
+
sendRealtimeInput({ audio, audioStreamEnd }) {
|
|
79
|
+
worker.postMessage({
|
|
80
|
+
type: 'sendRealtimeInput',
|
|
81
|
+
audio,
|
|
82
|
+
audioStreamEnd,
|
|
83
|
+
} satisfies WorkerInMessage)
|
|
84
|
+
},
|
|
85
|
+
sendTextInput(text) {
|
|
86
|
+
worker.postMessage({
|
|
87
|
+
type: 'sendTextInput',
|
|
88
|
+
text,
|
|
89
|
+
} satisfies WorkerInMessage)
|
|
90
|
+
},
|
|
91
|
+
interrupt() {
|
|
92
|
+
worker.postMessage({
|
|
93
|
+
type: 'interrupt',
|
|
94
|
+
} satisfies WorkerInMessage)
|
|
95
|
+
},
|
|
96
|
+
async stop() {
|
|
97
|
+
genaiWrapperLogger.log('Stopping worker...')
|
|
98
|
+
// Send stop message to trigger graceful shutdown
|
|
99
|
+
worker.postMessage({ type: 'stop' } satisfies WorkerInMessage)
|
|
100
|
+
|
|
101
|
+
// Wait for worker to exit gracefully (with timeout)
|
|
102
|
+
await new Promise<void>((resolve) => {
|
|
103
|
+
let resolved = false
|
|
104
|
+
|
|
105
|
+
// Listen for worker exit
|
|
106
|
+
worker.once('exit', (code) => {
|
|
107
|
+
if (!resolved) {
|
|
108
|
+
resolved = true
|
|
109
|
+
genaiWrapperLogger.log(
|
|
110
|
+
`[GENAI WORKER WRAPPER] Worker exited with code ${code}`,
|
|
111
|
+
)
|
|
112
|
+
resolve()
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
// Timeout after 5 seconds and force terminate
|
|
117
|
+
setTimeout(() => {
|
|
118
|
+
if (!resolved) {
|
|
119
|
+
resolved = true
|
|
120
|
+
genaiWrapperLogger.log(
|
|
121
|
+
'[GENAI WORKER WRAPPER] Worker did not exit gracefully, terminating...',
|
|
122
|
+
)
|
|
123
|
+
worker.terminate().then(() => {
|
|
124
|
+
genaiWrapperLogger.log('Worker terminated')
|
|
125
|
+
resolve()
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
}, 5000)
|
|
129
|
+
})
|
|
130
|
+
},
|
|
131
|
+
})
|
|
132
|
+
break
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
// Handle worker errors
|
|
137
|
+
worker.on('error', (error) => {
|
|
138
|
+
genaiWorkerLogger.error('Worker error:', error)
|
|
139
|
+
reject(error)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
worker.on('exit', (code) => {
|
|
143
|
+
if (code !== 0) {
|
|
144
|
+
genaiWorkerLogger.error(`Worker stopped with exit code ${code}`)
|
|
145
|
+
void notifyError(
|
|
146
|
+
new Error(`GenAI worker exited with code ${code}`),
|
|
147
|
+
'GenAI worker non-zero exit after init',
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
// Send initialization message
|
|
153
|
+
const initMessage: WorkerInMessage = {
|
|
154
|
+
type: 'init',
|
|
155
|
+
directory: options.directory,
|
|
156
|
+
systemMessage: options.systemMessage,
|
|
157
|
+
guildId: options.guildId,
|
|
158
|
+
channelId: options.channelId,
|
|
159
|
+
appId: options.appId,
|
|
160
|
+
geminiApiKey: options.geminiApiKey,
|
|
161
|
+
}
|
|
162
|
+
worker.postMessage(initMessage)
|
|
163
|
+
})
|
|
164
|
+
}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
// Worker thread for GenAI voice processing.
|
|
2
|
+
// Runs in a separate thread to handle audio encoding/decoding without blocking.
|
|
3
|
+
// Resamples 24kHz GenAI output to 48kHz stereo Opus packets for Discord.
|
|
4
|
+
|
|
5
|
+
import { parentPort, threadId } from 'node:worker_threads'
|
|
6
|
+
import { createWriteStream, type WriteStream } from 'node:fs'
|
|
7
|
+
import path from 'node:path'
|
|
8
|
+
import * as errore from 'errore'
|
|
9
|
+
import { Resampler } from '@purinton/resampler'
|
|
10
|
+
import * as prism from 'prism-media'
|
|
11
|
+
import { startGenAiSession } from './genai.js'
|
|
12
|
+
import type { Session } from '@google/genai'
|
|
13
|
+
import { getTools } from './tools.js'
|
|
14
|
+
import { mkdir } from 'node:fs/promises'
|
|
15
|
+
import type { WorkerInMessage, WorkerOutMessage } from './worker-types.js'
|
|
16
|
+
import { createLogger, formatErrorWithStack, LogPrefix } from './logger.js'
|
|
17
|
+
import { initSentry, notifyError } from './sentry.js'
|
|
18
|
+
|
|
19
|
+
if (!parentPort) {
|
|
20
|
+
throw new Error('This module must be run as a worker thread')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const workerLogger = createLogger(`${LogPrefix.WORKER}_${threadId}`)
|
|
24
|
+
workerLogger.log('GenAI worker started')
|
|
25
|
+
|
|
26
|
+
// Initialize Sentry in worker thread (inherits KIMAKI_SENTRY_DSN from parent)
|
|
27
|
+
initSentry()
|
|
28
|
+
|
|
29
|
+
// Define sendError early so it can be used by global handlers
|
|
30
|
+
function sendError(error: string) {
|
|
31
|
+
if (parentPort) {
|
|
32
|
+
parentPort.postMessage({
|
|
33
|
+
type: 'error',
|
|
34
|
+
error,
|
|
35
|
+
} satisfies WorkerOutMessage)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Add global error handlers for the worker thread
|
|
40
|
+
process.on('uncaughtException', (error) => {
|
|
41
|
+
workerLogger.error('Uncaught exception in worker:', error)
|
|
42
|
+
void notifyError(error, 'Uncaught exception in GenAI worker')
|
|
43
|
+
sendError(`Worker crashed: ${error.message}`)
|
|
44
|
+
process.exit(1)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
48
|
+
const formattedReason = formatErrorWithStack(reason)
|
|
49
|
+
workerLogger.error(
|
|
50
|
+
'Unhandled rejection in worker:',
|
|
51
|
+
formattedReason,
|
|
52
|
+
'at promise:',
|
|
53
|
+
promise,
|
|
54
|
+
)
|
|
55
|
+
const error =
|
|
56
|
+
reason instanceof Error
|
|
57
|
+
? reason
|
|
58
|
+
: new Error(formattedReason)
|
|
59
|
+
void notifyError(error, 'Unhandled rejection in GenAI worker')
|
|
60
|
+
sendError(`Worker unhandled rejection: ${formattedReason}`)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// Audio configuration
|
|
64
|
+
const AUDIO_CONFIG = {
|
|
65
|
+
inputSampleRate: 24000, // GenAI output
|
|
66
|
+
inputChannels: 1,
|
|
67
|
+
outputSampleRate: 48000, // Discord expects
|
|
68
|
+
outputChannels: 2,
|
|
69
|
+
opusFrameSize: 960, // 20ms at 48kHz
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Initialize audio processing components
|
|
73
|
+
const resampler = new Resampler({
|
|
74
|
+
inRate: AUDIO_CONFIG.inputSampleRate,
|
|
75
|
+
outRate: AUDIO_CONFIG.outputSampleRate,
|
|
76
|
+
inChannels: AUDIO_CONFIG.inputChannels,
|
|
77
|
+
outChannels: AUDIO_CONFIG.outputChannels,
|
|
78
|
+
volume: 1,
|
|
79
|
+
filterWindow: 8,
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const opusEncoder = new prism.opus.Encoder({
|
|
83
|
+
rate: AUDIO_CONFIG.outputSampleRate,
|
|
84
|
+
channels: AUDIO_CONFIG.outputChannels,
|
|
85
|
+
frameSize: AUDIO_CONFIG.opusFrameSize,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
// Pipe resampler to encoder with error handling
|
|
89
|
+
resampler.pipe(opusEncoder).on('error', (error) => {
|
|
90
|
+
workerLogger.error('Pipe error between resampler and encoder:', error)
|
|
91
|
+
void notifyError(error, 'GenAI worker audio pipeline error')
|
|
92
|
+
sendError(`Audio pipeline error: ${error.message}`)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// Opus packet queue and interval for 20ms packet sending
|
|
96
|
+
const opusPacketQueue: Buffer[] = []
|
|
97
|
+
let packetInterval: NodeJS.Timeout | null = null
|
|
98
|
+
|
|
99
|
+
// Send packets every 20ms
|
|
100
|
+
function startPacketSending() {
|
|
101
|
+
if (packetInterval) return
|
|
102
|
+
|
|
103
|
+
packetInterval = setInterval(() => {
|
|
104
|
+
const packet = opusPacketQueue.shift()
|
|
105
|
+
if (!packet) return
|
|
106
|
+
|
|
107
|
+
// Transfer packet as ArrayBuffer
|
|
108
|
+
const arrayBuffer = packet.buffer.slice(
|
|
109
|
+
packet.byteOffset,
|
|
110
|
+
packet.byteOffset + packet.byteLength,
|
|
111
|
+
) as ArrayBuffer
|
|
112
|
+
|
|
113
|
+
parentPort!.postMessage(
|
|
114
|
+
{
|
|
115
|
+
type: 'assistantOpusPacket',
|
|
116
|
+
packet: arrayBuffer,
|
|
117
|
+
} satisfies WorkerOutMessage,
|
|
118
|
+
[arrayBuffer], // Transfer ownership
|
|
119
|
+
)
|
|
120
|
+
}, 20)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function stopPacketSending() {
|
|
124
|
+
if (packetInterval) {
|
|
125
|
+
clearInterval(packetInterval)
|
|
126
|
+
packetInterval = null
|
|
127
|
+
}
|
|
128
|
+
opusPacketQueue.length = 0
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Session state
|
|
132
|
+
let session: { session: Session; stop: () => void } | null = null
|
|
133
|
+
|
|
134
|
+
// Audio log stream for assistant audio
|
|
135
|
+
let audioLogStream: WriteStream | null = null
|
|
136
|
+
|
|
137
|
+
// Create assistant audio log stream for debugging
|
|
138
|
+
async function createAssistantAudioLogStream(
|
|
139
|
+
guildId: string,
|
|
140
|
+
channelId: string,
|
|
141
|
+
): Promise<WriteStream | null> {
|
|
142
|
+
if (!process.env.DEBUG) return null
|
|
143
|
+
|
|
144
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
|
|
145
|
+
const audioDir = path.join(
|
|
146
|
+
process.cwd(),
|
|
147
|
+
'discord-audio-logs',
|
|
148
|
+
guildId,
|
|
149
|
+
channelId,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
const mkdirError = await errore.tryAsync({
|
|
153
|
+
try: () => mkdir(audioDir, { recursive: true }),
|
|
154
|
+
catch: (e) => e as Error,
|
|
155
|
+
})
|
|
156
|
+
if (mkdirError instanceof Error) {
|
|
157
|
+
workerLogger.error(
|
|
158
|
+
`Failed to create audio log directory:`,
|
|
159
|
+
mkdirError.message,
|
|
160
|
+
)
|
|
161
|
+
return null
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Create stream for assistant audio (24kHz mono s16le PCM)
|
|
165
|
+
const outputFileName = `assistant_${timestamp}.24.pcm`
|
|
166
|
+
const outputFilePath = path.join(audioDir, outputFileName)
|
|
167
|
+
const outputAudioStream = createWriteStream(outputFilePath)
|
|
168
|
+
|
|
169
|
+
// Add error handler to prevent crashes
|
|
170
|
+
outputAudioStream.on('error', (error) => {
|
|
171
|
+
workerLogger.error(`Assistant audio log stream error:`, error)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
workerLogger.log(`Created assistant audio log: ${outputFilePath}`)
|
|
175
|
+
|
|
176
|
+
return outputAudioStream
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Handle encoded Opus packets
|
|
180
|
+
opusEncoder.on('data', (packet: Buffer) => {
|
|
181
|
+
opusPacketQueue.push(packet)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
// Handle stream end events
|
|
185
|
+
opusEncoder.on('end', () => {
|
|
186
|
+
workerLogger.log('Opus encoder stream ended')
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
resampler.on('end', () => {
|
|
190
|
+
workerLogger.log('Resampler stream ended')
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
// Handle errors
|
|
194
|
+
resampler.on('error', (error: unknown) => {
|
|
195
|
+
workerLogger.error(`Resampler error:`, error)
|
|
196
|
+
void notifyError(error, 'GenAI worker resampler error')
|
|
197
|
+
sendError(`Resampler error: ${(error as Error).message}`)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
opusEncoder.on('error', (error: unknown) => {
|
|
201
|
+
workerLogger.error(`Encoder error:`, error)
|
|
202
|
+
const errMsg = (error as Error).message || ''
|
|
203
|
+
// Check for specific corrupted data errors
|
|
204
|
+
if (errMsg.includes('The compressed data passed is corrupted')) {
|
|
205
|
+
workerLogger.warn('Received corrupted audio data in opus encoder')
|
|
206
|
+
} else {
|
|
207
|
+
void notifyError(error, 'GenAI worker encoder error')
|
|
208
|
+
sendError(`Encoder error: ${errMsg}`)
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
async function cleanupAsync(): Promise<void> {
|
|
213
|
+
workerLogger.log(`Starting async cleanup`)
|
|
214
|
+
|
|
215
|
+
stopPacketSending()
|
|
216
|
+
|
|
217
|
+
if (session) {
|
|
218
|
+
workerLogger.log(`Stopping GenAI session`)
|
|
219
|
+
session.stop()
|
|
220
|
+
session = null
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Wait for audio log stream to finish writing
|
|
224
|
+
if (audioLogStream) {
|
|
225
|
+
workerLogger.log(`Closing assistant audio log stream`)
|
|
226
|
+
await new Promise<void>((resolve, reject) => {
|
|
227
|
+
audioLogStream!.end(() => {
|
|
228
|
+
workerLogger.log(`Assistant audio log stream closed`)
|
|
229
|
+
resolve()
|
|
230
|
+
})
|
|
231
|
+
audioLogStream!.on('error', reject)
|
|
232
|
+
// Add timeout to prevent hanging
|
|
233
|
+
setTimeout(() => {
|
|
234
|
+
workerLogger.log(`Audio stream close timeout, continuing`)
|
|
235
|
+
resolve()
|
|
236
|
+
}, 3000)
|
|
237
|
+
})
|
|
238
|
+
audioLogStream = null
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Unpipe and end the encoder first
|
|
242
|
+
resampler.unpipe(opusEncoder)
|
|
243
|
+
|
|
244
|
+
// End the encoder stream
|
|
245
|
+
await new Promise<void>((resolve) => {
|
|
246
|
+
opusEncoder.end(() => {
|
|
247
|
+
workerLogger.log(`Opus encoder ended`)
|
|
248
|
+
resolve()
|
|
249
|
+
})
|
|
250
|
+
// Add timeout
|
|
251
|
+
setTimeout(resolve, 1000)
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
// End the resampler stream
|
|
255
|
+
await new Promise<void>((resolve) => {
|
|
256
|
+
resampler.end(() => {
|
|
257
|
+
workerLogger.log(`Resampler ended`)
|
|
258
|
+
resolve()
|
|
259
|
+
})
|
|
260
|
+
// Add timeout
|
|
261
|
+
setTimeout(resolve, 1000)
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
workerLogger.log(`Async cleanup complete`)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Handle messages from main thread
|
|
268
|
+
parentPort.on('message', async (message: WorkerInMessage) => {
|
|
269
|
+
try {
|
|
270
|
+
switch (message.type) {
|
|
271
|
+
case 'init': {
|
|
272
|
+
workerLogger.log(`Initializing with directory:`, message.directory)
|
|
273
|
+
|
|
274
|
+
// Create audio log stream for assistant audio
|
|
275
|
+
audioLogStream = await createAssistantAudioLogStream(
|
|
276
|
+
message.guildId,
|
|
277
|
+
message.channelId,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
// Start packet sending interval
|
|
281
|
+
startPacketSending()
|
|
282
|
+
|
|
283
|
+
// Get tools for the directory
|
|
284
|
+
const { tools } = await getTools({
|
|
285
|
+
directory: message.directory,
|
|
286
|
+
onMessageCompleted: (params) => {
|
|
287
|
+
parentPort!.postMessage({
|
|
288
|
+
type: 'toolCallCompleted',
|
|
289
|
+
...params,
|
|
290
|
+
} satisfies WorkerOutMessage)
|
|
291
|
+
},
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
// Start GenAI session
|
|
295
|
+
session = await startGenAiSession({
|
|
296
|
+
tools,
|
|
297
|
+
systemMessage: message.systemMessage,
|
|
298
|
+
geminiApiKey: message.geminiApiKey,
|
|
299
|
+
onAssistantAudioChunk({ data }) {
|
|
300
|
+
// Write to audio log if enabled
|
|
301
|
+
if (audioLogStream && !audioLogStream.destroyed) {
|
|
302
|
+
audioLogStream.write(data, (err) => {
|
|
303
|
+
if (err) {
|
|
304
|
+
workerLogger.error('Error writing to audio log:', err)
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Write PCM data to resampler which will output Opus packets
|
|
310
|
+
if (!resampler.destroyed) {
|
|
311
|
+
resampler.write(data, (err) => {
|
|
312
|
+
if (err) {
|
|
313
|
+
workerLogger.error('Error writing to resampler:', err)
|
|
314
|
+
sendError(`Failed to process audio: ${err.message}`)
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
onAssistantStartSpeaking() {
|
|
320
|
+
parentPort!.postMessage({
|
|
321
|
+
type: 'assistantStartSpeaking',
|
|
322
|
+
} satisfies WorkerOutMessage)
|
|
323
|
+
},
|
|
324
|
+
onAssistantStopSpeaking() {
|
|
325
|
+
parentPort!.postMessage({
|
|
326
|
+
type: 'assistantStopSpeaking',
|
|
327
|
+
} satisfies WorkerOutMessage)
|
|
328
|
+
},
|
|
329
|
+
onAssistantInterruptSpeaking() {
|
|
330
|
+
parentPort!.postMessage({
|
|
331
|
+
type: 'assistantInterruptSpeaking',
|
|
332
|
+
} satisfies WorkerOutMessage)
|
|
333
|
+
},
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
// Notify main thread we're ready
|
|
337
|
+
parentPort!.postMessage({
|
|
338
|
+
type: 'ready',
|
|
339
|
+
} satisfies WorkerOutMessage)
|
|
340
|
+
break
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
case 'sendRealtimeInput': {
|
|
344
|
+
if (!session) {
|
|
345
|
+
sendError('Session not initialized')
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
session.session.sendRealtimeInput({
|
|
349
|
+
audio: message.audio,
|
|
350
|
+
audioStreamEnd: message.audioStreamEnd,
|
|
351
|
+
})
|
|
352
|
+
break
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
case 'sendTextInput': {
|
|
356
|
+
if (!session) {
|
|
357
|
+
sendError('Session not initialized')
|
|
358
|
+
return
|
|
359
|
+
}
|
|
360
|
+
session.session.sendRealtimeInput({
|
|
361
|
+
text: message.text,
|
|
362
|
+
})
|
|
363
|
+
break
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
case 'interrupt': {
|
|
367
|
+
workerLogger.log(`Interrupting playback`)
|
|
368
|
+
// Clear the opus packet queue
|
|
369
|
+
opusPacketQueue.length = 0
|
|
370
|
+
break
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
case 'stop': {
|
|
374
|
+
workerLogger.log(`Stopping worker`)
|
|
375
|
+
await cleanupAsync()
|
|
376
|
+
// process.exit(0)
|
|
377
|
+
break
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
} catch (error) {
|
|
381
|
+
workerLogger.error(`Error handling message:`, error)
|
|
382
|
+
sendError(
|
|
383
|
+
error instanceof Error ? error.message : 'Unknown error in worker',
|
|
384
|
+
)
|
|
385
|
+
}
|
|
386
|
+
})
|