@theowlops/boombot 0.0.0-stage → 1.1.0
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/README.md +69 -3
- package/dist/LICENSE +21 -0
- package/dist/bin/boombot.js +16 -0
- package/dist/cli/api-key.js +123 -0
- package/dist/cli/bin-bun.js +17 -0
- package/dist/cli/bin.js +34 -0
- package/dist/cli/cli/cli-error.js +61 -0
- package/dist/cli/cli/dispatch.js +138 -0
- package/dist/cli/cli/help.js +83 -0
- package/dist/cli/cli/parser.js +89 -0
- package/dist/cli/cli/registry.js +185 -0
- package/dist/cli/cli/spec.js +41 -0
- package/dist/cli/cli/suggest.js +125 -0
- package/dist/cli/cli/version.js +30 -0
- package/dist/cli/cli/vocabulary.js +87 -0
- package/dist/cli/commands/doctor.js +365 -0
- package/dist/cli/commands/gateway.js +290 -0
- package/dist/cli/commands/migrate.js +270 -0
- package/dist/cli/commands/plugin.js +97 -0
- package/dist/cli/commands/setup.js +230 -0
- package/dist/cli/config-file.js +133 -0
- package/dist/cli/master-key.js +78 -0
- package/dist/cli/open-browser.js +59 -0
- package/dist/cli/web-assets.js +58 -0
- package/dist/gateway/auth.js +46 -0
- package/dist/gateway/config-format.js +202 -0
- package/dist/gateway/config.js +427 -0
- package/dist/gateway/context.js +1 -0
- package/dist/gateway/env-file.js +129 -0
- package/dist/gateway/events.js +172 -0
- package/dist/gateway/guard/tagging.js +505 -0
- package/dist/gateway/handlers/index.js +66 -0
- package/dist/gateway/handlers/memory.js +54 -0
- package/dist/gateway/handlers/providers.js +73 -0
- package/dist/gateway/handlers/registry.js +39 -0
- package/dist/gateway/handlers/sessions.js +188 -0
- package/dist/gateway/handlers/system.js +12 -0
- package/dist/gateway/handlers/tasks.js +191 -0
- package/dist/gateway/handlers/turn.js +100 -0
- package/dist/gateway/index.js +126 -0
- package/dist/gateway/main.js +23 -0
- package/dist/gateway/server.js +354 -0
- package/dist/gateway/services/context-limits.js +75 -0
- package/dist/gateway/services/history-normalizer.js +42 -0
- package/dist/gateway/services/image-pipeline.js +56 -0
- package/dist/gateway/services/key-roulette-client.js +43 -0
- package/dist/gateway/services/llm-client.js +593 -0
- package/dist/gateway/services/llm-decoder.js +200 -0
- package/dist/gateway/services/local-fallback.js +135 -0
- package/dist/gateway/services/memory.js +511 -0
- package/dist/gateway/services/persistent-memory.js +196 -0
- package/dist/gateway/services/providers.js +44 -0
- package/dist/gateway/services/tasks.js +237 -0
- package/dist/gateway/services/verification.js +124 -0
- package/dist/gateway/services/vision-payload.js +48 -0
- package/dist/gateway/services.js +257 -0
- package/dist/gateway/session-snapshot.js +19 -0
- package/dist/gateway/sessions.js +701 -0
- package/dist/gateway/static.js +239 -0
- package/dist/gateway/turns/clarify-tool.js +57 -0
- package/dist/gateway/turns/cronjob-tool.js +96 -0
- package/dist/gateway/turns/deferred-tools.js +154 -0
- package/dist/gateway/turns/execute-code-tool.js +84 -0
- package/dist/gateway/turns/file-tool-defs.js +178 -0
- package/dist/gateway/turns/file-tool-ops.js +178 -0
- package/dist/gateway/turns/file-tool-scan.js +182 -0
- package/dist/gateway/turns/file-tool.js +302 -0
- package/dist/gateway/turns/index.js +416 -0
- package/dist/gateway/turns/jev-middleware.js +91 -0
- package/dist/gateway/turns/local-reply.js +156 -0
- package/dist/gateway/turns/mcp-client.js +643 -0
- package/dist/gateway/turns/memory-tool.js +171 -0
- package/dist/gateway/turns/middleware.js +96 -0
- package/dist/gateway/turns/python-core.js +397 -0
- package/dist/gateway/turns/routing/index.js +3 -0
- package/dist/gateway/turns/routing/intent-classifier.js +60 -0
- package/dist/gateway/turns/routing/planner-adapter.js +91 -0
- package/dist/gateway/turns/routing/types.js +1 -0
- package/dist/gateway/turns/runtime-deps.js +228 -0
- package/dist/gateway/turns/self-config-tool.js +277 -0
- package/dist/gateway/turns/service-types.js +11 -0
- package/dist/gateway/turns/service.js +820 -0
- package/dist/gateway/turns/session-adapter.js +158 -0
- package/dist/gateway/turns/session-tool.js +49 -0
- package/dist/gateway/turns/shell-tool.js +190 -0
- package/dist/gateway/turns/skill-tool.js +239 -0
- package/dist/gateway/turns/spillover.js +201 -0
- package/dist/gateway/turns/subagent-tool.js +140 -0
- package/dist/gateway/turns/system-prompt.js +121 -0
- package/dist/gateway/turns/tool-schema.js +90 -0
- package/dist/gateway/turns/turn-utils.js +58 -0
- package/dist/gateway/turns/vision-tool.js +120 -0
- package/dist/gateway/types.js +1 -0
- package/dist/native/win32-x64/boombot-native-host.exe +0 -0
- package/dist/node_modules/@boombot/channels/handlers.d.ts +9 -0
- package/dist/node_modules/@boombot/channels/handlers.js +109 -0
- package/dist/node_modules/@boombot/channels/index.d.ts +12 -0
- package/dist/node_modules/@boombot/channels/index.js +12 -0
- package/dist/node_modules/@boombot/channels/package.json +9 -0
- package/dist/node_modules/@boombot/channels/service.d.ts +62 -0
- package/dist/node_modules/@boombot/channels/service.js +608 -0
- package/dist/node_modules/@boombot/channels/tool.d.ts +10 -0
- package/dist/node_modules/@boombot/channels/tool.js +46 -0
- package/dist/node_modules/@boombot/channels/transports/common/dedup.d.ts +17 -0
- package/dist/node_modules/@boombot/channels/transports/common/dedup.js +42 -0
- package/dist/node_modules/@boombot/channels/transports/common/handoff.d.ts +19 -0
- package/dist/node_modules/@boombot/channels/transports/common/handoff.js +32 -0
- package/dist/node_modules/@boombot/channels/transports/common/index.d.ts +3 -0
- package/dist/node_modules/@boombot/channels/transports/common/index.js +3 -0
- package/dist/node_modules/@boombot/channels/transports/common/types.d.ts +99 -0
- package/dist/node_modules/@boombot/channels/transports/common/types.js +54 -0
- package/dist/node_modules/@boombot/channels/transports/common.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/common.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/dedup/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/dedup/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/dedup.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/dedup.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/discord/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/discord/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/discord/transport.d.ts +36 -0
- package/dist/node_modules/@boombot/channels/transports/discord/transport.js +199 -0
- package/dist/node_modules/@boombot/channels/transports/discord.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/discord.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/handoff.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/handoff.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/messenger/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/messenger/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/messenger/transport.d.ts +34 -0
- package/dist/node_modules/@boombot/channels/transports/messenger/transport.js +180 -0
- package/dist/node_modules/@boombot/channels/transports/messenger.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/messenger.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/slack/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/slack/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/slack/transport.d.ts +35 -0
- package/dist/node_modules/@boombot/channels/transports/slack/transport.js +183 -0
- package/dist/node_modules/@boombot/channels/transports/slack.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/slack.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/streamer/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/streamer/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/streamer/streamer.d.ts +31 -0
- package/dist/node_modules/@boombot/channels/transports/streamer/streamer.js +141 -0
- package/dist/node_modules/@boombot/channels/transports/streamer.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/streamer.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/telegram/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/telegram/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/telegram/transport.d.ts +75 -0
- package/dist/node_modules/@boombot/channels/transports/telegram/transport.js +395 -0
- package/dist/node_modules/@boombot/channels/transports/telegram.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/telegram.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/tiktok/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/tiktok/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/tiktok/transport.d.ts +27 -0
- package/dist/node_modules/@boombot/channels/transports/tiktok/transport.js +97 -0
- package/dist/node_modules/@boombot/channels/transports/tiktok.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/tiktok.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/whatsapp/index.d.ts +1 -0
- package/dist/node_modules/@boombot/channels/transports/whatsapp/index.js +1 -0
- package/dist/node_modules/@boombot/channels/transports/whatsapp/transport.d.ts +38 -0
- package/dist/node_modules/@boombot/channels/transports/whatsapp/transport.js +189 -0
- package/dist/node_modules/@boombot/channels/transports/whatsapp.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/whatsapp.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/zalo/index.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/zalo/index.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/zalo/oa.d.ts +28 -0
- package/dist/node_modules/@boombot/channels/transports/zalo/oa.js +94 -0
- package/dist/node_modules/@boombot/channels/transports/zalo/personal.d.ts +106 -0
- package/dist/node_modules/@boombot/channels/transports/zalo/personal.js +171 -0
- package/dist/node_modules/@boombot/channels/transports/zalo-personal.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/zalo-personal.js +2 -0
- package/dist/node_modules/@boombot/channels/transports/zalo.d.ts +2 -0
- package/dist/node_modules/@boombot/channels/transports/zalo.js +2 -0
- package/dist/node_modules/@boombot/core/agent-core/index.d.ts +12 -0
- package/dist/node_modules/@boombot/core/agent-core/index.js +12 -0
- package/dist/node_modules/@boombot/core/agent-core/middleware.d.ts +60 -0
- package/dist/node_modules/@boombot/core/agent-core/middleware.js +152 -0
- package/dist/node_modules/@boombot/core/agent-core/routing.d.ts +128 -0
- package/dist/node_modules/@boombot/core/agent-core/routing.js +437 -0
- package/dist/node_modules/@boombot/core/agent-core/runtime-deps.d.ts +88 -0
- package/dist/node_modules/@boombot/core/agent-core/runtime-deps.js +8 -0
- package/dist/node_modules/@boombot/core/agent-core/tool-errors.d.ts +44 -0
- package/dist/node_modules/@boombot/core/agent-core/tool-errors.js +60 -0
- package/dist/node_modules/@boombot/core/agent-core/turn-loop.d.ts +21 -0
- package/dist/node_modules/@boombot/core/agent-core/turn-loop.js +561 -0
- package/dist/node_modules/@boombot/core/agent-core/types.d.ts +189 -0
- package/dist/node_modules/@boombot/core/agent-core/types.js +7 -0
- package/dist/node_modules/@boombot/core/compaction/compactor.d.ts +19 -0
- package/dist/node_modules/@boombot/core/compaction/compactor.js +127 -0
- package/dist/node_modules/@boombot/core/compaction/estimator.d.ts +10 -0
- package/dist/node_modules/@boombot/core/compaction/estimator.js +48 -0
- package/dist/node_modules/@boombot/core/compaction/index.d.ts +3 -0
- package/dist/node_modules/@boombot/core/compaction/index.js +3 -0
- package/dist/node_modules/@boombot/core/compaction/types.d.ts +31 -0
- package/dist/node_modules/@boombot/core/compaction/types.js +7 -0
- package/dist/node_modules/@boombot/core/index.d.ts +9 -0
- package/dist/node_modules/@boombot/core/index.js +9 -0
- package/dist/node_modules/@boombot/core/jev/index.d.ts +2 -0
- package/dist/node_modules/@boombot/core/jev/index.js +2 -0
- package/dist/node_modules/@boombot/core/jev/jev.d.ts +30 -0
- package/dist/node_modules/@boombot/core/jev/jev.js +164 -0
- package/dist/node_modules/@boombot/core/jev/types.d.ts +22 -0
- package/dist/node_modules/@boombot/core/jev/types.js +1 -0
- package/dist/node_modules/@boombot/core/memory/consolidation/budget.d.ts +63 -0
- package/dist/node_modules/@boombot/core/memory/consolidation/budget.js +94 -0
- package/dist/node_modules/@boombot/core/memory/consolidation/consolidator.d.ts +152 -0
- package/dist/node_modules/@boombot/core/memory/consolidation/consolidator.js +318 -0
- package/dist/node_modules/@boombot/core/memory/consolidation/index.d.ts +12 -0
- package/dist/node_modules/@boombot/core/memory/consolidation/index.js +12 -0
- package/dist/node_modules/@boombot/core/memory/curation.d.ts +86 -0
- package/dist/node_modules/@boombot/core/memory/curation.js +120 -0
- package/dist/node_modules/@boombot/core/memory/errors.d.ts +12 -0
- package/dist/node_modules/@boombot/core/memory/errors.js +20 -0
- package/dist/node_modules/@boombot/core/memory/hybrid-search.d.ts +29 -0
- package/dist/node_modules/@boombot/core/memory/hybrid-search.js +52 -0
- package/dist/node_modules/@boombot/core/memory/index.d.ts +23 -0
- package/dist/node_modules/@boombot/core/memory/index.js +23 -0
- package/dist/node_modules/@boombot/core/memory/indexer.d.ts +47 -0
- package/dist/node_modules/@boombot/core/memory/indexer.js +138 -0
- package/dist/node_modules/@boombot/core/memory/lifecycle.d.ts +12 -0
- package/dist/node_modules/@boombot/core/memory/lifecycle.js +66 -0
- package/dist/node_modules/@boombot/core/memory/provider.d.ts +84 -0
- package/dist/node_modules/@boombot/core/memory/provider.js +117 -0
- package/dist/node_modules/@boombot/core/memory/recall.d.ts +7 -0
- package/dist/node_modules/@boombot/core/memory/recall.js +111 -0
- package/dist/node_modules/@boombot/core/memory/scope-resolver.d.ts +27 -0
- package/dist/node_modules/@boombot/core/memory/scope-resolver.js +60 -0
- package/dist/node_modules/@boombot/core/memory/search.d.ts +37 -0
- package/dist/node_modules/@boombot/core/memory/search.js +158 -0
- package/dist/node_modules/@boombot/core/memory/store.d.ts +60 -0
- package/dist/node_modules/@boombot/core/memory/store.js +289 -0
- package/dist/node_modules/@boombot/core/memory/types.d.ts +121 -0
- package/dist/node_modules/@boombot/core/memory/types.js +21 -0
- package/dist/node_modules/@boombot/core/memory/vector-index.d.ts +34 -0
- package/dist/node_modules/@boombot/core/memory/vector-index.js +65 -0
- package/dist/node_modules/@boombot/core/package.json +9 -0
- package/dist/node_modules/@boombot/core/reflection/classifier.d.ts +13 -0
- package/dist/node_modules/@boombot/core/reflection/classifier.js +77 -0
- package/dist/node_modules/@boombot/core/reflection/index.d.ts +2 -0
- package/dist/node_modules/@boombot/core/reflection/index.js +2 -0
- package/dist/node_modules/@boombot/core/reflection/types.d.ts +13 -0
- package/dist/node_modules/@boombot/core/reflection/types.js +4 -0
- package/dist/node_modules/@boombot/core/research/config.d.ts +89 -0
- package/dist/node_modules/@boombot/core/research/config.js +206 -0
- package/dist/node_modules/@boombot/core/research/crawl.d.ts +36 -0
- package/dist/node_modules/@boombot/core/research/crawl.js +179 -0
- package/dist/node_modules/@boombot/core/research/factory.d.ts +26 -0
- package/dist/node_modules/@boombot/core/research/factory.js +46 -0
- package/dist/node_modules/@boombot/core/research/http.d.ts +87 -0
- package/dist/node_modules/@boombot/core/research/http.js +250 -0
- package/dist/node_modules/@boombot/core/research/index.d.ts +17 -0
- package/dist/node_modules/@boombot/core/research/index.js +10 -0
- package/dist/node_modules/@boombot/core/research/orchestrator.d.ts +113 -0
- package/dist/node_modules/@boombot/core/research/orchestrator.js +252 -0
- package/dist/node_modules/@boombot/core/research/parse/docx.d.ts +24 -0
- package/dist/node_modules/@boombot/core/research/parse/docx.js +183 -0
- package/dist/node_modules/@boombot/core/research/parse/guards.d.ts +89 -0
- package/dist/node_modules/@boombot/core/research/parse/guards.js +171 -0
- package/dist/node_modules/@boombot/core/research/parse/index.d.ts +68 -0
- package/dist/node_modules/@boombot/core/research/parse/index.js +219 -0
- package/dist/node_modules/@boombot/core/research/parse/pdf.d.ts +38 -0
- package/dist/node_modules/@boombot/core/research/parse/pdf.js +567 -0
- package/dist/node_modules/@boombot/core/research/providers/arxiv.d.ts +9 -0
- package/dist/node_modules/@boombot/core/research/providers/arxiv.js +72 -0
- package/dist/node_modules/@boombot/core/research/providers/brave.d.ts +11 -0
- package/dist/node_modules/@boombot/core/research/providers/brave.js +36 -0
- package/dist/node_modules/@boombot/core/research/providers/duckduckgo.d.ts +9 -0
- package/dist/node_modules/@boombot/core/research/providers/duckduckgo.js +78 -0
- package/dist/node_modules/@boombot/core/research/providers/exa.d.ts +11 -0
- package/dist/node_modules/@boombot/core/research/providers/exa.js +34 -0
- package/dist/node_modules/@boombot/core/research/providers/firecrawl.d.ts +16 -0
- package/dist/node_modules/@boombot/core/research/providers/firecrawl.js +47 -0
- package/dist/node_modules/@boombot/core/research/providers/index.d.ts +17 -0
- package/dist/node_modules/@boombot/core/research/providers/index.js +11 -0
- package/dist/node_modules/@boombot/core/research/providers/jina.d.ts +13 -0
- package/dist/node_modules/@boombot/core/research/providers/jina.js +114 -0
- package/dist/node_modules/@boombot/core/research/providers/openalex.d.ts +14 -0
- package/dist/node_modules/@boombot/core/research/providers/openalex.js +66 -0
- package/dist/node_modules/@boombot/core/research/providers/semantic-scholar.d.ts +9 -0
- package/dist/node_modules/@boombot/core/research/providers/semantic-scholar.js +56 -0
- package/dist/node_modules/@boombot/core/research/providers/tavily.d.ts +11 -0
- package/dist/node_modules/@boombot/core/research/providers/tavily.js +35 -0
- package/dist/node_modules/@boombot/core/research/providers/wikipedia.d.ts +12 -0
- package/dist/node_modules/@boombot/core/research/providers/wikipedia.js +47 -0
- package/dist/node_modules/@boombot/core/research/tools.d.ts +27 -0
- package/dist/node_modules/@boombot/core/research/tools.js +251 -0
- package/dist/node_modules/@boombot/core/research/types.d.ts +133 -0
- package/dist/node_modules/@boombot/core/research/types.js +8 -0
- package/dist/node_modules/@boombot/core/research/wiring.d.ts +25 -0
- package/dist/node_modules/@boombot/core/research/wiring.js +51 -0
- package/dist/node_modules/@boombot/core/scheduler/cron.d.ts +34 -0
- package/dist/node_modules/@boombot/core/scheduler/cron.js +147 -0
- package/dist/node_modules/@boombot/core/scheduler/evaluator.d.ts +42 -0
- package/dist/node_modules/@boombot/core/scheduler/evaluator.js +148 -0
- package/dist/node_modules/@boombot/core/scheduler/index.d.ts +15 -0
- package/dist/node_modules/@boombot/core/scheduler/index.js +15 -0
- package/dist/node_modules/@boombot/core/scheduler/registry.d.ts +81 -0
- package/dist/node_modules/@boombot/core/scheduler/registry.js +263 -0
- package/dist/node_modules/@boombot/core/scheduler/runner.d.ts +59 -0
- package/dist/node_modules/@boombot/core/scheduler/runner.js +257 -0
- package/dist/node_modules/@boombot/core/scheduler/store.d.ts +150 -0
- package/dist/node_modules/@boombot/core/scheduler/store.js +526 -0
- package/dist/node_modules/@boombot/core/scheduler/timezone.d.ts +35 -0
- package/dist/node_modules/@boombot/core/scheduler/timezone.js +96 -0
- package/dist/node_modules/@boombot/core/scheduler/types.d.ts +157 -0
- package/dist/node_modules/@boombot/core/scheduler/types.js +89 -0
- package/dist/node_modules/@boombot/core/task-runner/index.d.ts +7 -0
- package/dist/node_modules/@boombot/core/task-runner/index.js +5 -0
- package/dist/node_modules/@boombot/core/task-runner/planner.d.ts +37 -0
- package/dist/node_modules/@boombot/core/task-runner/planner.js +154 -0
- package/dist/node_modules/@boombot/core/task-runner/runner.d.ts +8 -0
- package/dist/node_modules/@boombot/core/task-runner/runner.js +564 -0
- package/dist/node_modules/@boombot/core/task-runner/store.d.ts +28 -0
- package/dist/node_modules/@boombot/core/task-runner/store.js +126 -0
- package/dist/node_modules/@boombot/core/task-runner/types.d.ts +138 -0
- package/dist/node_modules/@boombot/core/task-runner/types.js +11 -0
- package/dist/node_modules/@boombot/core/workflow/dag.d.ts +16 -0
- package/dist/node_modules/@boombot/core/workflow/dag.js +109 -0
- package/dist/node_modules/@boombot/core/workflow/engine.d.ts +34 -0
- package/dist/node_modules/@boombot/core/workflow/engine.js +304 -0
- package/dist/node_modules/@boombot/core/workflow/index.d.ts +3 -0
- package/dist/node_modules/@boombot/core/workflow/index.js +3 -0
- package/dist/node_modules/@boombot/core/workflow/types.d.ts +98 -0
- package/dist/node_modules/@boombot/core/workflow/types.js +21 -0
- package/dist/node_modules/@boombot/cronjobs/handlers.d.ts +11 -0
- package/dist/node_modules/@boombot/cronjobs/handlers.js +174 -0
- package/dist/node_modules/@boombot/cronjobs/index.d.ts +2 -0
- package/dist/node_modules/@boombot/cronjobs/index.js +2 -0
- package/dist/node_modules/@boombot/cronjobs/package.json +9 -0
- package/dist/node_modules/@boombot/cronjobs/service.d.ts +53 -0
- package/dist/node_modules/@boombot/cronjobs/service.js +200 -0
- package/dist/node_modules/@boombot/plugins/index.d.ts +2 -0
- package/dist/node_modules/@boombot/plugins/index.js +2 -0
- package/dist/node_modules/@boombot/plugins/installer.d.ts +45 -0
- package/dist/node_modules/@boombot/plugins/installer.js +487 -0
- package/dist/node_modules/@boombot/plugins/manager.d.ts +23 -0
- package/dist/node_modules/@boombot/plugins/manager.js +243 -0
- package/dist/node_modules/@boombot/plugins/package.json +9 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-channels.d.ts +125 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-channels.js +76 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-plugins.d.ts +220 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-plugins.js +60 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-scheduler.d.ts +33 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-scheduler.js +25 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands.d.ts +4349 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/commands.js +563 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/envelope.d.ts +32 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/envelope.js +61 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/errors.d.ts +13 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/errors.js +16 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/events.d.ts +567 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/events.js +395 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/index.d.ts +20 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/index.js +13 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/types.d.ts +91 -0
- package/dist/node_modules/@boombot/protocol/gateway-protocol/types.js +7 -0
- package/dist/node_modules/@boombot/protocol/index.d.ts +4 -0
- package/dist/node_modules/@boombot/protocol/index.js +3 -0
- package/dist/node_modules/@boombot/protocol/package.json +9 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/channel.d.ts +41 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/channel.js +17 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/index.d.ts +10 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/index.js +10 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/manifest.d.ts +58 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/manifest.js +116 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/plugin.d.ts +25 -0
- package/dist/node_modules/@boombot/protocol/plugin-sdk/plugin.js +1 -0
- package/dist/node_modules/@boombot/protocol/providers/cipher.d.ts +7 -0
- package/dist/node_modules/@boombot/protocol/providers/cipher.js +52 -0
- package/dist/node_modules/@boombot/protocol/providers/index.d.ts +19 -0
- package/dist/node_modules/@boombot/protocol/providers/index.js +19 -0
- package/dist/node_modules/@boombot/protocol/providers/key-roulette.d.ts +79 -0
- package/dist/node_modules/@boombot/protocol/providers/key-roulette.js +81 -0
- package/dist/node_modules/@boombot/protocol/providers/probe.d.ts +29 -0
- package/dist/node_modules/@boombot/protocol/providers/probe.js +96 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/anthropic.d.ts +11 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/anthropic.js +11 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/custom.d.ts +22 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/custom.js +222 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/deepseek.d.ts +7 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/deepseek.js +9 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/google.d.ts +10 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/google.js +10 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/index.d.ts +31 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/index.js +56 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/local.d.ts +10 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/local.js +11 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/mistral.d.ts +7 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/mistral.js +9 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/openai-compatible.d.ts +10 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/openai-compatible.js +10 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/openai.d.ts +7 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/openai.js +11 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/types.d.ts +58 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/types.js +1 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/xai.d.ts +7 -0
- package/dist/node_modules/@boombot/protocol/providers/profiles/xai.js +10 -0
- package/dist/node_modules/@boombot/protocol/providers/request-profile.d.ts +102 -0
- package/dist/node_modules/@boombot/protocol/providers/request-profile.js +118 -0
- package/dist/node_modules/@boombot/protocol/providers/service.d.ts +2 -0
- package/dist/node_modules/@boombot/protocol/providers/service.js +418 -0
- package/dist/node_modules/@boombot/protocol/providers/types.d.ts +141 -0
- package/dist/node_modules/@boombot/protocol/providers/types.js +16 -0
- package/dist/node_modules/@boombot/sandbox/fs-path.d.ts +13 -0
- package/dist/node_modules/@boombot/sandbox/fs-path.js +79 -0
- package/dist/node_modules/@boombot/sandbox/index.d.ts +13 -0
- package/dist/node_modules/@boombot/sandbox/index.js +13 -0
- package/dist/node_modules/@boombot/sandbox/native.d.ts +39 -0
- package/dist/node_modules/@boombot/sandbox/native.js +224 -0
- package/dist/node_modules/@boombot/sandbox/package.json +9 -0
- package/dist/node_modules/@boombot/sandbox/sandbox.d.ts +92 -0
- package/dist/node_modules/@boombot/sandbox/sandbox.js +229 -0
- package/dist/node_modules/@boombot/security/harness-guard/boundary.d.ts +22 -0
- package/dist/node_modules/@boombot/security/harness-guard/boundary.js +105 -0
- package/dist/node_modules/@boombot/security/harness-guard/gates.d.ts +90 -0
- package/dist/node_modules/@boombot/security/harness-guard/gates.js +791 -0
- package/dist/node_modules/@boombot/security/harness-guard/index.d.ts +23 -0
- package/dist/node_modules/@boombot/security/harness-guard/index.js +23 -0
- package/dist/node_modules/@boombot/security/harness-guard/policy.d.ts +20 -0
- package/dist/node_modules/@boombot/security/harness-guard/policy.js +70 -0
- package/dist/node_modules/@boombot/security/harness-guard/sanitizer.d.ts +53 -0
- package/dist/node_modules/@boombot/security/harness-guard/sanitizer.js +145 -0
- package/dist/node_modules/@boombot/security/harness-guard/types.d.ts +71 -0
- package/dist/node_modules/@boombot/security/harness-guard/types.js +10 -0
- package/dist/node_modules/@boombot/security/harness-guard/verifier.d.ts +59 -0
- package/dist/node_modules/@boombot/security/harness-guard/verifier.js +169 -0
- package/dist/node_modules/@boombot/security/index.d.ts +5 -0
- package/dist/node_modules/@boombot/security/index.js +9 -0
- package/dist/node_modules/@boombot/security/package.json +9 -0
- package/dist/node_modules/@boombot/security/policy/approval.d.ts +117 -0
- package/dist/node_modules/@boombot/security/policy/approval.js +223 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/claim-extractor.d.ts +10 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/claim-extractor.js +124 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/evidence-ledger.d.ts +25 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/evidence-ledger.js +70 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/gate.d.ts +38 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/gate.js +61 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/grounding-check.d.ts +12 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/grounding-check.js +234 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/index.d.ts +17 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/index.js +13 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/policy.d.ts +15 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/policy.js +39 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/types.d.ts +46 -0
- package/dist/node_modules/@boombot/security/reasoning-guard/types.js +7 -0
- package/dist/node_modules/@boombot/security/security/approval.d.ts +63 -0
- package/dist/node_modules/@boombot/security/security/approval.js +140 -0
- package/dist/node_modules/@boombot/security/security/audit.d.ts +39 -0
- package/dist/node_modules/@boombot/security/security/audit.js +70 -0
- package/dist/node_modules/@boombot/security/security/index.d.ts +12 -0
- package/dist/node_modules/@boombot/security/security/index.js +12 -0
- package/dist/node_modules/@boombot/security/security/trust.d.ts +79 -0
- package/dist/node_modules/@boombot/security/security/trust.js +87 -0
- package/dist/package.json +5 -0
- package/dist/proto/agent_core.proto +56 -0
- package/dist/python/agent-core-python/pyproject.toml +40 -0
- package/dist/python/agent-core-python/src/boombot_agent/__init__.py +5 -0
- package/dist/python/agent-core-python/src/boombot_agent/generated/__init__.py +0 -0
- package/dist/python/agent-core-python/src/boombot_agent/generated/agent_core_pb2.py +54 -0
- package/dist/python/agent-core-python/src/boombot_agent/generated/agent_core_pb2_grpc.py +140 -0
- package/dist/python/agent-core-python/src/boombot_agent/rpc.py +265 -0
- package/dist/python/agent-core-python/src/boombot_agent/turn.py +577 -0
- package/dist/python/agent-core-python/src/boombot_ml/__init__.py +12 -0
- package/dist/python/agent-core-python/src/boombot_ml/embeddings.py +58 -0
- package/dist/python/agent-core-python/src/boombot_ml/training.py +156 -0
- package/dist/python/agent-core-python/uv.lock +2856 -0
- package/dist/web/assets/ChatView-BZrSD-64.js +262 -0
- package/dist/web/assets/ChatView-Cahbjez6.css +1 -0
- package/dist/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/web/assets/MemoryView-BJbXqqs7.css +1 -0
- package/dist/web/assets/MemoryView-BfMq_6CD.js +1 -0
- package/dist/web/assets/SchedulesView-B22QmPR-.js +1 -0
- package/dist/web/assets/SchedulesView-C6XK7A8a.css +1 -0
- package/dist/web/assets/SessionsView-C1dqdhxI.css +1 -0
- package/dist/web/assets/SessionsView-DS0-aH2a.js +1 -0
- package/dist/web/assets/TasksView-Dtasg4hd.css +1 -0
- package/dist/web/assets/TasksView-ey0FLsW8.js +2 -0
- package/dist/web/assets/Textarea-DN3Zybl1.js +1 -0
- package/dist/web/assets/Tooltip-Bn2cqFPz.css +1 -0
- package/dist/web/assets/Tooltip-MzV2Gqnc.js +1 -0
- package/dist/web/assets/browser-Bnt73-Ub.js +8 -0
- package/dist/web/assets/index-BL8bi7Yt.css +1 -0
- package/dist/web/assets/index-CuPR73Tn.js +73 -0
- package/dist/web/favicon.svg +4 -0
- package/dist/web/index.html +26 -0
- package/dist/web/logo.jpg +0 -0
- package/dist/web/logo.png +0 -0
- package/package.json +51 -4
|
@@ -0,0 +1,701 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
3
|
+
/**
|
|
4
|
+
* Durable session storage on `node:sqlite`.
|
|
5
|
+
*
|
|
6
|
+
* Conventions mirror `packages/scheduler/src/store.ts`: `CREATE TABLE IF NOT
|
|
7
|
+
* EXISTS` migrations, WAL mode, foreign keys on, `node:sqlite` built-in.
|
|
8
|
+
* One instance per gateway process, backed by `<dataDir>/sessions.sqlite`.
|
|
9
|
+
*
|
|
10
|
+
* Also checkpoints HITL approval requests (`turn_approvals`): each
|
|
11
|
+
* `turn.approval_required` pause is recorded as `pending` and later resolved
|
|
12
|
+
* to `approved` | `rejected` | `timeout`. Rows still `pending` at `open()`
|
|
13
|
+
* died with the previous process (their in-memory resolvers are gone), so
|
|
14
|
+
* they are reaped as `timeout` — the history stays truthful across restarts.
|
|
15
|
+
*
|
|
16
|
+
* Message variants (P2-3, linear variant-set branching): any message can hold
|
|
17
|
+
* alternate contents in `message_variants`, keyed by
|
|
18
|
+
* `(session_id, message_id, variant_index)` with `variant_index >= 1`. The
|
|
19
|
+
* base row keeps the original content; `session_messages.select_index`
|
|
20
|
+
* (0 = base) points at the active variant. Reads (`getMessages`,
|
|
21
|
+
* `getRecentMessages`) resolve to the selected variant, so the prompt always
|
|
22
|
+
* sees the active content; the journal never loses the base row. Lighter
|
|
23
|
+
* than a branch tree: one pointer per position, no graph.
|
|
24
|
+
*/
|
|
25
|
+
const SCHEMA_SQL = `
|
|
26
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
27
|
+
id TEXT PRIMARY KEY,
|
|
28
|
+
title TEXT NOT NULL,
|
|
29
|
+
created_at TEXT NOT NULL,
|
|
30
|
+
updated_at TEXT NOT NULL,
|
|
31
|
+
config_snapshot_json TEXT
|
|
32
|
+
);
|
|
33
|
+
CREATE TABLE IF NOT EXISTS session_messages (
|
|
34
|
+
id TEXT PRIMARY KEY,
|
|
35
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
36
|
+
role TEXT NOT NULL CHECK (role IN ('user', 'assistant', 'system', 'tool')),
|
|
37
|
+
content TEXT NOT NULL,
|
|
38
|
+
created_at TEXT NOT NULL,
|
|
39
|
+
metadata TEXT,
|
|
40
|
+
is_checkpoint INTEGER NOT NULL DEFAULT 0,
|
|
41
|
+
select_index INTEGER NOT NULL DEFAULT 0
|
|
42
|
+
);
|
|
43
|
+
CREATE INDEX IF NOT EXISTS session_messages_by_session
|
|
44
|
+
ON session_messages (session_id, created_at);
|
|
45
|
+
CREATE TABLE IF NOT EXISTS message_variants (
|
|
46
|
+
id TEXT PRIMARY KEY,
|
|
47
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
48
|
+
message_id TEXT NOT NULL REFERENCES session_messages(id) ON DELETE CASCADE,
|
|
49
|
+
variant_index INTEGER NOT NULL CHECK (variant_index >= 1),
|
|
50
|
+
content TEXT NOT NULL,
|
|
51
|
+
metadata TEXT,
|
|
52
|
+
created_at TEXT NOT NULL,
|
|
53
|
+
UNIQUE (session_id, message_id, variant_index)
|
|
54
|
+
);
|
|
55
|
+
CREATE INDEX IF NOT EXISTS message_variants_by_message
|
|
56
|
+
ON message_variants (message_id, variant_index);
|
|
57
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS session_messages_fts USING fts5(
|
|
58
|
+
content,
|
|
59
|
+
content = 'session_messages',
|
|
60
|
+
content_rowid = 'rowid',
|
|
61
|
+
tokenize = 'unicode61 remove_diacritics 2'
|
|
62
|
+
);
|
|
63
|
+
CREATE TRIGGER IF NOT EXISTS session_messages_fts_insert AFTER INSERT ON session_messages BEGIN
|
|
64
|
+
INSERT INTO session_messages_fts(rowid, content) VALUES (new.rowid, new.content);
|
|
65
|
+
END;
|
|
66
|
+
CREATE TRIGGER IF NOT EXISTS session_messages_fts_delete AFTER DELETE ON session_messages BEGIN
|
|
67
|
+
INSERT INTO session_messages_fts(session_messages_fts, rowid, content)
|
|
68
|
+
VALUES ('delete', old.rowid, old.content);
|
|
69
|
+
END;
|
|
70
|
+
CREATE TABLE IF NOT EXISTS turn_approvals (
|
|
71
|
+
run_id TEXT PRIMARY KEY,
|
|
72
|
+
turn_id TEXT NOT NULL,
|
|
73
|
+
session_id TEXT NOT NULL,
|
|
74
|
+
tool_name TEXT NOT NULL,
|
|
75
|
+
args_json TEXT NOT NULL,
|
|
76
|
+
reason TEXT NOT NULL,
|
|
77
|
+
status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'rejected', 'timeout')),
|
|
78
|
+
created_at TEXT NOT NULL,
|
|
79
|
+
resolved_at TEXT
|
|
80
|
+
);
|
|
81
|
+
CREATE INDEX IF NOT EXISTS turn_approvals_by_session
|
|
82
|
+
ON turn_approvals (session_id, created_at);
|
|
83
|
+
`;
|
|
84
|
+
const ROLES = new Set(['user', 'assistant', 'system', 'tool']);
|
|
85
|
+
/** Thrown by mutating operations when the target session does not exist. */
|
|
86
|
+
export class SessionNotFoundError extends Error {
|
|
87
|
+
code = 'SESSION_NOT_FOUND';
|
|
88
|
+
constructor(sessionId) {
|
|
89
|
+
super(`Session not found: ${sessionId}`);
|
|
90
|
+
this.name = 'SessionNotFoundError';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Thrown when a variant operation targets a message or variant that does not exist. */
|
|
94
|
+
export class VariantNotFoundError extends Error {
|
|
95
|
+
code = 'VARIANT_NOT_FOUND';
|
|
96
|
+
constructor(message) {
|
|
97
|
+
super(message);
|
|
98
|
+
this.name = 'VariantNotFoundError';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function sessionFromRow(row) {
|
|
102
|
+
return {
|
|
103
|
+
id: row.id,
|
|
104
|
+
title: row.title,
|
|
105
|
+
createdAt: row.created_at,
|
|
106
|
+
updatedAt: row.updated_at,
|
|
107
|
+
messageCount: row.message_count,
|
|
108
|
+
...(row.config_snapshot_json == null
|
|
109
|
+
? {}
|
|
110
|
+
: { configSnapshot: parseConfigSnapshot(row.config_snapshot_json) }),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function messageFromRow(row) {
|
|
114
|
+
const role = ROLES.has(row.role) ? row.role : 'user';
|
|
115
|
+
return {
|
|
116
|
+
id: row.id,
|
|
117
|
+
sessionId: row.session_id,
|
|
118
|
+
role,
|
|
119
|
+
content: row.content,
|
|
120
|
+
createdAt: row.created_at,
|
|
121
|
+
...(row.metadata == null ? {} : { metadata: parseMessageMetadata(row.metadata) }),
|
|
122
|
+
...(row.is_checkpoint === 1 ? { isCheckpoint: true } : {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function variantFromRow(row) {
|
|
126
|
+
return {
|
|
127
|
+
id: row.id,
|
|
128
|
+
sessionId: row.session_id,
|
|
129
|
+
messageId: row.message_id,
|
|
130
|
+
variantIndex: row.variant_index,
|
|
131
|
+
content: row.content,
|
|
132
|
+
createdAt: row.created_at,
|
|
133
|
+
...(row.metadata == null ? {} : { metadata: parseMessageMetadata(row.metadata) }),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Resolve each message row to its active variant (P2-3). `select_index` 0
|
|
138
|
+
* (or a dangling pointer to a deleted variant) reads the base row; any other
|
|
139
|
+
* index swaps in the variant's content and metadata so history reads and the
|
|
140
|
+
* prompt projection always see the selected variant.
|
|
141
|
+
*/
|
|
142
|
+
function resolveVariants(rows, variants) {
|
|
143
|
+
const byMessage = new Map();
|
|
144
|
+
for (const variant of variants) {
|
|
145
|
+
let perMessage = byMessage.get(variant.message_id);
|
|
146
|
+
if (perMessage === undefined) {
|
|
147
|
+
perMessage = new Map();
|
|
148
|
+
byMessage.set(variant.message_id, perMessage);
|
|
149
|
+
}
|
|
150
|
+
perMessage.set(variant.variant_index, variant);
|
|
151
|
+
}
|
|
152
|
+
return rows.map((row) => {
|
|
153
|
+
const message = messageFromRow(row);
|
|
154
|
+
const selected = row.select_index ?? 0;
|
|
155
|
+
if (selected <= 0)
|
|
156
|
+
return message;
|
|
157
|
+
const variant = byMessage.get(row.id)?.get(selected);
|
|
158
|
+
if (variant === undefined)
|
|
159
|
+
return message; // fail-safe: dangling pointer reads the base row
|
|
160
|
+
// The variant fully replaces the base row: content AND tool-call
|
|
161
|
+
// metadata, so a selected variant never inherits the base message's
|
|
162
|
+
// tool_call pairing.
|
|
163
|
+
const resolved = {
|
|
164
|
+
id: message.id,
|
|
165
|
+
sessionId: message.sessionId,
|
|
166
|
+
role: message.role,
|
|
167
|
+
content: variant.content,
|
|
168
|
+
createdAt: message.createdAt,
|
|
169
|
+
variantIndex: selected,
|
|
170
|
+
};
|
|
171
|
+
if (message.isCheckpoint === true)
|
|
172
|
+
resolved.isCheckpoint = true;
|
|
173
|
+
const variantMetadata = variant.metadata == null ? undefined : parseMessageMetadata(variant.metadata);
|
|
174
|
+
if (variantMetadata !== undefined)
|
|
175
|
+
resolved.metadata = variantMetadata;
|
|
176
|
+
return resolved;
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/** Fetch every variant row for the given message ids (one query, no N+1). */
|
|
180
|
+
function fetchVariants(db, messageIds) {
|
|
181
|
+
if (messageIds.length === 0)
|
|
182
|
+
return [];
|
|
183
|
+
const placeholders = messageIds.map(() => '?').join(',');
|
|
184
|
+
return db
|
|
185
|
+
.prepare(`SELECT * FROM message_variants WHERE message_id IN (${placeholders}) ORDER BY variant_index ASC`)
|
|
186
|
+
.all(...messageIds);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Parse the `metadata` JSON column. Corrupt or unexpected shapes degrade to
|
|
190
|
+
* `undefined` (fail-safe: the message is still usable, just without pairing).
|
|
191
|
+
*/
|
|
192
|
+
function parseMessageMetadata(raw) {
|
|
193
|
+
let parsed;
|
|
194
|
+
try {
|
|
195
|
+
parsed = JSON.parse(raw);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
201
|
+
return undefined;
|
|
202
|
+
const record = parsed;
|
|
203
|
+
const metadata = {};
|
|
204
|
+
if (Array.isArray(record['toolCalls'])) {
|
|
205
|
+
const toolCalls = record['toolCalls'].filter((tc) => typeof tc === 'object' &&
|
|
206
|
+
tc !== null &&
|
|
207
|
+
typeof tc['id'] === 'string' &&
|
|
208
|
+
typeof tc['name'] === 'string');
|
|
209
|
+
if (toolCalls.length > 0)
|
|
210
|
+
metadata.toolCalls = toolCalls;
|
|
211
|
+
}
|
|
212
|
+
if (typeof record['toolCallId'] === 'string' && record['toolCallId'] !== '') {
|
|
213
|
+
metadata.toolCallId = record['toolCallId'];
|
|
214
|
+
}
|
|
215
|
+
if (typeof record['toolName'] === 'string' && record['toolName'] !== '') {
|
|
216
|
+
metadata.toolName = record['toolName'];
|
|
217
|
+
}
|
|
218
|
+
return Object.keys(metadata).length > 0 ? metadata : undefined;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Parse the `config_snapshot_json` column. Corrupt or unexpected shapes
|
|
222
|
+
* degrade to `undefined` (fail-safe: the session is still usable, just
|
|
223
|
+
* without its config record) — same contract as `parseMessageMetadata`.
|
|
224
|
+
*/
|
|
225
|
+
function parseConfigSnapshot(raw) {
|
|
226
|
+
let parsed;
|
|
227
|
+
try {
|
|
228
|
+
parsed = JSON.parse(raw);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
234
|
+
return undefined;
|
|
235
|
+
const record = parsed;
|
|
236
|
+
const toolsEnabled = record['toolsEnabled'];
|
|
237
|
+
const systemPromptHash = record['systemPromptHash'];
|
|
238
|
+
if (typeof toolsEnabled !== 'number' ||
|
|
239
|
+
!Number.isInteger(toolsEnabled) ||
|
|
240
|
+
toolsEnabled < 0 ||
|
|
241
|
+
typeof systemPromptHash !== 'string' ||
|
|
242
|
+
systemPromptHash === '') {
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
245
|
+
const model = record['model'];
|
|
246
|
+
const provider = record['provider'];
|
|
247
|
+
return {
|
|
248
|
+
...(typeof model === 'string' && model !== '' ? { model } : {}),
|
|
249
|
+
...(typeof provider === 'string' && provider !== '' ? { provider } : {}),
|
|
250
|
+
toolsEnabled,
|
|
251
|
+
systemPromptHash,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
const APPROVAL_STATUSES = new Set(['pending', 'approved', 'rejected', 'timeout']);
|
|
255
|
+
function approvalFromRow(row) {
|
|
256
|
+
const status = APPROVAL_STATUSES.has(row.status) ? row.status : 'timeout';
|
|
257
|
+
return {
|
|
258
|
+
runId: row.run_id,
|
|
259
|
+
turnId: row.turn_id,
|
|
260
|
+
sessionId: row.session_id,
|
|
261
|
+
toolName: row.tool_name,
|
|
262
|
+
argsJson: row.args_json,
|
|
263
|
+
reason: row.reason,
|
|
264
|
+
status,
|
|
265
|
+
createdAt: row.created_at,
|
|
266
|
+
resolvedAt: row.resolved_at,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function newId(prefix) {
|
|
270
|
+
return `${prefix}_${randomUUID().replace(/-/g, '')}`;
|
|
271
|
+
}
|
|
272
|
+
const SESSION_WITH_COUNT = `
|
|
273
|
+
SELECT s.*, (SELECT COUNT(*) FROM session_messages m WHERE m.session_id = s.id) AS message_count
|
|
274
|
+
FROM sessions s
|
|
275
|
+
`;
|
|
276
|
+
export class SqliteSessionStore {
|
|
277
|
+
db;
|
|
278
|
+
constructor(db) {
|
|
279
|
+
this.db = db;
|
|
280
|
+
}
|
|
281
|
+
static open(path) {
|
|
282
|
+
const db = new DatabaseSync(path);
|
|
283
|
+
db.exec('PRAGMA journal_mode = WAL;');
|
|
284
|
+
db.exec('PRAGMA foreign_keys = ON;');
|
|
285
|
+
const hadSearchIndex = db.prepare(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'session_messages_fts'`).get() !== undefined;
|
|
286
|
+
db.exec(SCHEMA_SQL);
|
|
287
|
+
// Migration for databases created before the `metadata` column existed
|
|
288
|
+
// (tool-call pairing, P0-1): `CREATE TABLE IF NOT EXISTS` never adds it.
|
|
289
|
+
const hasMetadataColumn = db
|
|
290
|
+
.prepare(`SELECT 1 FROM pragma_table_info('session_messages') WHERE name = 'metadata'`)
|
|
291
|
+
.get() !== undefined;
|
|
292
|
+
if (!hasMetadataColumn) {
|
|
293
|
+
db.exec('ALTER TABLE session_messages ADD COLUMN metadata TEXT');
|
|
294
|
+
}
|
|
295
|
+
// Migration for the context checkpoint flag (P1-6): marks the message a
|
|
296
|
+
// prompt projection must start from; older messages are dropped from the
|
|
297
|
+
// model prompt (the journal keeps them).
|
|
298
|
+
const hasCheckpointColumn = db
|
|
299
|
+
.prepare(`SELECT 1 FROM pragma_table_info('session_messages') WHERE name = 'is_checkpoint'`)
|
|
300
|
+
.get() !== undefined;
|
|
301
|
+
if (!hasCheckpointColumn) {
|
|
302
|
+
db.exec('ALTER TABLE session_messages ADD COLUMN is_checkpoint INTEGER NOT NULL DEFAULT 0');
|
|
303
|
+
}
|
|
304
|
+
// Migration for the variant-set pointer (P2-3): which variant of a
|
|
305
|
+
// message is active (0 = the base row). Old databases keep working:
|
|
306
|
+
// every existing message reads as select_index 0.
|
|
307
|
+
const hasSelectIndexColumn = db
|
|
308
|
+
.prepare(`SELECT 1 FROM pragma_table_info('session_messages') WHERE name = 'select_index'`)
|
|
309
|
+
.get() !== undefined;
|
|
310
|
+
if (!hasSelectIndexColumn) {
|
|
311
|
+
db.exec('ALTER TABLE session_messages ADD COLUMN select_index INTEGER NOT NULL DEFAULT 0');
|
|
312
|
+
}
|
|
313
|
+
// Migration for the per-session config snapshot (P1-7): point-in-time
|
|
314
|
+
// record of the model/provider/tools/system prompt serving the session.
|
|
315
|
+
const hasSnapshotColumn = db
|
|
316
|
+
.prepare(`SELECT 1 FROM pragma_table_info('sessions') WHERE name = 'config_snapshot_json'`)
|
|
317
|
+
.get() !== undefined;
|
|
318
|
+
if (!hasSnapshotColumn) {
|
|
319
|
+
db.exec('ALTER TABLE sessions ADD COLUMN config_snapshot_json TEXT');
|
|
320
|
+
}
|
|
321
|
+
if (!hadSearchIndex) {
|
|
322
|
+
db.exec(`INSERT INTO session_messages_fts(session_messages_fts) VALUES ('rebuild');`);
|
|
323
|
+
}
|
|
324
|
+
// Reap stale approvals: any request still `pending` died with the
|
|
325
|
+
// previous process — its in-memory resolver is gone, so mark `timeout`.
|
|
326
|
+
db.prepare(`UPDATE turn_approvals SET status = 'timeout', resolved_at = ? WHERE status = 'pending'`).run(new Date().toISOString());
|
|
327
|
+
return new SqliteSessionStore(db);
|
|
328
|
+
}
|
|
329
|
+
static openMemory() {
|
|
330
|
+
return SqliteSessionStore.open(':memory:');
|
|
331
|
+
}
|
|
332
|
+
close() {
|
|
333
|
+
this.db.close();
|
|
334
|
+
}
|
|
335
|
+
create(title, id) {
|
|
336
|
+
const now = new Date().toISOString();
|
|
337
|
+
const finalId = id ?? newId('ses');
|
|
338
|
+
const finalTitle = title ?? '';
|
|
339
|
+
this.db
|
|
340
|
+
.prepare('INSERT INTO sessions (id, title, created_at, updated_at) VALUES (?, ?, ?, ?)')
|
|
341
|
+
.run(finalId, finalTitle, now, now);
|
|
342
|
+
return { id: finalId, title: finalTitle, createdAt: now, updatedAt: now, messageCount: 0 };
|
|
343
|
+
}
|
|
344
|
+
list() {
|
|
345
|
+
const rows = this.db
|
|
346
|
+
.prepare(`${SESSION_WITH_COUNT} ORDER BY s.updated_at DESC, s.rowid DESC`)
|
|
347
|
+
.all();
|
|
348
|
+
return rows.map(sessionFromRow);
|
|
349
|
+
}
|
|
350
|
+
get(id) {
|
|
351
|
+
const row = this.db
|
|
352
|
+
.prepare(`${SESSION_WITH_COUNT} WHERE s.id = ?`)
|
|
353
|
+
.get(id);
|
|
354
|
+
return row === undefined ? undefined : sessionFromRow(row);
|
|
355
|
+
}
|
|
356
|
+
rename(id, title) {
|
|
357
|
+
// Single process, no await between check and write: no TOCTOU window.
|
|
358
|
+
if (this.get(id) === undefined) {
|
|
359
|
+
throw new SessionNotFoundError(id);
|
|
360
|
+
}
|
|
361
|
+
const now = new Date().toISOString();
|
|
362
|
+
this.db.prepare('UPDATE sessions SET title = ?, updated_at = ? WHERE id = ?').run(title, now, id);
|
|
363
|
+
const renamed = this.get(id);
|
|
364
|
+
if (renamed === undefined) {
|
|
365
|
+
throw new SessionNotFoundError(id);
|
|
366
|
+
}
|
|
367
|
+
return renamed;
|
|
368
|
+
}
|
|
369
|
+
branch(id, title) {
|
|
370
|
+
const source = this.get(id);
|
|
371
|
+
if (source === undefined)
|
|
372
|
+
throw new SessionNotFoundError(id);
|
|
373
|
+
const now = new Date().toISOString();
|
|
374
|
+
const branchId = newId('ses');
|
|
375
|
+
const branchTitle = (title?.trim() || `${source.title || 'Chat'} (branch)`).slice(0, 200);
|
|
376
|
+
this.db.exec('BEGIN IMMEDIATE');
|
|
377
|
+
try {
|
|
378
|
+
this.db
|
|
379
|
+
.prepare('INSERT INTO sessions (id, title, created_at, updated_at) VALUES (?, ?, ?, ?)')
|
|
380
|
+
.run(branchId, branchTitle, now, now);
|
|
381
|
+
// Raw rows, not getMessages(): the branch keeps the full variant set
|
|
382
|
+
// (base content + select_index + every variant), never the resolved view.
|
|
383
|
+
const rows = this.db
|
|
384
|
+
.prepare('SELECT * FROM session_messages WHERE session_id = ? ORDER BY created_at ASC, rowid ASC')
|
|
385
|
+
.all(id);
|
|
386
|
+
const insert = this.db.prepare('INSERT INTO session_messages (id, session_id, role, content, created_at, metadata, is_checkpoint, select_index) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
|
|
387
|
+
const idMap = new Map();
|
|
388
|
+
for (const row of rows) {
|
|
389
|
+
const newMessageId = newId('msg');
|
|
390
|
+
idMap.set(row.id, newMessageId);
|
|
391
|
+
insert.run(newMessageId, branchId, row.role, row.content, row.created_at, row.metadata, row.is_checkpoint ?? 0, row.select_index ?? 0);
|
|
392
|
+
}
|
|
393
|
+
const variantRows = this.db
|
|
394
|
+
.prepare('SELECT * FROM message_variants WHERE session_id = ?')
|
|
395
|
+
.all(id);
|
|
396
|
+
const insertVariant = this.db.prepare('INSERT INTO message_variants (id, session_id, message_id, variant_index, content, metadata, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)');
|
|
397
|
+
for (const variant of variantRows) {
|
|
398
|
+
const newMessageId = idMap.get(variant.message_id);
|
|
399
|
+
if (newMessageId === undefined)
|
|
400
|
+
continue; // defensive: orphan variant rows never cross sessions
|
|
401
|
+
insertVariant.run(newId('var'), branchId, newMessageId, variant.variant_index, variant.content, variant.metadata, variant.created_at);
|
|
402
|
+
}
|
|
403
|
+
this.db.exec('COMMIT');
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
try {
|
|
407
|
+
this.db.exec('ROLLBACK');
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
// Preserve the original database error.
|
|
411
|
+
}
|
|
412
|
+
throw error;
|
|
413
|
+
}
|
|
414
|
+
const branch = this.get(branchId);
|
|
415
|
+
if (branch === undefined)
|
|
416
|
+
throw new SessionNotFoundError(branchId);
|
|
417
|
+
return branch;
|
|
418
|
+
}
|
|
419
|
+
delete(id) {
|
|
420
|
+
const result = this.db.prepare('DELETE FROM sessions WHERE id = ?').run(id);
|
|
421
|
+
return Number(result.changes) === 1;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Record the session's config snapshot (P1-7): model, provider, tool count
|
|
425
|
+
* and system prompt hash serving this session's turns. Written once — the
|
|
426
|
+
* turn service only calls this while the snapshot is still absent — so
|
|
427
|
+
* later config drift cannot rewrite history.
|
|
428
|
+
*/
|
|
429
|
+
setConfigSnapshot(sessionId, snapshot) {
|
|
430
|
+
if (this.get(sessionId) === undefined) {
|
|
431
|
+
throw new SessionNotFoundError(sessionId);
|
|
432
|
+
}
|
|
433
|
+
this.db
|
|
434
|
+
.prepare('UPDATE sessions SET config_snapshot_json = ?, updated_at = ? WHERE id = ?')
|
|
435
|
+
.run(JSON.stringify(snapshot), new Date().toISOString(), sessionId);
|
|
436
|
+
const updated = this.get(sessionId);
|
|
437
|
+
if (updated === undefined) {
|
|
438
|
+
throw new SessionNotFoundError(sessionId);
|
|
439
|
+
}
|
|
440
|
+
return updated;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Mark a message as a context checkpoint (P1-6). Prompt projection drops
|
|
444
|
+
* every message before the newest checkpoint; the journal keeps them.
|
|
445
|
+
* With no `messageId` the latest message becomes the checkpoint. Earlier
|
|
446
|
+
* checkpoints are kept (append-only journal) — only the newest one cuts.
|
|
447
|
+
*/
|
|
448
|
+
markCheckpoint(sessionId, messageId) {
|
|
449
|
+
if (this.get(sessionId) === undefined) {
|
|
450
|
+
throw new SessionNotFoundError(sessionId);
|
|
451
|
+
}
|
|
452
|
+
let target;
|
|
453
|
+
if (messageId === undefined) {
|
|
454
|
+
const rows = this.db
|
|
455
|
+
.prepare(`SELECT * FROM session_messages
|
|
456
|
+
WHERE session_id = ?
|
|
457
|
+
ORDER BY created_at DESC, rowid DESC LIMIT 1`)
|
|
458
|
+
.all(sessionId);
|
|
459
|
+
target = rows.length === 0 ? undefined : messageFromRow(rows[0]);
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
const row = this.db
|
|
463
|
+
.prepare('SELECT * FROM session_messages WHERE id = ? AND session_id = ?')
|
|
464
|
+
.get(messageId, sessionId);
|
|
465
|
+
target = row === undefined ? undefined : messageFromRow(row);
|
|
466
|
+
}
|
|
467
|
+
if (target === undefined) {
|
|
468
|
+
throw new Error(messageId === undefined
|
|
469
|
+
? `Cannot checkpoint an empty session: ${sessionId}`
|
|
470
|
+
: `Message not found in session ${sessionId}: ${messageId}`);
|
|
471
|
+
}
|
|
472
|
+
this.db.prepare('UPDATE session_messages SET is_checkpoint = 1 WHERE id = ?').run(target.id);
|
|
473
|
+
return { ...target, isCheckpoint: true };
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Append a new variant at a message position (P2-3). The variant index is
|
|
477
|
+
* assigned as max+1 (starting at 1); creating a variant never moves
|
|
478
|
+
* `select_index` — selecting stays an explicit `selectVariant` call.
|
|
479
|
+
*/
|
|
480
|
+
createVariant(sessionId, messageId, content, metadata) {
|
|
481
|
+
if (this.get(sessionId) === undefined) {
|
|
482
|
+
throw new SessionNotFoundError(sessionId);
|
|
483
|
+
}
|
|
484
|
+
const anchor = this.readMessageRow(sessionId, messageId);
|
|
485
|
+
if (anchor === undefined) {
|
|
486
|
+
throw new VariantNotFoundError(`Message not found in session ${sessionId}: ${messageId}`);
|
|
487
|
+
}
|
|
488
|
+
if (typeof content !== 'string') {
|
|
489
|
+
throw new Error('Variant content must be a string');
|
|
490
|
+
}
|
|
491
|
+
const maxIndex = this.db
|
|
492
|
+
.prepare('SELECT COALESCE(MAX(variant_index), 0) AS max_index FROM message_variants WHERE message_id = ?')
|
|
493
|
+
.get(anchor.id);
|
|
494
|
+
const now = new Date().toISOString();
|
|
495
|
+
const id = newId('var');
|
|
496
|
+
const variantIndex = Number(maxIndex.max_index) + 1;
|
|
497
|
+
this.db
|
|
498
|
+
.prepare('INSERT INTO message_variants (id, session_id, message_id, variant_index, content, metadata, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)')
|
|
499
|
+
.run(id, sessionId, anchor.id, variantIndex, content, metadata === undefined ? null : JSON.stringify(metadata), now);
|
|
500
|
+
this.db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(now, sessionId);
|
|
501
|
+
return {
|
|
502
|
+
id,
|
|
503
|
+
sessionId,
|
|
504
|
+
messageId: anchor.id,
|
|
505
|
+
variantIndex,
|
|
506
|
+
content,
|
|
507
|
+
createdAt: now,
|
|
508
|
+
...(metadata === undefined ? {} : { metadata }),
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* List every variant stored at a message position, oldest first.
|
|
513
|
+
* The base row (index 0) is not a variant and is not listed.
|
|
514
|
+
*/
|
|
515
|
+
listVariants(sessionId, messageId) {
|
|
516
|
+
if (this.get(sessionId) === undefined) {
|
|
517
|
+
throw new SessionNotFoundError(sessionId);
|
|
518
|
+
}
|
|
519
|
+
const anchor = this.readMessageRow(sessionId, messageId);
|
|
520
|
+
if (anchor === undefined) {
|
|
521
|
+
throw new VariantNotFoundError(`Message not found in session ${sessionId}: ${messageId}`);
|
|
522
|
+
}
|
|
523
|
+
const rows = this.db
|
|
524
|
+
.prepare('SELECT * FROM message_variants WHERE session_id = ? AND message_id = ? ORDER BY variant_index ASC')
|
|
525
|
+
.all(sessionId, anchor.id);
|
|
526
|
+
return rows.map(variantFromRow);
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Point `select_index` at a variant (P2-3). Index 0 re-selects the base
|
|
530
|
+
* row; any other index must already exist. Returns the message resolved
|
|
531
|
+
* to the newly selected content — the same shape history reads return.
|
|
532
|
+
*/
|
|
533
|
+
selectVariant(sessionId, messageId, variantIndex) {
|
|
534
|
+
if (this.get(sessionId) === undefined) {
|
|
535
|
+
throw new SessionNotFoundError(sessionId);
|
|
536
|
+
}
|
|
537
|
+
const anchor = this.readMessageRow(sessionId, messageId);
|
|
538
|
+
if (anchor === undefined) {
|
|
539
|
+
throw new VariantNotFoundError(`Message not found in session ${sessionId}: ${messageId}`);
|
|
540
|
+
}
|
|
541
|
+
if (!Number.isInteger(variantIndex) || variantIndex < 0) {
|
|
542
|
+
throw new Error(`Variant index must be a non-negative integer, got: ${String(variantIndex)}`);
|
|
543
|
+
}
|
|
544
|
+
if (variantIndex > 0) {
|
|
545
|
+
const exists = this.db
|
|
546
|
+
.prepare('SELECT 1 FROM message_variants WHERE session_id = ? AND message_id = ? AND variant_index = ?')
|
|
547
|
+
.get(sessionId, anchor.id, variantIndex) !== undefined;
|
|
548
|
+
if (!exists) {
|
|
549
|
+
throw new VariantNotFoundError(`Variant ${variantIndex} does not exist at message ${messageId} in session ${sessionId}`);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
this.db
|
|
553
|
+
.prepare('UPDATE session_messages SET select_index = ? WHERE id = ?')
|
|
554
|
+
.run(variantIndex, anchor.id);
|
|
555
|
+
this.db
|
|
556
|
+
.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?')
|
|
557
|
+
.run(new Date().toISOString(), sessionId);
|
|
558
|
+
const resolved = this.readMessageRow(sessionId, anchor.id);
|
|
559
|
+
if (resolved === undefined)
|
|
560
|
+
throw new VariantNotFoundError(`Message vanished: ${messageId}`);
|
|
561
|
+
return resolveVariants([resolved], fetchVariants(this.db, variantIndex > 0 ? [resolved.id] : []))[0];
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Raw message row for this session, or `undefined`. Used by variant
|
|
565
|
+
* operations so they work on the stored row, never the resolved view.
|
|
566
|
+
*/
|
|
567
|
+
readMessageRow(sessionId, messageId) {
|
|
568
|
+
return this.db
|
|
569
|
+
.prepare('SELECT * FROM session_messages WHERE id = ? AND session_id = ?')
|
|
570
|
+
.get(messageId, sessionId);
|
|
571
|
+
}
|
|
572
|
+
undoLastExchange(id) {
|
|
573
|
+
if (this.get(id) === undefined)
|
|
574
|
+
throw new SessionNotFoundError(id);
|
|
575
|
+
const boundary = this.db
|
|
576
|
+
.prepare(`SELECT rowid FROM session_messages
|
|
577
|
+
WHERE session_id = ? AND role = 'user'
|
|
578
|
+
ORDER BY created_at DESC, rowid DESC LIMIT 1`)
|
|
579
|
+
.get(id);
|
|
580
|
+
if (boundary === undefined)
|
|
581
|
+
return 0;
|
|
582
|
+
let removedCount = 0;
|
|
583
|
+
this.db.exec('BEGIN IMMEDIATE');
|
|
584
|
+
try {
|
|
585
|
+
const result = this.db
|
|
586
|
+
.prepare('DELETE FROM session_messages WHERE session_id = ? AND rowid >= ?')
|
|
587
|
+
.run(id, boundary.rowid);
|
|
588
|
+
removedCount = Number(result.changes);
|
|
589
|
+
this.db
|
|
590
|
+
.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?')
|
|
591
|
+
.run(new Date().toISOString(), id);
|
|
592
|
+
this.db.exec('COMMIT');
|
|
593
|
+
}
|
|
594
|
+
catch (error) {
|
|
595
|
+
try {
|
|
596
|
+
this.db.exec('ROLLBACK');
|
|
597
|
+
}
|
|
598
|
+
catch {
|
|
599
|
+
// Preserve the original database error.
|
|
600
|
+
}
|
|
601
|
+
throw error;
|
|
602
|
+
}
|
|
603
|
+
return removedCount;
|
|
604
|
+
}
|
|
605
|
+
appendMessage(sessionId, role, text, metadata) {
|
|
606
|
+
if (!ROLES.has(role)) {
|
|
607
|
+
throw new Error(`Invalid session message role: ${String(role)}`);
|
|
608
|
+
}
|
|
609
|
+
// Single process, no await between check and write: no TOCTOU window.
|
|
610
|
+
if (this.get(sessionId) === undefined) {
|
|
611
|
+
throw new SessionNotFoundError(sessionId);
|
|
612
|
+
}
|
|
613
|
+
const now = new Date().toISOString();
|
|
614
|
+
const id = newId('msg');
|
|
615
|
+
this.db
|
|
616
|
+
.prepare('INSERT INTO session_messages (id, session_id, role, content, created_at, metadata) VALUES (?, ?, ?, ?, ?, ?)')
|
|
617
|
+
.run(id, sessionId, role, text, now, metadata === undefined ? null : JSON.stringify(metadata));
|
|
618
|
+
this.db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(now, sessionId);
|
|
619
|
+
return {
|
|
620
|
+
id,
|
|
621
|
+
sessionId,
|
|
622
|
+
role,
|
|
623
|
+
content: text,
|
|
624
|
+
createdAt: now,
|
|
625
|
+
...(metadata === undefined ? {} : { metadata }),
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
getMessages(sessionId) {
|
|
629
|
+
const rows = this.db
|
|
630
|
+
.prepare('SELECT * FROM session_messages WHERE session_id = ? ORDER BY created_at ASC, rowid ASC')
|
|
631
|
+
.all(sessionId);
|
|
632
|
+
// P2-3: history reads resolve to the selected variant, so the journal
|
|
633
|
+
// and the prompt projection (via the session adapter) see the same
|
|
634
|
+
// active content.
|
|
635
|
+
return resolveVariants(rows, fetchVariants(this.db, rows.filter((row) => (row.select_index ?? 0) > 0).map((row) => row.id)));
|
|
636
|
+
}
|
|
637
|
+
getRecentMessages(sessionId, limit) {
|
|
638
|
+
const boundedLimit = Math.min(1_000, Math.max(1, Math.trunc(limit)));
|
|
639
|
+
const rows = this.db.prepare(`
|
|
640
|
+
SELECT id, session_id, role, content, created_at, metadata, is_checkpoint, select_index FROM (
|
|
641
|
+
SELECT rowid AS sort_rowid, * FROM session_messages
|
|
642
|
+
WHERE session_id = ?
|
|
643
|
+
ORDER BY created_at DESC, rowid DESC
|
|
644
|
+
LIMIT ?
|
|
645
|
+
)
|
|
646
|
+
ORDER BY created_at ASC, sort_rowid ASC
|
|
647
|
+
`).all(sessionId, boundedLimit);
|
|
648
|
+
return resolveVariants(rows, fetchVariants(this.db, rows.filter((row) => (row.select_index ?? 0) > 0).map((row) => row.id)));
|
|
649
|
+
}
|
|
650
|
+
searchMessages(query, limit = 5) {
|
|
651
|
+
const terms = query.trim().match(/[\p{L}\p{N}_]+/gu)?.slice(0, 16) ?? [];
|
|
652
|
+
if (terms.length === 0)
|
|
653
|
+
return [];
|
|
654
|
+
const boundedLimit = Math.min(20, Math.max(1, Math.trunc(limit)));
|
|
655
|
+
const match = terms.map((term) => `"${term.replaceAll('"', '""')}"*`).join(' AND ');
|
|
656
|
+
const rows = this.db.prepare(`
|
|
657
|
+
SELECT m.id AS message_id, m.session_id, s.title AS session_title, m.role,
|
|
658
|
+
snippet(session_messages_fts, 0, '[', ']', ' … ', 24) AS excerpt,
|
|
659
|
+
m.created_at
|
|
660
|
+
FROM session_messages_fts
|
|
661
|
+
JOIN session_messages m ON m.rowid = session_messages_fts.rowid
|
|
662
|
+
JOIN sessions s ON s.id = m.session_id
|
|
663
|
+
WHERE session_messages_fts MATCH ?
|
|
664
|
+
ORDER BY bm25(session_messages_fts), m.created_at DESC
|
|
665
|
+
LIMIT ?
|
|
666
|
+
`).all(match, boundedLimit);
|
|
667
|
+
return rows.map((row) => ({
|
|
668
|
+
messageId: row.message_id,
|
|
669
|
+
sessionId: row.session_id,
|
|
670
|
+
sessionTitle: row.session_title,
|
|
671
|
+
role: ROLES.has(row.role) ? row.role : 'user',
|
|
672
|
+
excerpt: row.excerpt,
|
|
673
|
+
createdAt: row.created_at,
|
|
674
|
+
}));
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Checkpoint a new HITL approval request as `pending`. Called by the turn
|
|
678
|
+
* service right after publishing `turn.approval_required`.
|
|
679
|
+
*/
|
|
680
|
+
recordApprovalRequest(row) {
|
|
681
|
+
const now = new Date().toISOString();
|
|
682
|
+
this.db
|
|
683
|
+
.prepare(`INSERT INTO turn_approvals
|
|
684
|
+
(run_id, turn_id, session_id, tool_name, args_json, reason, status, created_at)
|
|
685
|
+
VALUES (?, ?, ?, ?, ?, ?, 'pending', ?)`)
|
|
686
|
+
.run(row.runId, row.turnId, row.sessionId, row.toolName, row.argsJson, row.reason, now);
|
|
687
|
+
}
|
|
688
|
+
/** Resolve a checkpointed approval request (`approved` | `rejected` | `timeout`). */
|
|
689
|
+
resolveApproval(runId, status, resolvedAt) {
|
|
690
|
+
this.db
|
|
691
|
+
.prepare('UPDATE turn_approvals SET status = ?, resolved_at = ? WHERE run_id = ?')
|
|
692
|
+
.run(status, resolvedAt, runId);
|
|
693
|
+
}
|
|
694
|
+
/** Fetch one checkpointed approval request, if it exists. */
|
|
695
|
+
getApproval(runId) {
|
|
696
|
+
const row = this.db
|
|
697
|
+
.prepare('SELECT * FROM turn_approvals WHERE run_id = ?')
|
|
698
|
+
.get(runId);
|
|
699
|
+
return row === undefined ? undefined : approvalFromRow(row);
|
|
700
|
+
}
|
|
701
|
+
}
|