@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,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @boombot/security — persisted HITL approval state machine.
|
|
3
|
+
*
|
|
4
|
+
* The single source of truth for the lifecycle of a human-in-the-loop
|
|
5
|
+
* approval request:
|
|
6
|
+
*
|
|
7
|
+
* pending → approved | denied | expired
|
|
8
|
+
*
|
|
9
|
+
* `pending` is the only non-terminal state. A human answers with
|
|
10
|
+
* `resolveApproval(id, 'approved' | 'denied')`; the deadline passing moves
|
|
11
|
+
* the row to `expired` (via `sweepExpired`, called by the live approval
|
|
12
|
+
* timer and by the boot scan). Every transition out of `pending` is guarded
|
|
13
|
+
* by `WHERE status = 'pending'`, so a decision applies exactly once — a
|
|
14
|
+
* retried `turn.approve` or a racing timer can never double-resolve a
|
|
15
|
+
* request.
|
|
16
|
+
*
|
|
17
|
+
* Persistence: `node:sqlite` (built-in, zero dependencies — same as
|
|
18
|
+
* `SqliteSessionStore`), table `approval_requests` in the store's own
|
|
19
|
+
* database file. The gateway opens `<dataDir>/approvals.sqlite`, so the
|
|
20
|
+
* session store's schema (`sessions.sqlite`, incl. its legacy
|
|
21
|
+
* `turn_approvals` checkpoint table) is never migrated by this module and
|
|
22
|
+
* no other worker's migrations can collide with it.
|
|
23
|
+
*
|
|
24
|
+
* Restart semantics (P1-2): rows still `pending` at boot died with the
|
|
25
|
+
* previous process — their in-memory waiters are gone. `resumePendingOnBoot`
|
|
26
|
+
* first sweeps past-deadline rows to `expired`, then returns the survivors
|
|
27
|
+
* so the gateway can re-announce them (`turn.approval_required`) instead of
|
|
28
|
+
* failing them silently. The turn that requested them cannot resume (its
|
|
29
|
+
* loop state died with the old process); answering records the human's
|
|
30
|
+
* decision and the client is told the turn is gone.
|
|
31
|
+
*
|
|
32
|
+
* This module is new code written for BoomBot. It implements the
|
|
33
|
+
* approval-as-persisted-state-machine pattern (cf. the research notes);
|
|
34
|
+
* it does not copy any third-party implementation.
|
|
35
|
+
*/
|
|
36
|
+
/** Lifecycle states of one approval request. `pending` is the only live state. */
|
|
37
|
+
export type ApprovalRequestStatus = 'pending' | 'approved' | 'denied' | 'expired';
|
|
38
|
+
/** The two decisions a human (or the `turn.approve` command) can record. */
|
|
39
|
+
export type ApprovalHumanDecision = 'approved' | 'denied';
|
|
40
|
+
/** Everything needed to open a new approval request. */
|
|
41
|
+
export interface ApprovalRequestInput {
|
|
42
|
+
/** Stable id for the request (the turn service uses the approval runId). */
|
|
43
|
+
readonly id: string;
|
|
44
|
+
/** Owning turn — used to correlate the re-announced SSE event after a restart. */
|
|
45
|
+
readonly turnId: string;
|
|
46
|
+
readonly sessionId: string;
|
|
47
|
+
/** Proposed tool, e.g. 'shell.exec'. */
|
|
48
|
+
readonly toolName: string;
|
|
49
|
+
/** JSON-serialized tool arguments, shown to the approver. */
|
|
50
|
+
readonly argsJson: string;
|
|
51
|
+
/** Why the policy asked instead of deciding itself. */
|
|
52
|
+
readonly reason: string;
|
|
53
|
+
/** Absolute deadline (ms since epoch). Past it the request is `expired`. */
|
|
54
|
+
readonly expiresAtMs: number;
|
|
55
|
+
/** Who asked (principal id). Optional; null when unknown. */
|
|
56
|
+
readonly requestedBy?: string;
|
|
57
|
+
}
|
|
58
|
+
/** A persisted approval request: input plus its lifecycle state. */
|
|
59
|
+
export interface ApprovalRequestRecord extends ApprovalRequestInput {
|
|
60
|
+
readonly status: ApprovalRequestStatus;
|
|
61
|
+
readonly requestedAtMs: number;
|
|
62
|
+
readonly resolvedAtMs: number | null;
|
|
63
|
+
/** Who resolved it; 'system:expiry…' for timeouts, null when unknown. */
|
|
64
|
+
readonly resolvedBy: string | null;
|
|
65
|
+
}
|
|
66
|
+
export interface ApprovalStoreOptions {
|
|
67
|
+
/** Clock override (tests). Defaults to `Date.now`. */
|
|
68
|
+
readonly now?: () => number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Persisted HITL approval state machine over `node:sqlite`.
|
|
72
|
+
* One instance per gateway process, backed by `<dataDir>/approvals.sqlite`.
|
|
73
|
+
*/
|
|
74
|
+
export declare class ApprovalStore {
|
|
75
|
+
private readonly db;
|
|
76
|
+
private readonly now;
|
|
77
|
+
private constructor();
|
|
78
|
+
static open(path: string, options?: ApprovalStoreOptions): ApprovalStore;
|
|
79
|
+
static openMemory(options?: ApprovalStoreOptions): ApprovalStore;
|
|
80
|
+
close(): void;
|
|
81
|
+
/**
|
|
82
|
+
* Open a new request as `pending`. Throws on invalid input or a duplicate
|
|
83
|
+
* id (ids are runIds — a duplicate means the caller re-requested instead
|
|
84
|
+
* of answering).
|
|
85
|
+
*/
|
|
86
|
+
requestApproval(input: ApprovalRequestInput): ApprovalRequestRecord;
|
|
87
|
+
/** Fetch one request by id, if it exists. */
|
|
88
|
+
getApproval(id: string): ApprovalRequestRecord | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* Record a human decision. Idempotent: only a `pending` row transitions;
|
|
91
|
+
* returns the updated record, or `undefined` when there was nothing
|
|
92
|
+
* pending to resolve (unknown id, already decided, or expired).
|
|
93
|
+
*/
|
|
94
|
+
resolveApproval(id: string, decision: ApprovalHumanDecision, resolvedBy?: string): ApprovalRequestRecord | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* Mark one `pending` request `expired` (gateway shutdown: the waiter is
|
|
97
|
+
* about to be dropped, so the request can never be answered for its
|
|
98
|
+
* turn). Returns the updated record, or `undefined` when not pending.
|
|
99
|
+
*/
|
|
100
|
+
expireApproval(id: string): ApprovalRequestRecord | undefined;
|
|
101
|
+
/** Every still-`pending` request, oldest first. */
|
|
102
|
+
listPendingApprovals(): ApprovalRequestRecord[];
|
|
103
|
+
/**
|
|
104
|
+
* Move every `pending` request past its deadline to `expired`.
|
|
105
|
+
* Idempotent; returns the swept requests (with their new state).
|
|
106
|
+
*/
|
|
107
|
+
sweepExpired(nowMs?: number): ApprovalRequestRecord[];
|
|
108
|
+
/**
|
|
109
|
+
* Boot scan. Past-deadline `pending` rows become `expired`; the survivors
|
|
110
|
+
* are returned for re-announcement (`turn.approval_required`) so a
|
|
111
|
+
* reconnecting client sees them instead of a silent timeout.
|
|
112
|
+
*/
|
|
113
|
+
resumePendingOnBoot(nowMs?: number): {
|
|
114
|
+
resumed: ApprovalRequestRecord[];
|
|
115
|
+
expired: ApprovalRequestRecord[];
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @boombot/security — persisted HITL approval state machine.
|
|
3
|
+
*
|
|
4
|
+
* The single source of truth for the lifecycle of a human-in-the-loop
|
|
5
|
+
* approval request:
|
|
6
|
+
*
|
|
7
|
+
* pending → approved | denied | expired
|
|
8
|
+
*
|
|
9
|
+
* `pending` is the only non-terminal state. A human answers with
|
|
10
|
+
* `resolveApproval(id, 'approved' | 'denied')`; the deadline passing moves
|
|
11
|
+
* the row to `expired` (via `sweepExpired`, called by the live approval
|
|
12
|
+
* timer and by the boot scan). Every transition out of `pending` is guarded
|
|
13
|
+
* by `WHERE status = 'pending'`, so a decision applies exactly once — a
|
|
14
|
+
* retried `turn.approve` or a racing timer can never double-resolve a
|
|
15
|
+
* request.
|
|
16
|
+
*
|
|
17
|
+
* Persistence: `node:sqlite` (built-in, zero dependencies — same as
|
|
18
|
+
* `SqliteSessionStore`), table `approval_requests` in the store's own
|
|
19
|
+
* database file. The gateway opens `<dataDir>/approvals.sqlite`, so the
|
|
20
|
+
* session store's schema (`sessions.sqlite`, incl. its legacy
|
|
21
|
+
* `turn_approvals` checkpoint table) is never migrated by this module and
|
|
22
|
+
* no other worker's migrations can collide with it.
|
|
23
|
+
*
|
|
24
|
+
* Restart semantics (P1-2): rows still `pending` at boot died with the
|
|
25
|
+
* previous process — their in-memory waiters are gone. `resumePendingOnBoot`
|
|
26
|
+
* first sweeps past-deadline rows to `expired`, then returns the survivors
|
|
27
|
+
* so the gateway can re-announce them (`turn.approval_required`) instead of
|
|
28
|
+
* failing them silently. The turn that requested them cannot resume (its
|
|
29
|
+
* loop state died with the old process); answering records the human's
|
|
30
|
+
* decision and the client is told the turn is gone.
|
|
31
|
+
*
|
|
32
|
+
* This module is new code written for BoomBot. It implements the
|
|
33
|
+
* approval-as-persisted-state-machine pattern (cf. the research notes);
|
|
34
|
+
* it does not copy any third-party implementation.
|
|
35
|
+
*/
|
|
36
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
37
|
+
const SCHEMA_SQL = `
|
|
38
|
+
CREATE TABLE IF NOT EXISTS approval_requests (
|
|
39
|
+
id TEXT PRIMARY KEY,
|
|
40
|
+
turn_id TEXT NOT NULL,
|
|
41
|
+
session_id TEXT NOT NULL,
|
|
42
|
+
tool_name TEXT NOT NULL,
|
|
43
|
+
args_json TEXT NOT NULL,
|
|
44
|
+
reason TEXT NOT NULL,
|
|
45
|
+
requested_by TEXT,
|
|
46
|
+
status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'denied', 'expired')),
|
|
47
|
+
requested_at_ms INTEGER NOT NULL,
|
|
48
|
+
expires_at_ms INTEGER NOT NULL,
|
|
49
|
+
resolved_at_ms INTEGER,
|
|
50
|
+
resolved_by TEXT
|
|
51
|
+
);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS approval_requests_pending_idx
|
|
53
|
+
ON approval_requests (status, expires_at_ms);
|
|
54
|
+
`;
|
|
55
|
+
const STATUSES = new Set(['pending', 'approved', 'denied', 'expired']);
|
|
56
|
+
function recordFromRow(row) {
|
|
57
|
+
// The CHECK constraint guarantees the status, but a foreign tool could
|
|
58
|
+
// have written the file: degrade to `expired` (fail-closed — never
|
|
59
|
+
// re-announce or resolve a row we don't understand).
|
|
60
|
+
const status = STATUSES.has(row.status) ? row.status : 'expired';
|
|
61
|
+
return {
|
|
62
|
+
id: row.id,
|
|
63
|
+
turnId: row.turn_id,
|
|
64
|
+
sessionId: row.session_id,
|
|
65
|
+
toolName: row.tool_name,
|
|
66
|
+
argsJson: row.args_json,
|
|
67
|
+
reason: row.reason,
|
|
68
|
+
...(row.requested_by == null ? {} : { requestedBy: row.requested_by }),
|
|
69
|
+
status,
|
|
70
|
+
requestedAtMs: row.requested_at_ms,
|
|
71
|
+
expiresAtMs: row.expires_at_ms,
|
|
72
|
+
resolvedAtMs: row.resolved_at_ms,
|
|
73
|
+
resolvedBy: row.resolved_by,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function assertValidInput(input) {
|
|
77
|
+
for (const [field, value] of [
|
|
78
|
+
['id', input.id],
|
|
79
|
+
['turnId', input.turnId],
|
|
80
|
+
['sessionId', input.sessionId],
|
|
81
|
+
['toolName', input.toolName],
|
|
82
|
+
]) {
|
|
83
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
84
|
+
throw new Error(`ApprovalStore.requestApproval: '${field}' must be a non-empty string`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (typeof input.argsJson !== 'string') {
|
|
88
|
+
throw new Error(`ApprovalStore.requestApproval: 'argsJson' must be a string`);
|
|
89
|
+
}
|
|
90
|
+
if (typeof input.reason !== 'string') {
|
|
91
|
+
throw new Error(`ApprovalStore.requestApproval: 'reason' must be a string`);
|
|
92
|
+
}
|
|
93
|
+
if (!Number.isFinite(input.expiresAtMs)) {
|
|
94
|
+
throw new Error(`ApprovalStore.requestApproval: 'expiresAtMs' must be a finite number`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Persisted HITL approval state machine over `node:sqlite`.
|
|
99
|
+
* One instance per gateway process, backed by `<dataDir>/approvals.sqlite`.
|
|
100
|
+
*/
|
|
101
|
+
export class ApprovalStore {
|
|
102
|
+
db;
|
|
103
|
+
now;
|
|
104
|
+
constructor(db, now) {
|
|
105
|
+
this.db = db;
|
|
106
|
+
this.now = now;
|
|
107
|
+
}
|
|
108
|
+
static open(path, options = {}) {
|
|
109
|
+
const db = new DatabaseSync(path);
|
|
110
|
+
db.exec('PRAGMA journal_mode = WAL;');
|
|
111
|
+
db.exec(SCHEMA_SQL);
|
|
112
|
+
return new ApprovalStore(db, options.now ?? Date.now);
|
|
113
|
+
}
|
|
114
|
+
static openMemory(options = {}) {
|
|
115
|
+
return ApprovalStore.open(':memory:', options);
|
|
116
|
+
}
|
|
117
|
+
close() {
|
|
118
|
+
this.db.close();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Open a new request as `pending`. Throws on invalid input or a duplicate
|
|
122
|
+
* id (ids are runIds — a duplicate means the caller re-requested instead
|
|
123
|
+
* of answering).
|
|
124
|
+
*/
|
|
125
|
+
requestApproval(input) {
|
|
126
|
+
assertValidInput(input);
|
|
127
|
+
const requestedAtMs = this.now();
|
|
128
|
+
this.db
|
|
129
|
+
.prepare(`INSERT INTO approval_requests
|
|
130
|
+
(id, turn_id, session_id, tool_name, args_json, reason, requested_by,
|
|
131
|
+
status, requested_at_ms, expires_at_ms)
|
|
132
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?)`)
|
|
133
|
+
.run(input.id, input.turnId, input.sessionId, input.toolName, input.argsJson, input.reason, input.requestedBy ?? null, requestedAtMs, input.expiresAtMs);
|
|
134
|
+
const record = this.getApproval(input.id);
|
|
135
|
+
if (record === undefined) {
|
|
136
|
+
// Unreachable unless the database misbehaves; fail loud, not silent.
|
|
137
|
+
throw new Error(`ApprovalStore.requestApproval: lost row for id '${input.id}'`);
|
|
138
|
+
}
|
|
139
|
+
return record;
|
|
140
|
+
}
|
|
141
|
+
/** Fetch one request by id, if it exists. */
|
|
142
|
+
getApproval(id) {
|
|
143
|
+
const row = this.db
|
|
144
|
+
.prepare('SELECT * FROM approval_requests WHERE id = ?')
|
|
145
|
+
.get(id);
|
|
146
|
+
return row === undefined ? undefined : recordFromRow(row);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Record a human decision. Idempotent: only a `pending` row transitions;
|
|
150
|
+
* returns the updated record, or `undefined` when there was nothing
|
|
151
|
+
* pending to resolve (unknown id, already decided, or expired).
|
|
152
|
+
*/
|
|
153
|
+
resolveApproval(id, decision, resolvedBy) {
|
|
154
|
+
if (decision !== 'approved' && decision !== 'denied') {
|
|
155
|
+
throw new Error(`ApprovalStore.resolveApproval: invalid decision '${String(decision)}'`);
|
|
156
|
+
}
|
|
157
|
+
const result = this.db
|
|
158
|
+
.prepare(`UPDATE approval_requests
|
|
159
|
+
SET status = ?, resolved_at_ms = ?, resolved_by = ?
|
|
160
|
+
WHERE id = ? AND status = 'pending'`)
|
|
161
|
+
.run(decision, this.now(), resolvedBy ?? null, id);
|
|
162
|
+
if (Number(result.changes) !== 1)
|
|
163
|
+
return undefined;
|
|
164
|
+
return this.getApproval(id);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Mark one `pending` request `expired` (gateway shutdown: the waiter is
|
|
168
|
+
* about to be dropped, so the request can never be answered for its
|
|
169
|
+
* turn). Returns the updated record, or `undefined` when not pending.
|
|
170
|
+
*/
|
|
171
|
+
expireApproval(id) {
|
|
172
|
+
const result = this.db
|
|
173
|
+
.prepare(`UPDATE approval_requests
|
|
174
|
+
SET status = 'expired', resolved_at_ms = ?, resolved_by = 'system:expiry'
|
|
175
|
+
WHERE id = ? AND status = 'pending'`)
|
|
176
|
+
.run(this.now(), id);
|
|
177
|
+
if (Number(result.changes) !== 1)
|
|
178
|
+
return undefined;
|
|
179
|
+
return this.getApproval(id);
|
|
180
|
+
}
|
|
181
|
+
/** Every still-`pending` request, oldest first. */
|
|
182
|
+
listPendingApprovals() {
|
|
183
|
+
const rows = this.db
|
|
184
|
+
.prepare(`SELECT * FROM approval_requests
|
|
185
|
+
WHERE status = 'pending'
|
|
186
|
+
ORDER BY requested_at_ms ASC, rowid ASC`)
|
|
187
|
+
.all();
|
|
188
|
+
return rows.map(recordFromRow);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Move every `pending` request past its deadline to `expired`.
|
|
192
|
+
* Idempotent; returns the swept requests (with their new state).
|
|
193
|
+
*/
|
|
194
|
+
sweepExpired(nowMs = this.now()) {
|
|
195
|
+
const stale = this.db
|
|
196
|
+
.prepare(`SELECT * FROM approval_requests
|
|
197
|
+
WHERE status = 'pending' AND expires_at_ms <= ?
|
|
198
|
+
ORDER BY requested_at_ms ASC, rowid ASC`)
|
|
199
|
+
.all(nowMs);
|
|
200
|
+
if (stale.length === 0)
|
|
201
|
+
return [];
|
|
202
|
+
this.db
|
|
203
|
+
.prepare(`UPDATE approval_requests
|
|
204
|
+
SET status = 'expired', resolved_at_ms = ?, resolved_by = 'system:expiry-sweep'
|
|
205
|
+
WHERE status = 'pending' AND expires_at_ms <= ?`)
|
|
206
|
+
.run(nowMs, nowMs);
|
|
207
|
+
return stale.map((row) => ({
|
|
208
|
+
...recordFromRow(row),
|
|
209
|
+
status: 'expired',
|
|
210
|
+
resolvedAtMs: nowMs,
|
|
211
|
+
resolvedBy: 'system:expiry-sweep',
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Boot scan. Past-deadline `pending` rows become `expired`; the survivors
|
|
216
|
+
* are returned for re-announcement (`turn.approval_required`) so a
|
|
217
|
+
* reconnecting client sees them instead of a silent timeout.
|
|
218
|
+
*/
|
|
219
|
+
resumePendingOnBoot(nowMs = this.now()) {
|
|
220
|
+
const expired = this.sweepExpired(nowMs);
|
|
221
|
+
return { resumed: this.listPendingApprovals(), expired };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Claim } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Split text into sentences and classify each one.
|
|
4
|
+
*
|
|
5
|
+
* - `id` is `claim-1`, `claim-2`, … in document order.
|
|
6
|
+
* - `text` is the sentence with surrounding whitespace and trailing sentence
|
|
7
|
+
* punctuation stripped.
|
|
8
|
+
* - `span` is the `[start, end)` offset of `text` in the original string.
|
|
9
|
+
*/
|
|
10
|
+
export declare function extractClaims(text: string): Claim[];
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic sentence splitter.
|
|
3
|
+
*
|
|
4
|
+
* Splits on sentence-terminating punctuation or newlines, but never on a `.`
|
|
5
|
+
* that directly follows a digit (protects decimals like "3.14", versions like
|
|
6
|
+
* "2.0"). Rule-based and fully deterministic: same input always yields the
|
|
7
|
+
* same claims, spans, and ids.
|
|
8
|
+
*/
|
|
9
|
+
// Boundary = newline run, or punctuation NOT preceded by a digit (lookbehind).
|
|
10
|
+
const SENTENCE_BOUNDARY = /(\r?\n+|(?<!\d)[.!?…]+)/g;
|
|
11
|
+
const TRAILING_PUNCT = /[.!?…]+$/u;
|
|
12
|
+
/**
|
|
13
|
+
* `\b` is ASCII-only: it never matches after letters like à, ó, ẽ. These
|
|
14
|
+
* Unicode-aware boundaries treat every letter/digit as a word character,
|
|
15
|
+
* so patterns containing Vietnamese words (là, có, đô, giờ, …) work.
|
|
16
|
+
*/
|
|
17
|
+
const UWB_BEFORE = '(?<![\\p{L}\\p{N}_])';
|
|
18
|
+
const UWB_AFTER = '(?![\\p{L}\\p{N}_])';
|
|
19
|
+
const GREETING_RE = new RegExp(`^(chào|xin chào|hello|hi|hey|cảm ơn|thanks)${UWB_AFTER}`, 'iu');
|
|
20
|
+
const QUESTION_START_RE = new RegExp(`^(ai|cái gì|tại sao|làm sao|bao giờ|bao nhiêu|what|why|how|when|which|who|where)${UWB_AFTER}`, 'iu');
|
|
21
|
+
const OPINION_RE = /(tôi nghĩ|theo tôi|mình nghĩ|có lẽ|i think|in my opinion|maybe|probably)/iu;
|
|
22
|
+
const INSTRUCTION_RE = /(hãy|đừng|vui lòng|please|bạn nên|you should)/iu;
|
|
23
|
+
const QUOTE_RE = /[""][^""]+[""]|«[^»]+»/u;
|
|
24
|
+
const NUMBER_UNIT_RE = new RegExp(`\\d+(?:\\.\\d+)?\\s*(?:%|percent|USD|VND|đồng|đô|ngày|giờ|phút|giây|tuần|tháng|năm|km|ms|GB|MB|°[cf]|rpm|tokens?|files?|dòng)${UWB_AFTER}`, 'iu');
|
|
25
|
+
const DATE_RE = /\d{1,2}[/-]\d{1,2}[/-]\d{2,4}|\d{4}-\d{2}-\d{2}/;
|
|
26
|
+
const TIME_RE = /\d{1,2}:\d{2}/;
|
|
27
|
+
const CURRENCY_RE = /[$€£¥]/;
|
|
28
|
+
const STATUS_RE = /(hoàn thành|đã xong|thành công|thất bại|đã deploy|đang chạy|đã dừng|đã lưu|đã tạo|đã xóa|completed|done|success|failed|deployed|running|created|deleted)\b/iu;
|
|
29
|
+
const ASSERT_VERB_RE = new RegExp(`${UWB_BEFORE}(là|có|được|sẽ|is|are|was|were|has|have)${UWB_AFTER}`, 'iu');
|
|
30
|
+
const UPPER_RE = /\p{Lu}/u;
|
|
31
|
+
/** A capitalized token that is not the first token of the sentence. */
|
|
32
|
+
function hasMidSentenceCapital(text) {
|
|
33
|
+
const tokens = text.split(/\s+/u).filter((t) => t.length > 0);
|
|
34
|
+
for (let i = 1; i < tokens.length; i += 1) {
|
|
35
|
+
const token = tokens[i];
|
|
36
|
+
if (token !== undefined && UPPER_RE.test(token)) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Classify one sentence. Rules are evaluated in priority order and the first
|
|
44
|
+
* match wins, so classification is stable and explainable.
|
|
45
|
+
*/
|
|
46
|
+
function classify(body, terminator) {
|
|
47
|
+
// 1. Non-falsifiable: questions, greetings, opinions, instructions.
|
|
48
|
+
if (terminator.includes('?') || QUESTION_START_RE.test(body)) {
|
|
49
|
+
return 'non-falsifiable';
|
|
50
|
+
}
|
|
51
|
+
if (GREETING_RE.test(body)) {
|
|
52
|
+
return 'non-falsifiable';
|
|
53
|
+
}
|
|
54
|
+
if (OPINION_RE.test(body)) {
|
|
55
|
+
return 'non-falsifiable';
|
|
56
|
+
}
|
|
57
|
+
if (INSTRUCTION_RE.test(body)) {
|
|
58
|
+
return 'non-falsifiable';
|
|
59
|
+
}
|
|
60
|
+
// 2. Quoted speech.
|
|
61
|
+
if (QUOTE_RE.test(body)) {
|
|
62
|
+
return 'quote';
|
|
63
|
+
}
|
|
64
|
+
// 3. Numbers, dates, times, money.
|
|
65
|
+
if (NUMBER_UNIT_RE.test(body) ||
|
|
66
|
+
DATE_RE.test(body) ||
|
|
67
|
+
TIME_RE.test(body) ||
|
|
68
|
+
CURRENCY_RE.test(body)) {
|
|
69
|
+
return 'factual-number';
|
|
70
|
+
}
|
|
71
|
+
// 4. Status assertions.
|
|
72
|
+
if (STATUS_RE.test(body)) {
|
|
73
|
+
return 'factual-status';
|
|
74
|
+
}
|
|
75
|
+
// 5. Entity assertions: assertive verb + a capitalized token past position 0.
|
|
76
|
+
if (ASSERT_VERB_RE.test(body) && hasMidSentenceCapital(body)) {
|
|
77
|
+
return 'factual-entity';
|
|
78
|
+
}
|
|
79
|
+
// 6. Everything else is not checkable.
|
|
80
|
+
return 'non-falsifiable';
|
|
81
|
+
}
|
|
82
|
+
function terminatorOf(boundary) {
|
|
83
|
+
return boundary.includes('?') ? '?' : boundary;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Split text into sentences and classify each one.
|
|
87
|
+
*
|
|
88
|
+
* - `id` is `claim-1`, `claim-2`, … in document order.
|
|
89
|
+
* - `text` is the sentence with surrounding whitespace and trailing sentence
|
|
90
|
+
* punctuation stripped.
|
|
91
|
+
* - `span` is the `[start, end)` offset of `text` in the original string.
|
|
92
|
+
*/
|
|
93
|
+
export function extractClaims(text) {
|
|
94
|
+
const claims = [];
|
|
95
|
+
let counter = 0;
|
|
96
|
+
const push = (start, end, terminator) => {
|
|
97
|
+
const raw = text.slice(start, end);
|
|
98
|
+
const trimmed = raw.trim();
|
|
99
|
+
if (trimmed.length === 0) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const body = trimmed.replace(TRAILING_PUNCT, '');
|
|
103
|
+
if (body.length === 0) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const spanStart = start + (raw.length - raw.trimStart().length);
|
|
107
|
+
counter += 1;
|
|
108
|
+
claims.push({
|
|
109
|
+
id: `claim-${counter}`,
|
|
110
|
+
text: body,
|
|
111
|
+
kind: classify(body, terminator),
|
|
112
|
+
span: { start: spanStart, end: spanStart + body.length },
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
SENTENCE_BOUNDARY.lastIndex = 0;
|
|
116
|
+
let segStart = 0;
|
|
117
|
+
let m;
|
|
118
|
+
while ((m = SENTENCE_BOUNDARY.exec(text)) !== null) {
|
|
119
|
+
push(segStart, m.index, terminatorOf(m[0]));
|
|
120
|
+
segStart = m.index + m[0].length;
|
|
121
|
+
}
|
|
122
|
+
push(segStart, text.length, '');
|
|
123
|
+
return claims;
|
|
124
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Evidence } from './types.js';
|
|
2
|
+
/** Input accepted by {@link EvidenceLedger.record}. */
|
|
3
|
+
export interface RecordInput {
|
|
4
|
+
readonly tool: string;
|
|
5
|
+
readonly args: unknown;
|
|
6
|
+
readonly output: string;
|
|
7
|
+
readonly error?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Append-only ledger of tool outputs.
|
|
11
|
+
*
|
|
12
|
+
* Every tool execution records an {@link Evidence}: the tool name, a stable
|
|
13
|
+
* hash of its arguments, a bounded excerpt of its output, and a timestamp.
|
|
14
|
+
* The grounding check can only cite evidence that was recorded here, which is
|
|
15
|
+
* what makes "no evidence → ungrounded" enforceable.
|
|
16
|
+
*/
|
|
17
|
+
export declare class EvidenceLedger {
|
|
18
|
+
private readonly maxExcerptChars;
|
|
19
|
+
private readonly entries;
|
|
20
|
+
private counter;
|
|
21
|
+
constructor(maxExcerptChars?: number);
|
|
22
|
+
record(input: RecordInput): Evidence;
|
|
23
|
+
all(): readonly Evidence[];
|
|
24
|
+
clear(): void;
|
|
25
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic, key-sorted JSON serialization used for `argsHash`.
|
|
3
|
+
* Key order never affects the hash: `{a:1,b:2}` and `{b:2,a:1}` hash alike.
|
|
4
|
+
*/
|
|
5
|
+
function stableStringify(value) {
|
|
6
|
+
if (value === null || value === undefined) {
|
|
7
|
+
return String(value);
|
|
8
|
+
}
|
|
9
|
+
if (typeof value === 'string') {
|
|
10
|
+
return JSON.stringify(value);
|
|
11
|
+
}
|
|
12
|
+
if (typeof value === 'number' ||
|
|
13
|
+
typeof value === 'boolean' ||
|
|
14
|
+
typeof value === 'bigint') {
|
|
15
|
+
return String(value);
|
|
16
|
+
}
|
|
17
|
+
if (Array.isArray(value)) {
|
|
18
|
+
return `[${value.map(stableStringify).join(',')}]`;
|
|
19
|
+
}
|
|
20
|
+
if (typeof value === 'object') {
|
|
21
|
+
const record = value;
|
|
22
|
+
const keys = Object.keys(record).sort();
|
|
23
|
+
const parts = keys.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`);
|
|
24
|
+
return `{${parts.join(',')}}`;
|
|
25
|
+
}
|
|
26
|
+
return String(value);
|
|
27
|
+
}
|
|
28
|
+
/** FNV-1a 32-bit, hex-encoded. Pure JS so the ledger stays portable. */
|
|
29
|
+
function fnv1a32Hex(input) {
|
|
30
|
+
let hash = 0x81_1c_9d_c5;
|
|
31
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
32
|
+
hash ^= input.charCodeAt(i);
|
|
33
|
+
hash = Math.imul(hash, 0x01_00_01_93);
|
|
34
|
+
}
|
|
35
|
+
return (hash >>> 0).toString(16).padStart(8, '0');
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Append-only ledger of tool outputs.
|
|
39
|
+
*
|
|
40
|
+
* Every tool execution records an {@link Evidence}: the tool name, a stable
|
|
41
|
+
* hash of its arguments, a bounded excerpt of its output, and a timestamp.
|
|
42
|
+
* The grounding check can only cite evidence that was recorded here, which is
|
|
43
|
+
* what makes "no evidence → ungrounded" enforceable.
|
|
44
|
+
*/
|
|
45
|
+
export class EvidenceLedger {
|
|
46
|
+
maxExcerptChars;
|
|
47
|
+
entries = [];
|
|
48
|
+
counter = 0;
|
|
49
|
+
constructor(maxExcerptChars = 500) {
|
|
50
|
+
this.maxExcerptChars = maxExcerptChars;
|
|
51
|
+
}
|
|
52
|
+
record(input) {
|
|
53
|
+
this.counter += 1;
|
|
54
|
+
const evidence = {
|
|
55
|
+
id: `e${this.counter}`,
|
|
56
|
+
tool: input.tool,
|
|
57
|
+
argsHash: fnv1a32Hex(stableStringify(input.args)),
|
|
58
|
+
excerpt: input.output.slice(0, this.maxExcerptChars),
|
|
59
|
+
ts: Date.now(),
|
|
60
|
+
};
|
|
61
|
+
this.entries.push(evidence);
|
|
62
|
+
return evidence;
|
|
63
|
+
}
|
|
64
|
+
all() {
|
|
65
|
+
return this.entries;
|
|
66
|
+
}
|
|
67
|
+
clear() {
|
|
68
|
+
this.entries.length = 0;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { EvidenceLedger } from './evidence-ledger.js';
|
|
2
|
+
import type { Claim, GuardConfig, VerificationReport } from './types.js';
|
|
3
|
+
/** Result of verifying one agent turn. */
|
|
4
|
+
export interface TurnResult {
|
|
5
|
+
readonly text: string;
|
|
6
|
+
readonly report: VerificationReport;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Result of verifying one agent turn with the extracted claims attached.
|
|
10
|
+
*
|
|
11
|
+
* Callers that annotate the report (e.g. the gateway streaming
|
|
12
|
+
* `turn.verification` with the claim text per verdict) need the claims
|
|
13
|
+
* alongside the report; re-extracting them outside the gate would
|
|
14
|
+
* duplicate the draft-truncation logic.
|
|
15
|
+
*/
|
|
16
|
+
export interface TurnVerification {
|
|
17
|
+
readonly text: string;
|
|
18
|
+
readonly claims: readonly Claim[];
|
|
19
|
+
readonly report: VerificationReport;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Verify a draft response against the tool-output evidence ledger,
|
|
23
|
+
* returning the claims as well as the verified text and the report.
|
|
24
|
+
*
|
|
25
|
+
* Pipeline: bound the draft (`maxClaimChars`) → extract claims →
|
|
26
|
+
* check grounding → apply the mode policy → build the report.
|
|
27
|
+
*
|
|
28
|
+
* `mode: 'off'` short-circuits: the draft passes through untouched with an
|
|
29
|
+
* empty report and no claims.
|
|
30
|
+
*/
|
|
31
|
+
export declare function verifyTurnWithClaims(draft: string, ledger: EvidenceLedger, config: GuardConfig): TurnVerification;
|
|
32
|
+
/**
|
|
33
|
+
* Verify a draft response against the tool-output evidence ledger.
|
|
34
|
+
*
|
|
35
|
+
* Same pipeline as {@link verifyTurnWithClaims}; this convenience form
|
|
36
|
+
* drops the claims when the caller only needs the text and the report.
|
|
37
|
+
*/
|
|
38
|
+
export declare function verifyTurn(draft: string, ledger: EvidenceLedger, config: GuardConfig): TurnResult;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { extractClaims } from './claim-extractor.js';
|
|
2
|
+
import { checkGrounding } from './grounding-check.js';
|
|
3
|
+
import { applyPolicy } from './policy.js';
|
|
4
|
+
import { DEFAULT_GUARD_CONFIG } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Verify a draft response against the tool-output evidence ledger,
|
|
7
|
+
* returning the claims as well as the verified text and the report.
|
|
8
|
+
*
|
|
9
|
+
* Pipeline: bound the draft (`maxClaimChars`) → extract claims →
|
|
10
|
+
* check grounding → apply the mode policy → build the report.
|
|
11
|
+
*
|
|
12
|
+
* `mode: 'off'` short-circuits: the draft passes through untouched with an
|
|
13
|
+
* empty report and no claims.
|
|
14
|
+
*/
|
|
15
|
+
export function verifyTurnWithClaims(draft, ledger, config) {
|
|
16
|
+
const resolved = { ...DEFAULT_GUARD_CONFIG, ...config };
|
|
17
|
+
if (resolved.mode === 'off') {
|
|
18
|
+
return {
|
|
19
|
+
text: draft,
|
|
20
|
+
claims: [],
|
|
21
|
+
report: {
|
|
22
|
+
verdicts: [],
|
|
23
|
+
ungroundedCount: 0,
|
|
24
|
+
mode: 'off',
|
|
25
|
+
msElapsed: 0,
|
|
26
|
+
truncated: false,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const startedAt = Date.now();
|
|
31
|
+
const truncated = draft.length > resolved.maxClaimChars;
|
|
32
|
+
const bounded = truncated ? draft.slice(0, resolved.maxClaimChars) : draft;
|
|
33
|
+
const claims = extractClaims(bounded);
|
|
34
|
+
const verdicts = checkGrounding(claims, ledger.all(), {
|
|
35
|
+
minEntityOverlap: resolved.minEntityOverlap,
|
|
36
|
+
});
|
|
37
|
+
const applied = applyPolicy(bounded, claims, verdicts, resolved.mode);
|
|
38
|
+
const text = truncated ? applied + draft.slice(resolved.maxClaimChars) : applied;
|
|
39
|
+
const ungroundedCount = verdicts.filter((verdict) => verdict.verdict === 'ungrounded').length;
|
|
40
|
+
return {
|
|
41
|
+
text,
|
|
42
|
+
claims,
|
|
43
|
+
report: {
|
|
44
|
+
verdicts,
|
|
45
|
+
ungroundedCount,
|
|
46
|
+
mode: resolved.mode,
|
|
47
|
+
msElapsed: Date.now() - startedAt,
|
|
48
|
+
truncated,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Verify a draft response against the tool-output evidence ledger.
|
|
54
|
+
*
|
|
55
|
+
* Same pipeline as {@link verifyTurnWithClaims}; this convenience form
|
|
56
|
+
* drops the claims when the caller only needs the text and the report.
|
|
57
|
+
*/
|
|
58
|
+
export function verifyTurn(draft, ledger, config) {
|
|
59
|
+
const { text, report } = verifyTurnWithClaims(draft, ledger, config);
|
|
60
|
+
return { text, report };
|
|
61
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Claim, ClaimVerdict, Evidence } from './types.js';
|
|
2
|
+
/** Options for {@link checkGrounding}. */
|
|
3
|
+
export interface GroundingOptions {
|
|
4
|
+
readonly minEntityOverlap: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Verify every claim against the evidence ledger.
|
|
8
|
+
*
|
|
9
|
+
* Conservative by design: when in doubt the claim is `ungrounded`, never
|
|
10
|
+
* silently passed. `non-falsifiable` claims always pass without evidence.
|
|
11
|
+
*/
|
|
12
|
+
export declare function checkGrounding(claims: readonly Claim[], evidences: readonly Evidence[], opts: GroundingOptions): ClaimVerdict[];
|