@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
package/src/logger.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// Prefixed logging utility using @clack/prompts for consistent visual style.
|
|
2
|
+
// All log methods use clack's log.message() with appropriate symbols to prevent
|
|
3
|
+
// output interleaving from concurrent async operations.
|
|
4
|
+
|
|
5
|
+
import { log as clackLog } from '@clack/prompts'
|
|
6
|
+
import fs from 'node:fs'
|
|
7
|
+
import path from 'node:path'
|
|
8
|
+
import util from 'node:util'
|
|
9
|
+
import pc from 'picocolors'
|
|
10
|
+
import { sanitizeSensitiveText, sanitizeUnknownValue } from './privacy-sanitizer.js'
|
|
11
|
+
|
|
12
|
+
// All known log prefixes - add new ones here to keep alignment consistent
|
|
13
|
+
export const LogPrefix = {
|
|
14
|
+
ABORT: 'ABORT',
|
|
15
|
+
ADD_PROJECT: 'ADD_PROJ',
|
|
16
|
+
AGENT: 'AGENT',
|
|
17
|
+
ASK_QUESTION: 'QUESTION',
|
|
18
|
+
CHANNEL: 'CHANNEL',
|
|
19
|
+
CLI: 'CLI',
|
|
20
|
+
COMPACT: 'COMPACT',
|
|
21
|
+
CREATE_PROJECT: 'NEW_PROJ',
|
|
22
|
+
DB: 'DB',
|
|
23
|
+
DIFF: 'DIFF',
|
|
24
|
+
FILE_UPLOAD: 'FILEUP',
|
|
25
|
+
DISCORD: 'DISCORD',
|
|
26
|
+
FORK: 'FORK',
|
|
27
|
+
FORMATTING: 'FORMAT',
|
|
28
|
+
GENAI: 'GENAI',
|
|
29
|
+
HEAP: 'HEAP',
|
|
30
|
+
GENAI_WORKER: 'GENAI_W',
|
|
31
|
+
INTERACTION: 'INTERACT',
|
|
32
|
+
IPC: 'IPC',
|
|
33
|
+
LOGIN: 'LOGIN',
|
|
34
|
+
MARKDOWN: 'MARKDOWN',
|
|
35
|
+
MCP: 'MCP',
|
|
36
|
+
MODEL: 'MODEL',
|
|
37
|
+
OPENAI: 'OPENAI',
|
|
38
|
+
OPENCODE: 'OPENCODE',
|
|
39
|
+
PERMISSIONS: 'PERMS',
|
|
40
|
+
QUEUE: 'QUEUE',
|
|
41
|
+
REMOVE_PROJECT: 'RM_PROJ',
|
|
42
|
+
RESUME: 'RESUME',
|
|
43
|
+
SESSION: 'SESSION',
|
|
44
|
+
SHARE: 'SHARE',
|
|
45
|
+
TASK: 'TASK',
|
|
46
|
+
TOOLS: 'TOOLS',
|
|
47
|
+
UNDO_REDO: 'UNDO',
|
|
48
|
+
USER_CMD: 'USER_CMD',
|
|
49
|
+
VERBOSITY: 'VERBOSE',
|
|
50
|
+
VOICE: 'VOICE',
|
|
51
|
+
WORKER: 'WORKER',
|
|
52
|
+
THINKING: 'THINK',
|
|
53
|
+
WORKTREE: 'WORKTREE',
|
|
54
|
+
XML: 'XML',
|
|
55
|
+
} as const
|
|
56
|
+
|
|
57
|
+
export type LogPrefixType = (typeof LogPrefix)[keyof typeof LogPrefix]
|
|
58
|
+
|
|
59
|
+
// compute max length from all known prefixes for alignment
|
|
60
|
+
const MAX_PREFIX_LENGTH = Math.max(
|
|
61
|
+
...Object.values(LogPrefix).map((p) => p.length),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
// Log file path is set by initLogFile() after the data directory is known.
|
|
65
|
+
// Before initLogFile() is called, file logging is skipped.
|
|
66
|
+
let logFilePath: string | null = null
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Initialize file logging. Call this after setDataDir() so the log file
|
|
70
|
+
* is written to `<dataDir>/kimaki.log`. The log file is truncated on
|
|
71
|
+
* every bot startup so it contains only the current run's logs.
|
|
72
|
+
*/
|
|
73
|
+
export function initLogFile(dataDir: string): void {
|
|
74
|
+
logFilePath = path.join(dataDir, 'kimaki.log')
|
|
75
|
+
const logDir = path.dirname(logFilePath)
|
|
76
|
+
if (!fs.existsSync(logDir)) {
|
|
77
|
+
fs.mkdirSync(logDir, { recursive: true })
|
|
78
|
+
}
|
|
79
|
+
fs.writeFileSync(
|
|
80
|
+
logFilePath,
|
|
81
|
+
`--- kimaki log started at ${new Date().toISOString()} ---\n`,
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Set the log file path without truncating. Use this in child processes
|
|
87
|
+
* (like the opencode plugin) that should append to the same log file
|
|
88
|
+
* the bot process already created with initLogFile().
|
|
89
|
+
*/
|
|
90
|
+
export function setLogFilePath(dataDir: string): void {
|
|
91
|
+
logFilePath = path.join(dataDir, 'kimaki.log')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function getLogFilePath(): string | null {
|
|
95
|
+
return logFilePath
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function formatArg(arg: unknown): string {
|
|
99
|
+
if (typeof arg === 'string') {
|
|
100
|
+
return sanitizeSensitiveText(arg, { redactPaths: false })
|
|
101
|
+
}
|
|
102
|
+
const safeArg = sanitizeUnknownValue(arg, { redactPaths: false })
|
|
103
|
+
return util.inspect(safeArg, { colors: true, depth: 4 })
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function formatErrorWithStack(error: unknown): string {
|
|
107
|
+
if (error instanceof Error) {
|
|
108
|
+
return sanitizeSensitiveText(
|
|
109
|
+
error.stack ?? `${error.name}: ${error.message}`,
|
|
110
|
+
{ redactPaths: false },
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
if (typeof error === 'string') {
|
|
114
|
+
return sanitizeSensitiveText(error, { redactPaths: false })
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Keep this stable and safe for unknown values (handles circular structures).
|
|
118
|
+
const safeError = sanitizeUnknownValue(error, { redactPaths: false })
|
|
119
|
+
return sanitizeSensitiveText(util.inspect(safeError, { colors: false, depth: 4 }), {
|
|
120
|
+
redactPaths: false,
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function writeToFile(level: string, prefix: string, args: unknown[]) {
|
|
125
|
+
const timestamp = new Date().toISOString()
|
|
126
|
+
const message = `[${timestamp}] [${level}] [${prefix}] ${args.map(formatArg).join(' ')}\n`
|
|
127
|
+
if (!logFilePath) {
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
fs.appendFileSync(logFilePath, message)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function getTimestamp(): string {
|
|
134
|
+
const now = new Date()
|
|
135
|
+
return `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function padPrefix(prefix: string): string {
|
|
139
|
+
return prefix.padEnd(MAX_PREFIX_LENGTH)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function formatMessage(
|
|
143
|
+
timestamp: string,
|
|
144
|
+
prefix: string,
|
|
145
|
+
args: unknown[],
|
|
146
|
+
): string {
|
|
147
|
+
return [pc.dim(timestamp), prefix, ...args.map(formatArg)].join(' ')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const noSpacing = { spacing: 0 }
|
|
151
|
+
|
|
152
|
+
// Suppress clack terminal output during vitest runs to avoid flooding
|
|
153
|
+
// test output with hundreds of log lines. File logging still works.
|
|
154
|
+
// Set KIMAKI_TEST_LOGS=1 when rerunning a failing test to see all
|
|
155
|
+
// kimaki logger output in the terminal for debugging.
|
|
156
|
+
const isVitest = !!process.env['KIMAKI_VITEST']
|
|
157
|
+
const showTestLogs = isVitest && !!process.env['KIMAKI_TEST_LOGS']
|
|
158
|
+
|
|
159
|
+
export function createLogger(prefix: LogPrefixType | string) {
|
|
160
|
+
const paddedPrefix = padPrefix(prefix)
|
|
161
|
+
const suppressConsole = isVitest && !showTestLogs
|
|
162
|
+
const log = (...args: unknown[]) => {
|
|
163
|
+
writeToFile('LOG', prefix, args)
|
|
164
|
+
if (suppressConsole) {
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
clackLog.message(
|
|
168
|
+
formatMessage(getTimestamp(), pc.cyan(paddedPrefix), args),
|
|
169
|
+
{
|
|
170
|
+
...noSpacing,
|
|
171
|
+
},
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
log,
|
|
176
|
+
error: (...args: unknown[]) => {
|
|
177
|
+
writeToFile('ERROR', prefix, args)
|
|
178
|
+
if (suppressConsole) {
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
clackLog.error(
|
|
182
|
+
formatMessage(getTimestamp(), pc.red(paddedPrefix), args),
|
|
183
|
+
noSpacing,
|
|
184
|
+
)
|
|
185
|
+
},
|
|
186
|
+
warn: (...args: unknown[]) => {
|
|
187
|
+
writeToFile('WARN', prefix, args)
|
|
188
|
+
if (suppressConsole) {
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
clackLog.warn(
|
|
192
|
+
formatMessage(getTimestamp(), pc.yellow(paddedPrefix), args),
|
|
193
|
+
noSpacing,
|
|
194
|
+
)
|
|
195
|
+
},
|
|
196
|
+
info: (...args: unknown[]) => {
|
|
197
|
+
writeToFile('INFO', prefix, args)
|
|
198
|
+
if (suppressConsole) {
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
clackLog.info(
|
|
202
|
+
formatMessage(getTimestamp(), pc.blue(paddedPrefix), args),
|
|
203
|
+
noSpacing,
|
|
204
|
+
)
|
|
205
|
+
},
|
|
206
|
+
debug: log,
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// Deterministic markdown export tests.
|
|
2
|
+
// Uses the shared opencode server manager with the deterministic provider,
|
|
3
|
+
// creates sessions with known content, and validates markdown output.
|
|
4
|
+
// No dependency on machine-local session state.
|
|
5
|
+
|
|
6
|
+
import fs from 'node:fs'
|
|
7
|
+
import path from 'node:path'
|
|
8
|
+
import url from 'node:url'
|
|
9
|
+
import { test, expect, beforeAll, afterAll } from 'vitest'
|
|
10
|
+
import type { OpencodeClient } from '@opencode-ai/sdk/v2'
|
|
11
|
+
import * as errore from 'errore'
|
|
12
|
+
import {
|
|
13
|
+
buildDeterministicOpencodeConfig,
|
|
14
|
+
type DeterministicMatcher,
|
|
15
|
+
} from 'opencode-deterministic-provider'
|
|
16
|
+
import { ShareMarkdown, getCompactSessionContext } from './markdown.js'
|
|
17
|
+
import { setDataDir } from './config.js'
|
|
18
|
+
import { initializeOpencodeForDirectory, getOpencodeClient, stopOpencodeServer } from './opencode.js'
|
|
19
|
+
import { cleanupTestSessions, initTestGitRepo } from './test-utils.js'
|
|
20
|
+
|
|
21
|
+
const ROOT = path.resolve(process.cwd(), 'tmp', 'markdown-test')
|
|
22
|
+
|
|
23
|
+
function createRunDirectories() {
|
|
24
|
+
fs.mkdirSync(ROOT, { recursive: true })
|
|
25
|
+
const dataDir = fs.mkdtempSync(path.join(ROOT, 'data-'))
|
|
26
|
+
const projectDirectory = path.join(ROOT, 'project')
|
|
27
|
+
fs.mkdirSync(projectDirectory, { recursive: true })
|
|
28
|
+
initTestGitRepo(projectDirectory)
|
|
29
|
+
return { dataDir, projectDirectory }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function createMatchers(): DeterministicMatcher[] {
|
|
33
|
+
const helloMatcher: DeterministicMatcher = {
|
|
34
|
+
id: 'hello-reply',
|
|
35
|
+
priority: 100,
|
|
36
|
+
when: { latestUserTextIncludes: 'hello markdown test' },
|
|
37
|
+
then: {
|
|
38
|
+
parts: [
|
|
39
|
+
{ type: 'stream-start', warnings: [] },
|
|
40
|
+
{ type: 'text-start', id: 'hello-text' },
|
|
41
|
+
{ type: 'text-delta', id: 'hello-text', delta: 'Hello! This is a deterministic markdown test response.' },
|
|
42
|
+
{ type: 'text-end', id: 'hello-text' },
|
|
43
|
+
{ type: 'finish', finishReason: 'stop', usage: { inputTokens: 10, outputTokens: 8, totalTokens: 18 } },
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const defaultMatcher: DeterministicMatcher = {
|
|
49
|
+
id: 'default-reply',
|
|
50
|
+
priority: 1,
|
|
51
|
+
then: {
|
|
52
|
+
parts: [
|
|
53
|
+
{ type: 'stream-start', warnings: [] },
|
|
54
|
+
{ type: 'text-start', id: 'default-text' },
|
|
55
|
+
{ type: 'text-delta', id: 'default-text', delta: 'ok' },
|
|
56
|
+
{ type: 'text-end', id: 'default-text' },
|
|
57
|
+
{ type: 'finish', finishReason: 'stop', usage: { inputTokens: 5, outputTokens: 1, totalTokens: 6 } },
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return [helloMatcher, defaultMatcher]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let client: OpencodeClient
|
|
66
|
+
let directories: ReturnType<typeof createRunDirectories>
|
|
67
|
+
let testStartTime: number
|
|
68
|
+
let sessionID: string
|
|
69
|
+
|
|
70
|
+
beforeAll(async () => {
|
|
71
|
+
testStartTime = Date.now()
|
|
72
|
+
directories = createRunDirectories()
|
|
73
|
+
setDataDir(directories.dataDir)
|
|
74
|
+
|
|
75
|
+
const providerNpm = url
|
|
76
|
+
.pathToFileURL(
|
|
77
|
+
path.resolve(
|
|
78
|
+
process.cwd(),
|
|
79
|
+
'..',
|
|
80
|
+
'opencode-deterministic-provider',
|
|
81
|
+
'src',
|
|
82
|
+
'index.ts',
|
|
83
|
+
),
|
|
84
|
+
)
|
|
85
|
+
.toString()
|
|
86
|
+
|
|
87
|
+
const opencodeConfig = buildDeterministicOpencodeConfig({
|
|
88
|
+
providerName: 'deterministic-provider',
|
|
89
|
+
providerNpm,
|
|
90
|
+
model: 'deterministic-v2',
|
|
91
|
+
smallModel: 'deterministic-v2',
|
|
92
|
+
settings: {
|
|
93
|
+
strict: false,
|
|
94
|
+
matchers: createMatchers(),
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
fs.writeFileSync(
|
|
98
|
+
path.join(directories.projectDirectory, 'opencode.json'),
|
|
99
|
+
JSON.stringify(opencodeConfig, null, 2),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
// Start the shared opencode server via kimaki's server manager
|
|
103
|
+
const getClient = await initializeOpencodeForDirectory(
|
|
104
|
+
directories.projectDirectory,
|
|
105
|
+
)
|
|
106
|
+
if (getClient instanceof Error) {
|
|
107
|
+
throw getClient
|
|
108
|
+
}
|
|
109
|
+
client = getClient()
|
|
110
|
+
|
|
111
|
+
// Create a session and send a known prompt
|
|
112
|
+
const createResult = await client.session.create({
|
|
113
|
+
directory: directories.projectDirectory,
|
|
114
|
+
title: 'Markdown Test Session',
|
|
115
|
+
})
|
|
116
|
+
sessionID = createResult.data!.id
|
|
117
|
+
|
|
118
|
+
// Send prompt and wait for completion (promptAsync returns immediately)
|
|
119
|
+
await client.session.promptAsync({
|
|
120
|
+
sessionID,
|
|
121
|
+
directory: directories.projectDirectory,
|
|
122
|
+
parts: [{ type: 'text', text: 'hello markdown test' }],
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// Wait for assistant text parts to be fully written (not just message existence).
|
|
126
|
+
// The deterministic provider responds instantly but opencode writes parts
|
|
127
|
+
// asynchronously, so we must poll until non-empty text content appears.
|
|
128
|
+
// Under parallel test load the server is slower, so use generous timeouts.
|
|
129
|
+
const maxWait = 15_000
|
|
130
|
+
const pollStart = Date.now()
|
|
131
|
+
while (Date.now() - pollStart < maxWait) {
|
|
132
|
+
const msgs = await client.session.messages({ sessionID })
|
|
133
|
+
const assistantMsg = msgs.data?.find((m) => m.info.role === 'assistant')
|
|
134
|
+
const hasTextParts = assistantMsg?.parts?.some((p) => {
|
|
135
|
+
return p.type === 'text' && p.text && !p.synthetic
|
|
136
|
+
})
|
|
137
|
+
if (hasTextParts) {
|
|
138
|
+
// Extra wait for step-start and other parts to be flushed
|
|
139
|
+
await new Promise((resolve) => {
|
|
140
|
+
setTimeout(resolve, 500)
|
|
141
|
+
})
|
|
142
|
+
break
|
|
143
|
+
}
|
|
144
|
+
await new Promise((resolve) => {
|
|
145
|
+
setTimeout(resolve, 200)
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
}, 60_000)
|
|
149
|
+
|
|
150
|
+
afterAll(async () => {
|
|
151
|
+
if (directories) {
|
|
152
|
+
await cleanupTestSessions({
|
|
153
|
+
projectDirectory: directories.projectDirectory,
|
|
154
|
+
testStartTime,
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
await stopOpencodeServer()
|
|
158
|
+
if (directories) {
|
|
159
|
+
fs.rmSync(directories.dataDir, { recursive: true, force: true })
|
|
160
|
+
}
|
|
161
|
+
}, 10_000)
|
|
162
|
+
|
|
163
|
+
// Strip dynamic parts (timestamps, durations, branch names) for stable assertions
|
|
164
|
+
function normalizeMarkdown(md: string): string {
|
|
165
|
+
return md
|
|
166
|
+
// Normalize "Completed in Xs" to a fixed string
|
|
167
|
+
.replace(/\*Completed in [\d.]+[ms]+\*/g, '*Completed in Xs*')
|
|
168
|
+
// Normalize "Duration: Xs" tool timing
|
|
169
|
+
.replace(/\*Duration: [\d.]+[ms]+\*/g, '*Duration: Xs*')
|
|
170
|
+
// Normalize ISO dates in session info
|
|
171
|
+
.replace(/\*\*Created\*\*: .+/g, '**Created**: <date>')
|
|
172
|
+
.replace(/\*\*Updated\*\*: .+/g, '**Updated**: <date>')
|
|
173
|
+
// Normalize opencode version
|
|
174
|
+
.replace(/\*\*OpenCode Version\*\*: v[\d.]+.*/g, '**OpenCode Version**: v<version>')
|
|
175
|
+
// Strip git branch context injected by opencode into user messages
|
|
176
|
+
.replace(/\[Current branch: [^\]]+\]\n?\n?/g, '')
|
|
177
|
+
.replace(/\[current git branch is [^\]]+\]\n?\n?/g, '')
|
|
178
|
+
.replace(/\[warning: repository is in detached HEAD[^\]]*\]\n?\n?/g, '')
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
test('generate markdown with system info', async () => {
|
|
182
|
+
const exporter = new ShareMarkdown(client)
|
|
183
|
+
|
|
184
|
+
const markdownResult = await exporter.generate({
|
|
185
|
+
sessionID,
|
|
186
|
+
includeSystemInfo: true,
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
expect(errore.isOk(markdownResult)).toBe(true)
|
|
190
|
+
const markdown = errore.unwrap(markdownResult)
|
|
191
|
+
|
|
192
|
+
expect(markdown).toContain('# Markdown Test Session')
|
|
193
|
+
expect(markdown).toContain('## Session Information')
|
|
194
|
+
expect(markdown).toContain('## Conversation')
|
|
195
|
+
expect(markdown).toContain('### 👤 User')
|
|
196
|
+
expect(markdown).toContain('hello markdown test')
|
|
197
|
+
expect(markdown).toContain('### 🤖 Assistant')
|
|
198
|
+
expect(markdown).toContain('Hello! This is a deterministic markdown test response.')
|
|
199
|
+
expect(markdown).toContain('**Started using deterministic-provider/deterministic-v2**')
|
|
200
|
+
|
|
201
|
+
const normalized = normalizeMarkdown(markdown)
|
|
202
|
+
expect(normalized).toMatchInlineSnapshot(`
|
|
203
|
+
"# Markdown Test Session
|
|
204
|
+
|
|
205
|
+
## Session Information
|
|
206
|
+
|
|
207
|
+
- **Created**: <date>
|
|
208
|
+
- **Updated**: <date>
|
|
209
|
+
- **OpenCode Version**: v<version>
|
|
210
|
+
|
|
211
|
+
## Conversation
|
|
212
|
+
|
|
213
|
+
### 👤 User
|
|
214
|
+
|
|
215
|
+
hello markdown test
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
### 🤖 Assistant (deterministic-v2)
|
|
219
|
+
|
|
220
|
+
**Started using deterministic-provider/deterministic-v2**
|
|
221
|
+
|
|
222
|
+
Hello! This is a deterministic markdown test response.
|
|
223
|
+
|
|
224
|
+
"
|
|
225
|
+
`)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
test('generate markdown without system info', async () => {
|
|
229
|
+
const exporter = new ShareMarkdown(client)
|
|
230
|
+
|
|
231
|
+
const markdown = await exporter.generate({
|
|
232
|
+
sessionID,
|
|
233
|
+
includeSystemInfo: false,
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
expect(errore.isOk(markdown)).toBe(true)
|
|
237
|
+
const md = errore.unwrap(markdown as string)
|
|
238
|
+
expect(md).toContain('# Markdown Test Session')
|
|
239
|
+
expect(md).not.toContain('## Session Information')
|
|
240
|
+
expect(md).toContain('## Conversation')
|
|
241
|
+
|
|
242
|
+
const normalized = normalizeMarkdown(md)
|
|
243
|
+
expect(normalized).toMatchInlineSnapshot(`
|
|
244
|
+
"# Markdown Test Session
|
|
245
|
+
|
|
246
|
+
## Conversation
|
|
247
|
+
|
|
248
|
+
### 👤 User
|
|
249
|
+
|
|
250
|
+
hello markdown test
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
### 🤖 Assistant (deterministic-v2)
|
|
254
|
+
|
|
255
|
+
**Started using deterministic-provider/deterministic-v2**
|
|
256
|
+
|
|
257
|
+
Hello! This is a deterministic markdown test response.
|
|
258
|
+
|
|
259
|
+
"
|
|
260
|
+
`)
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
test('error handling for non-existent session', async () => {
|
|
264
|
+
const exporter = new ShareMarkdown(client)
|
|
265
|
+
const badSessionID = 'ses_nonexistent_' + Date.now()
|
|
266
|
+
|
|
267
|
+
const result = await exporter.generate({ sessionID: badSessionID })
|
|
268
|
+
expect(result).toBeInstanceOf(Error)
|
|
269
|
+
expect((result as Error).message).toContain(`Session ${badSessionID} not found`)
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
test('getCompactSessionContext generates compact format', async () => {
|
|
273
|
+
const contextResult = await getCompactSessionContext({
|
|
274
|
+
client,
|
|
275
|
+
sessionId: sessionID,
|
|
276
|
+
includeSystemPrompt: false,
|
|
277
|
+
maxMessages: 10,
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
expect(errore.isOk(contextResult)).toBe(true)
|
|
281
|
+
const context = errore.unwrap(contextResult)
|
|
282
|
+
|
|
283
|
+
expect(context).toBeTruthy()
|
|
284
|
+
// User text may be prefixed with branch context injected by opencode
|
|
285
|
+
expect(context).toContain('hello markdown test')
|
|
286
|
+
expect(context).toContain('[User]:')
|
|
287
|
+
expect(context).toContain('[Assistant]:')
|
|
288
|
+
expect(context).toContain('Hello! This is a deterministic markdown test response.')
|
|
289
|
+
expect(context).not.toContain('[System Prompt]')
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
test('generate markdown with lastAssistantOnly', async () => {
|
|
293
|
+
const exporter = new ShareMarkdown(client)
|
|
294
|
+
|
|
295
|
+
const markdownResult = await exporter.generate({
|
|
296
|
+
sessionID,
|
|
297
|
+
lastAssistantOnly: true,
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
expect(errore.isOk(markdownResult)).toBe(true)
|
|
301
|
+
const markdown = errore.unwrap(markdownResult)
|
|
302
|
+
|
|
303
|
+
// lastAssistantOnly should NOT include title header or conversation section header
|
|
304
|
+
expect(markdown).not.toContain('# Markdown Test Session')
|
|
305
|
+
expect(markdown).not.toContain('## Conversation')
|
|
306
|
+
// Should contain the assistant response
|
|
307
|
+
expect(markdown).toContain('Hello! This is a deterministic markdown test response.')
|
|
308
|
+
})
|