@theowlops/boombot 0.0.0-stage → 1.1.1
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 +244 -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,820 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn service: runs agent-core turns, bridges loop events to
|
|
3
|
+
* `@boombot/protocol` stream events, persists session messages,
|
|
4
|
+
* and tracks in-flight turns for cancellation.
|
|
5
|
+
*
|
|
6
|
+
* Lifecycle: `submit()` resolves (or creates) the session, persists the
|
|
7
|
+
* user message, then runs the turn in the background and returns
|
|
8
|
+
* `{ turnId, sessionId }` immediately — the client follows progress over
|
|
9
|
+
* SSE (`GET /v1/turns/:turnId/events`). Terminal stream events
|
|
10
|
+
* (`agent.turn.done` / `agent.turn.error`) are published only after the
|
|
11
|
+
* turn's new messages have been persisted.
|
|
12
|
+
*
|
|
13
|
+
* HITL approvals: the service implements agent-core's `ApprovalPort`
|
|
14
|
+
* itself and injects it into the turn's runtime deps, so a policy `'ask'`
|
|
15
|
+
* pauses the turn here instead of being denied. The pause is announced
|
|
16
|
+
* over SSE as `turn.approval_required`; the client answers with the
|
|
17
|
+
* `turn.approve` command (`approveTurn`), or the request times out and
|
|
18
|
+
* the turn ends with an `approval_timeout` error. Each request is
|
|
19
|
+
* persisted in `@boombot/security`'s `ApprovalStore`
|
|
20
|
+
* (`pending` → `approved` | `denied` | `expired`) — the single source of
|
|
21
|
+
* truth for the request lifecycle — so a restart re-announces survivors
|
|
22
|
+
* instead of failing them silently (P1-2).
|
|
23
|
+
*/
|
|
24
|
+
import { randomUUID } from 'node:crypto';
|
|
25
|
+
import { ApprovalTimeoutError, ContextCompactor } from '@boombot/core';
|
|
26
|
+
import { createPolicy } from '@boombot/security';
|
|
27
|
+
import { hashSystemPrompt } from '../session-snapshot.js';
|
|
28
|
+
import { simpleLocalReply } from './local-reply.js';
|
|
29
|
+
import { createTurnMiddlewares } from './middleware.js';
|
|
30
|
+
import { IntentClassifier, executePlannedTurn } from './routing/index.js';
|
|
31
|
+
import { attachHarnessGuard, getToolDefinitions } from './runtime-deps.js';
|
|
32
|
+
import { DEFAULT_APPROVAL_TIMEOUT_MS, } from './service-types.js';
|
|
33
|
+
import { buildSystemPrompt } from './system-prompt.js';
|
|
34
|
+
import { deriveTitle, lastAssistantText, parseApprovalArgs, selectNamedSkill, settleWithin, unrefTimer, } from './turn-utils.js';
|
|
35
|
+
// Re-exported from ./service-types.js for backward compatibility.
|
|
36
|
+
// Existing importers (`./index.ts`, `session-adapter.ts`, `session-tool.ts`,
|
|
37
|
+
// `../types.ts`, `../handlers/turn.ts`) keep importing from './service.js'.
|
|
38
|
+
export { DEFAULT_APPROVAL_TIMEOUT_MS, } from './service-types.js';
|
|
39
|
+
export function createTurnService(deps) {
|
|
40
|
+
return new GatewayTurnService(deps);
|
|
41
|
+
}
|
|
42
|
+
class GatewayTurnService {
|
|
43
|
+
deps;
|
|
44
|
+
activeTurns = new Map();
|
|
45
|
+
activeTasks = new Map();
|
|
46
|
+
/**
|
|
47
|
+
* Recently settled terminal events, keyed by turnId, so a late SSE
|
|
48
|
+
* subscriber can replay a terminal event published before it connected.
|
|
49
|
+
* Bounded: a turn id is unique per process, old entries are evicted.
|
|
50
|
+
*/
|
|
51
|
+
terminalEvents = new Map();
|
|
52
|
+
progressEvents = new Map();
|
|
53
|
+
static MAX_TERMINAL_EVENTS = 200;
|
|
54
|
+
/** In-flight HITL approvals, keyed by runId. */
|
|
55
|
+
pendingApprovals = new Map();
|
|
56
|
+
/** runId → owning turn, so the ApprovalPort can publish SSE with the turnId. */
|
|
57
|
+
runToTurn = new Map();
|
|
58
|
+
constructor(deps) {
|
|
59
|
+
this.deps = deps;
|
|
60
|
+
}
|
|
61
|
+
getTerminalEvent(turnId) {
|
|
62
|
+
return this.terminalEvents.get(turnId);
|
|
63
|
+
}
|
|
64
|
+
getProgressEvents(turnId) {
|
|
65
|
+
return this.progressEvents.get(turnId) ?? [];
|
|
66
|
+
}
|
|
67
|
+
async submit(input) {
|
|
68
|
+
const text = input.input.trim();
|
|
69
|
+
if (text === '') {
|
|
70
|
+
throw new Error('turn input must be a non-empty string');
|
|
71
|
+
}
|
|
72
|
+
// 1. Resolve (or create) the session.
|
|
73
|
+
let sessionId = input.sessionId;
|
|
74
|
+
let history;
|
|
75
|
+
if (sessionId !== undefined) {
|
|
76
|
+
let session = await this.deps.sessions.getSession(sessionId);
|
|
77
|
+
if (session === null) {
|
|
78
|
+
if (input.channel === 'scheduler') {
|
|
79
|
+
const title = `[Lịch chạy] ${input.replyTo ? text.slice(0, 30) + '…' : 'Tự động'}`;
|
|
80
|
+
const created = await this.deps.sessions.createSession({ id: sessionId, title });
|
|
81
|
+
session = created;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
throw new Error(`session not found: ${sessionId}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// For scheduler turns: do not carry massive prior tool outputs/history from previous days/hours
|
|
88
|
+
history = input.channel === 'scheduler' ? [] : session.messages;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const created = await this.deps.sessions.createSession({ title: deriveTitle(text) });
|
|
92
|
+
sessionId = created.id;
|
|
93
|
+
history = created.messages;
|
|
94
|
+
}
|
|
95
|
+
// 2. Persist the user message immediately so history survives turn failures.
|
|
96
|
+
const userMessage = {
|
|
97
|
+
role: 'user',
|
|
98
|
+
content: text,
|
|
99
|
+
...(input.images?.length ? { images: input.images } : {}),
|
|
100
|
+
};
|
|
101
|
+
// Fresh runtime deps per turn, so provider/memory/plugin changes are picked up live.
|
|
102
|
+
const runtimeDeps = await this.deps.getDeps({ sessionId, channel: input.channel, replyTo: input.replyTo });
|
|
103
|
+
const tools = await getToolDefinitions(runtimeDeps);
|
|
104
|
+
const toolInfos = tools.map((t) => ({ name: t.name, description: t.description }));
|
|
105
|
+
const providerSnapshot = await this.deps.describeProvider?.().catch(() => undefined);
|
|
106
|
+
const localReply = simpleLocalReply(text, {
|
|
107
|
+
tools: toolInfos,
|
|
108
|
+
activeProvider: providerSnapshot?.model,
|
|
109
|
+
contextLimit: 257_000,
|
|
110
|
+
port: 8788,
|
|
111
|
+
});
|
|
112
|
+
let skillCatalog;
|
|
113
|
+
let loadedSkill;
|
|
114
|
+
if (localReply === undefined && this.deps.getSkillCatalog !== undefined) {
|
|
115
|
+
try {
|
|
116
|
+
skillCatalog = await this.deps.getSkillCatalog();
|
|
117
|
+
const namedSkill = selectNamedSkill(text, skillCatalog);
|
|
118
|
+
const semanticMatch = namedSkill === undefined
|
|
119
|
+
? await this.deps.matchSkill?.(text, skillCatalog)
|
|
120
|
+
: undefined;
|
|
121
|
+
const selectedSkill = namedSkill ?? semanticMatch?.name;
|
|
122
|
+
if (selectedSkill !== undefined && this.deps.readSkill !== undefined) {
|
|
123
|
+
const skill = await this.deps.readSkill(selectedSkill);
|
|
124
|
+
loadedSkill = {
|
|
125
|
+
name: selectedSkill,
|
|
126
|
+
content: skill.content,
|
|
127
|
+
reason: namedSkill === undefined
|
|
128
|
+
? `semantic match ${semanticMatch?.score.toFixed(3) ?? ''}`.trim()
|
|
129
|
+
: 'the user named it',
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Skill discovery is advisory; a broken user skill directory must not block chat.
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// E3 wire-up: prepend the guard-tagging context notice (when a previous
|
|
138
|
+
// turn of this session left untrusted tool outputs) so the model treats
|
|
139
|
+
// them as data, never as instructions. The notice is derived fresh per
|
|
140
|
+
// turn and never persisted: persistNewMessages filters it out because it
|
|
141
|
+
// is part of baseMessages (same reference).
|
|
142
|
+
const tagNotice = this.deps.guardContext?.buildContextNotice(sessionId);
|
|
143
|
+
const memoryPrompt = this.deps.persistentMemory?.formatForSystemPrompt();
|
|
144
|
+
const baseMessages = [
|
|
145
|
+
{
|
|
146
|
+
role: 'system',
|
|
147
|
+
content: buildSystemPrompt(skillCatalog, loadedSkill, this.deps.assistantInstructions, memoryPrompt, toolInfos),
|
|
148
|
+
},
|
|
149
|
+
...(tagNotice === undefined ? [] : [{ role: 'system', content: tagNotice }]),
|
|
150
|
+
...history, userMessage,
|
|
151
|
+
];
|
|
152
|
+
await this.deps.sessions.appendMessages(sessionId, [userMessage]);
|
|
153
|
+
const runId = randomUUID();
|
|
154
|
+
// Moved up: the middleware chain needs the turn id at registration.
|
|
155
|
+
const turnId = `turn_${randomUUID()}`;
|
|
156
|
+
this.progressList(turnId);
|
|
157
|
+
const turnContext = {
|
|
158
|
+
sessionId,
|
|
159
|
+
messages: baseMessages,
|
|
160
|
+
// Policy-filtered: a tool this turn's policy would deny is never
|
|
161
|
+
// offered, so the model cannot choose its way into a stopped turn.
|
|
162
|
+
tools,
|
|
163
|
+
runId,
|
|
164
|
+
budget: {
|
|
165
|
+
maxRounds: input.channel === 'scheduler' ? 4 : 20,
|
|
166
|
+
maxToolCalls: input.channel === 'scheduler' ? 8 : 64,
|
|
167
|
+
},
|
|
168
|
+
// PR #12 + PR #14 wire-up: run every turn through the middleware
|
|
169
|
+
// onion (adaptive routing + reasoning-guard verification). Built per
|
|
170
|
+
// turn so the verification-snapshot holder never leaks across turns;
|
|
171
|
+
// an empty chain would keep the pipeline's zero-cost old path.
|
|
172
|
+
middlewares: createTurnMiddlewares({
|
|
173
|
+
routingEnabled: this.deps.routing?.enabled ?? true,
|
|
174
|
+
verificationConfig: this.deps.reasoningGuard ?? { mode: 'warn' },
|
|
175
|
+
turnId,
|
|
176
|
+
runId,
|
|
177
|
+
publish: (event) => this.safePublish(event),
|
|
178
|
+
}),
|
|
179
|
+
};
|
|
180
|
+
// 3c. Config snapshot (P1-7): record once per session the exact model,
|
|
181
|
+
// provider, tool count and system prompt serving its turns. The adapter
|
|
182
|
+
// keeps the first capture, so later config drift cannot rewrite history.
|
|
183
|
+
// Best-effort by design: a snapshot failure must never break a turn.
|
|
184
|
+
try {
|
|
185
|
+
const describeProvider = this.deps.describeProvider;
|
|
186
|
+
const providerPart = describeProvider === undefined
|
|
187
|
+
? undefined
|
|
188
|
+
: await describeProvider().catch(() => undefined);
|
|
189
|
+
const systemPrompt = baseMessages[0]?.content ?? '';
|
|
190
|
+
await this.deps.sessions.captureConfigSnapshot?.(sessionId, {
|
|
191
|
+
...(providerPart ?? {}),
|
|
192
|
+
toolsEnabled: turnContext.tools.length,
|
|
193
|
+
systemPromptHash: hashSystemPrompt(systemPrompt),
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
// Observability only — the turn proceeds without a snapshot.
|
|
198
|
+
}
|
|
199
|
+
// 4. Track the turn and run it in the background; submit returns now.
|
|
200
|
+
const controller = new AbortController();
|
|
201
|
+
this.activeTurns.set(turnId, controller);
|
|
202
|
+
this.runToTurn.set(runId, { turnId, sessionId });
|
|
203
|
+
const state = { memoryHits: 0, toolStartedAt: new Map() };
|
|
204
|
+
// 3b. Harness guard: deterministic pre-execution gate for every tool
|
|
205
|
+
// call. Denials never reach the tool handler and are announced on the
|
|
206
|
+
// wire as `guard.blocked` so the client knows why the call was refused.
|
|
207
|
+
attachHarnessGuard(runtimeDeps, createPolicy({
|
|
208
|
+
mode: this.deps.harnessGuard?.mode ?? 'warn',
|
|
209
|
+
writeZones: this.deps.harnessGuard?.writeZones ?? [],
|
|
210
|
+
}), (info) => this.safePublish({
|
|
211
|
+
type: 'guard.blocked',
|
|
212
|
+
payload: {
|
|
213
|
+
turnId,
|
|
214
|
+
runId,
|
|
215
|
+
toolName: info.toolName,
|
|
216
|
+
layer: info.layer,
|
|
217
|
+
reason: info.reason,
|
|
218
|
+
},
|
|
219
|
+
}));
|
|
220
|
+
const task = this.runInBackground(turnId, runId, controller, sessionId, baseMessages, state, runtimeDeps, turnContext, localReply);
|
|
221
|
+
this.activeTasks.set(turnId, task);
|
|
222
|
+
void task.finally(() => this.activeTasks.delete(turnId));
|
|
223
|
+
return { turnId, sessionId };
|
|
224
|
+
}
|
|
225
|
+
async cancel(turnId) {
|
|
226
|
+
const controller = this.activeTurns.get(turnId);
|
|
227
|
+
if (controller === undefined) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
this.activeTurns.delete(turnId);
|
|
231
|
+
controller.abort();
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
async shutdown() {
|
|
235
|
+
// Fail pending HITL approvals first: the human can no longer answer,
|
|
236
|
+
// so each waiting turn ends with an `approval_timeout` error instead of
|
|
237
|
+
// hanging on a promise whose resolvers are about to be dropped.
|
|
238
|
+
const pendings = [...this.pendingApprovals.entries()];
|
|
239
|
+
const pendingTasks = pendings.flatMap(([runId]) => {
|
|
240
|
+
const turnId = this.runToTurn.get(runId)?.turnId;
|
|
241
|
+
const task = turnId === undefined ? undefined : this.activeTasks.get(turnId);
|
|
242
|
+
return task === undefined ? [] : [task];
|
|
243
|
+
});
|
|
244
|
+
this.pendingApprovals.clear();
|
|
245
|
+
const store = this.deps.approvalStore;
|
|
246
|
+
for (const [runId, pending] of pendings) {
|
|
247
|
+
// The waiter is about to be dropped: expire the persisted request so
|
|
248
|
+
// a later boot scan does not resurrect it. Best-effort — the turn
|
|
249
|
+
// still fails even if the store is broken.
|
|
250
|
+
try {
|
|
251
|
+
store?.expireApproval(runId);
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// fall through: the rejection below still ends the turn
|
|
255
|
+
}
|
|
256
|
+
const err = new ApprovalTimeoutError(runId, 0);
|
|
257
|
+
err.message = `gateway shutting down: ${err.message}`;
|
|
258
|
+
pending.reject(err);
|
|
259
|
+
}
|
|
260
|
+
await settleWithin(pendingTasks, 2_000);
|
|
261
|
+
this.runToTurn.clear();
|
|
262
|
+
const controllers = [...this.activeTurns.values()];
|
|
263
|
+
this.activeTurns.clear();
|
|
264
|
+
for (const controller of controllers) {
|
|
265
|
+
controller.abort();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// -- ApprovalPort (HITL) -------------------------------------------------
|
|
269
|
+
async requestApproval(req, signal) {
|
|
270
|
+
const owner = this.runToTurn.get(req.runId);
|
|
271
|
+
if (owner === undefined) {
|
|
272
|
+
throw new Error(`no active turn for approval run ${req.runId}`);
|
|
273
|
+
}
|
|
274
|
+
const timeoutMs = this.deps.approvalTimeoutMs ?? DEFAULT_APPROVAL_TIMEOUT_MS;
|
|
275
|
+
const { turnId, sessionId } = owner;
|
|
276
|
+
// Announce the pause on the wire so the client can prompt a human.
|
|
277
|
+
this.safePublish({
|
|
278
|
+
type: 'turn.approval_required',
|
|
279
|
+
payload: {
|
|
280
|
+
turnId,
|
|
281
|
+
runId: req.runId,
|
|
282
|
+
toolName: req.toolName,
|
|
283
|
+
args: { ...req.args },
|
|
284
|
+
reason: req.reason,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
// Persist first: the state machine is the single source of truth for
|
|
288
|
+
// the request lifecycle; the in-memory waiter below is ephemeral. A
|
|
289
|
+
// broken store degrades to in-memory-only — it never breaks the turn.
|
|
290
|
+
const store = this.deps.approvalStore;
|
|
291
|
+
if (store !== undefined) {
|
|
292
|
+
try {
|
|
293
|
+
store.requestApproval({
|
|
294
|
+
id: req.runId,
|
|
295
|
+
turnId,
|
|
296
|
+
sessionId,
|
|
297
|
+
toolName: req.toolName,
|
|
298
|
+
argsJson: JSON.stringify(req.args),
|
|
299
|
+
reason: req.reason,
|
|
300
|
+
expiresAtMs: Date.now() + timeoutMs,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
// degraded: the waiter below still works; the boot scan just won't see it
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return new Promise((resolve, reject) => {
|
|
308
|
+
const cleanup = () => {
|
|
309
|
+
clearTimeout(timer);
|
|
310
|
+
signal?.removeEventListener('abort', onAbort);
|
|
311
|
+
};
|
|
312
|
+
const onAbort = () => {
|
|
313
|
+
this.pendingApprovals.delete(req.runId);
|
|
314
|
+
cleanup();
|
|
315
|
+
const err = new Error('turn cancelled while awaiting approval');
|
|
316
|
+
err.name = 'AbortError';
|
|
317
|
+
reject(err);
|
|
318
|
+
};
|
|
319
|
+
const timer = setTimeout(() => {
|
|
320
|
+
this.pendingApprovals.delete(req.runId);
|
|
321
|
+
// Enforce the deadline on the persisted row too, so the boot scan
|
|
322
|
+
// never resurrects a request this process already killed.
|
|
323
|
+
try {
|
|
324
|
+
store?.sweepExpired();
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
// degraded: the turn still fails below
|
|
328
|
+
}
|
|
329
|
+
reject(new ApprovalTimeoutError(req.runId, timeoutMs));
|
|
330
|
+
}, timeoutMs);
|
|
331
|
+
// The approval timer must not pin the process (or a test worker) open.
|
|
332
|
+
unrefTimer(timer);
|
|
333
|
+
this.pendingApprovals.set(req.runId, {
|
|
334
|
+
resolve: (verdict) => {
|
|
335
|
+
cleanup();
|
|
336
|
+
resolve(verdict);
|
|
337
|
+
},
|
|
338
|
+
reject: (err) => {
|
|
339
|
+
cleanup();
|
|
340
|
+
reject(err);
|
|
341
|
+
},
|
|
342
|
+
turnId,
|
|
343
|
+
toolName: req.toolName,
|
|
344
|
+
});
|
|
345
|
+
if (signal?.aborted === true) {
|
|
346
|
+
onAbort();
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
async approveTurn(runId, approved, editedArgs) {
|
|
353
|
+
const pending = this.pendingApprovals.get(runId);
|
|
354
|
+
if (pending !== undefined) {
|
|
355
|
+
this.pendingApprovals.delete(runId);
|
|
356
|
+
try {
|
|
357
|
+
this.deps.approvalStore?.resolveApproval(runId, approved ? 'approved' : 'denied');
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
// The verdict was already delivered; persistence is best-effort.
|
|
361
|
+
}
|
|
362
|
+
pending.resolve({ approved, editedArgs });
|
|
363
|
+
return { found: true, resumed: true };
|
|
364
|
+
}
|
|
365
|
+
// Restart path: no live waiter, but the request may still be `pending`
|
|
366
|
+
// in the store. Record the human's decision; the turn itself cannot
|
|
367
|
+
// resume (its loop state died with the previous process).
|
|
368
|
+
try {
|
|
369
|
+
const resolved = this.deps.approvalStore?.resolveApproval(runId, approved ? 'approved' : 'denied');
|
|
370
|
+
if (resolved !== undefined) {
|
|
371
|
+
return { found: true, resumed: false };
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
// fall through to not-found
|
|
376
|
+
}
|
|
377
|
+
return { found: false, resumed: false };
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Re-announce approvals still `pending` from the previous process
|
|
381
|
+
* (P1-2 approval resume). Called once at gateway boot, before the HTTP
|
|
382
|
+
* server starts listening: every survivor is re-published as
|
|
383
|
+
* `turn.approval_required` (payload flag `resumed: true`) and retained in
|
|
384
|
+
* the per-turn progress replay, so a client opening
|
|
385
|
+
* `GET /v1/turns/:turnId/events` after boot still sees the pending
|
|
386
|
+
* approval instead of a silent timeout. Past-deadline requests are marked
|
|
387
|
+
* `expired` by the store and are NOT re-announced. Returns the number
|
|
388
|
+
* re-announced.
|
|
389
|
+
*
|
|
390
|
+
* Honest limit: the turn that requested the approval is gone (its loop
|
|
391
|
+
* state died with the old process). Answering via `turn.approve` records
|
|
392
|
+
* the human's decision; `approveTurn` reports `{ found: true, resumed:
|
|
393
|
+
* false }` for these.
|
|
394
|
+
*/
|
|
395
|
+
reannouncePendingApprovals() {
|
|
396
|
+
const store = this.deps.approvalStore;
|
|
397
|
+
if (store === undefined)
|
|
398
|
+
return 0;
|
|
399
|
+
let resumed;
|
|
400
|
+
try {
|
|
401
|
+
resumed = store.resumePendingOnBoot().resumed;
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
for (const req of resumed) {
|
|
407
|
+
// Retain for late SSE subscribers: the turn never ran in this
|
|
408
|
+
// process, so no progress list exists for it yet.
|
|
409
|
+
this.progressList(req.turnId);
|
|
410
|
+
this.safePublish({
|
|
411
|
+
type: 'turn.approval_required',
|
|
412
|
+
payload: {
|
|
413
|
+
turnId: req.turnId,
|
|
414
|
+
runId: req.id,
|
|
415
|
+
toolName: req.toolName,
|
|
416
|
+
args: parseApprovalArgs(req.argsJson),
|
|
417
|
+
reason: req.reason,
|
|
418
|
+
resumed: true,
|
|
419
|
+
},
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
return resumed.length;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Get-or-create the per-turn progress replay list, evicting the oldest
|
|
426
|
+
* turn when the bound is hit. Used for live turns and for approvals
|
|
427
|
+
* re-announced after a restart (their turns never ran in this process).
|
|
428
|
+
*/
|
|
429
|
+
progressList(turnId) {
|
|
430
|
+
let list = this.progressEvents.get(turnId);
|
|
431
|
+
if (list === undefined) {
|
|
432
|
+
if (this.progressEvents.size >= GatewayTurnService.MAX_TERMINAL_EVENTS) {
|
|
433
|
+
const oldest = this.progressEvents.keys().next();
|
|
434
|
+
if (!oldest.done)
|
|
435
|
+
this.progressEvents.delete(oldest.value);
|
|
436
|
+
}
|
|
437
|
+
list = [];
|
|
438
|
+
this.progressEvents.set(turnId, list);
|
|
439
|
+
}
|
|
440
|
+
return list;
|
|
441
|
+
}
|
|
442
|
+
async runInBackground(turnId, runId, controller, sessionId, baseMessages, state, runtimeDeps, turnContext, localReply) {
|
|
443
|
+
try {
|
|
444
|
+
if (localReply !== undefined) {
|
|
445
|
+
this.bridgeEvent(turnId, state, { type: 'turn.start', sessionId, runId });
|
|
446
|
+
const result = {
|
|
447
|
+
status: 'completed',
|
|
448
|
+
messages: [...baseMessages, { role: 'assistant', content: localReply }],
|
|
449
|
+
rounds: 0,
|
|
450
|
+
toolCallsExecuted: 0,
|
|
451
|
+
toolCallsDenied: 0,
|
|
452
|
+
};
|
|
453
|
+
await this.persistNewMessages(turnId, sessionId, baseMessages, 0, result.messages);
|
|
454
|
+
this.publishTerminal(turnId, result);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
// Apply Context Auto-Compaction with dynamic provider context limits
|
|
458
|
+
const limits = this.deps.resolveContextLimits
|
|
459
|
+
? await this.deps.resolveContextLimits()
|
|
460
|
+
: { maxContextTokens: 128_000, maxContextChars: 512_000, triggerThreshold: 0.75, hotZoneRounds: 4 };
|
|
461
|
+
const compactor = new ContextCompactor({
|
|
462
|
+
maxContextTokens: limits.maxContextTokens,
|
|
463
|
+
triggerThreshold: limits.triggerThreshold ?? 0.75,
|
|
464
|
+
hotZoneRounds: limits.hotZoneRounds ?? 4,
|
|
465
|
+
});
|
|
466
|
+
const compactionRes = await compactor.compact(turnContext.messages);
|
|
467
|
+
const effectiveContext = compactionRes.compacted
|
|
468
|
+
? { ...turnContext, messages: compactionRes.messages }
|
|
469
|
+
: turnContext;
|
|
470
|
+
if (compactionRes.compacted) {
|
|
471
|
+
this.safePublish({
|
|
472
|
+
type: 'turn.compacted',
|
|
473
|
+
payload: {
|
|
474
|
+
turnId,
|
|
475
|
+
prunedTurns: compactionRes.prunedTurnsCount,
|
|
476
|
+
originalTokens: compactionRes.originalTokens,
|
|
477
|
+
compactedTokens: compactionRes.compactedTokens,
|
|
478
|
+
},
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
// The service is its own ApprovalPort: a policy 'ask' pauses the turn
|
|
482
|
+
// here until `approveTurn` (or the timeout) resolves the verdict.
|
|
483
|
+
const depsWithApproval = { ...runtimeDeps, approval: this };
|
|
484
|
+
// Dynamic Macro Routing: Evaluate intent to decide between Planner vs ReAct
|
|
485
|
+
const latestUserContent = [...effectiveContext.messages].reverse().find((m) => m.role === 'user')?.content ?? '';
|
|
486
|
+
const decision = IntentClassifier.classify(latestUserContent);
|
|
487
|
+
let result;
|
|
488
|
+
if (decision.mode === 'planner') {
|
|
489
|
+
this.safePublish({
|
|
490
|
+
type: 'turn.routed',
|
|
491
|
+
payload: {
|
|
492
|
+
turnId,
|
|
493
|
+
runId,
|
|
494
|
+
route: 'deep',
|
|
495
|
+
reason: `planner:${decision.reason}`,
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
result = await executePlannedTurn({
|
|
499
|
+
goal: decision.goal,
|
|
500
|
+
turnId,
|
|
501
|
+
runId,
|
|
502
|
+
baseContext: effectiveContext,
|
|
503
|
+
runtimeDeps: depsWithApproval,
|
|
504
|
+
runTurn: this.deps.runTurn,
|
|
505
|
+
onEvent: (event) => this.bridgeEvent(turnId, state, event),
|
|
506
|
+
signal: controller.signal,
|
|
507
|
+
onPhaseProgress: (phase, total, title) => {
|
|
508
|
+
this.safePublish({
|
|
509
|
+
type: 'task.progress',
|
|
510
|
+
payload: {
|
|
511
|
+
taskId: turnId,
|
|
512
|
+
stepIndex: phase - 1,
|
|
513
|
+
totalSteps: total,
|
|
514
|
+
detail: title,
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
result = await this.deps.runTurn(depsWithApproval, effectiveContext, (event) => this.bridgeEvent(turnId, state, event), controller.signal);
|
|
522
|
+
}
|
|
523
|
+
// E3 wire-up: tag this turn's tool outputs (trusted/untrusted) into
|
|
524
|
+
// the gateway guard context so the next turn of the same session can
|
|
525
|
+
// see them, and announce each created tag on the wire (`guard.tagged`).
|
|
526
|
+
// Tagging never breaks turn settlement.
|
|
527
|
+
try {
|
|
528
|
+
const records = this.deps.guardContext?.tagTurnOutputs({
|
|
529
|
+
sessionId,
|
|
530
|
+
turnId,
|
|
531
|
+
runId,
|
|
532
|
+
messages: result.messages,
|
|
533
|
+
});
|
|
534
|
+
for (const record of records ?? []) {
|
|
535
|
+
try {
|
|
536
|
+
this.safePublish({
|
|
537
|
+
type: 'guard.tagged',
|
|
538
|
+
payload: {
|
|
539
|
+
turnId,
|
|
540
|
+
runId,
|
|
541
|
+
toolName: record.toolName,
|
|
542
|
+
trust: record.tag.trust,
|
|
543
|
+
callId: record.callId || undefined,
|
|
544
|
+
patterns: record.patterns.length > 0 ? [...record.patterns] : undefined,
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
catch {
|
|
549
|
+
// Intentionally swallowed: one broken stream event must not
|
|
550
|
+
// hide the remaining tags or break turn settlement.
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
// Intentionally swallowed: persistence and terminal events continue.
|
|
556
|
+
}
|
|
557
|
+
await this.persistNewMessages(turnId, sessionId, baseMessages, state.memoryHits, result.messages, compactionRes.compacted, effectiveContext.messages.length);
|
|
558
|
+
this.publishTerminal(turnId, result);
|
|
559
|
+
}
|
|
560
|
+
catch (err) {
|
|
561
|
+
// runTurn is not supposed to reject (cancellation and errors become
|
|
562
|
+
// TurnResult), but a background turn must never die silently.
|
|
563
|
+
this.safePublish({
|
|
564
|
+
type: 'agent.turn.error',
|
|
565
|
+
payload: {
|
|
566
|
+
turnId,
|
|
567
|
+
error: err instanceof Error ? err.message : 'turn failed unexpectedly',
|
|
568
|
+
retryable: true,
|
|
569
|
+
},
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
finally {
|
|
573
|
+
if (this.activeTurns.get(turnId) === controller) {
|
|
574
|
+
this.activeTurns.delete(turnId);
|
|
575
|
+
}
|
|
576
|
+
this.runToTurn.delete(runId);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Map loop events to wire stream events. The terminal `turn.end` is NOT
|
|
581
|
+
* published here — it goes out via `publishTerminal` after persistence.
|
|
582
|
+
*
|
|
583
|
+
* Additive mapping: the typed `turn.*` events (turn.start, turn.tool_start,
|
|
584
|
+
* turn.tool_end, turn.approval_required) are published alongside the
|
|
585
|
+
* legacy `agent.turn.*` events, which keep their exact shapes.
|
|
586
|
+
*/
|
|
587
|
+
bridgeEvent(turnId, state, event) {
|
|
588
|
+
switch (event.type) {
|
|
589
|
+
case 'turn.start': {
|
|
590
|
+
state.runId = event.runId;
|
|
591
|
+
this.safePublish({
|
|
592
|
+
type: 'turn.start',
|
|
593
|
+
payload: { turnId, runId: event.runId },
|
|
594
|
+
});
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
case 'turn.routed': {
|
|
598
|
+
this.safePublish({
|
|
599
|
+
type: 'turn.routed',
|
|
600
|
+
payload: {
|
|
601
|
+
turnId,
|
|
602
|
+
runId: event.runId,
|
|
603
|
+
route: event.mode,
|
|
604
|
+
reason: event.reason,
|
|
605
|
+
},
|
|
606
|
+
});
|
|
607
|
+
break;
|
|
608
|
+
}
|
|
609
|
+
case 'turn.escalated': {
|
|
610
|
+
// The routing middleware only ever escalates fast → deep, at most
|
|
611
|
+
// once per turn (routing.ts: the `finalize` hook checks
|
|
612
|
+
// `state.mode === 'fast'` and flips it to `'deep'` with reason
|
|
613
|
+
// `guard:unsupported-claims`) — so the wire fields are fixed here
|
|
614
|
+
// rather than carried on the internal event.
|
|
615
|
+
this.safePublish({
|
|
616
|
+
type: 'turn.escalated',
|
|
617
|
+
payload: {
|
|
618
|
+
turnId,
|
|
619
|
+
runId: event.runId,
|
|
620
|
+
from: 'fast',
|
|
621
|
+
to: 'deep',
|
|
622
|
+
trigger: event.reason,
|
|
623
|
+
},
|
|
624
|
+
});
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
case 'text.delta': {
|
|
628
|
+
this.safePublish({
|
|
629
|
+
type: 'agent.turn.token',
|
|
630
|
+
payload: { turnId, delta: event.text },
|
|
631
|
+
});
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
634
|
+
case 'reasoning.delta': {
|
|
635
|
+
// Reasoning fragments are a separate stream from the final answer:
|
|
636
|
+
// they must not flow through `agent.turn.token` (which clients render
|
|
637
|
+
// as the answer) so web UI can render them in a collapsible block.
|
|
638
|
+
this.safePublish({
|
|
639
|
+
type: 'turn.reasoning_delta',
|
|
640
|
+
payload: {
|
|
641
|
+
turnId,
|
|
642
|
+
...(state.runId !== undefined ? { runId: state.runId } : {}),
|
|
643
|
+
delta: event.text,
|
|
644
|
+
},
|
|
645
|
+
});
|
|
646
|
+
break;
|
|
647
|
+
}
|
|
648
|
+
case 'tool.call': {
|
|
649
|
+
this.safePublish({
|
|
650
|
+
type: 'agent.turn.tool_call',
|
|
651
|
+
payload: {
|
|
652
|
+
turnId,
|
|
653
|
+
toolCall: {
|
|
654
|
+
id: event.call.id,
|
|
655
|
+
name: event.call.name,
|
|
656
|
+
arguments: { ...event.call.arguments },
|
|
657
|
+
startedAt: new Date().toISOString(),
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
});
|
|
661
|
+
if (state.runId !== undefined) {
|
|
662
|
+
state.toolStartedAt.set(event.call.id, Date.now());
|
|
663
|
+
this.safePublish({
|
|
664
|
+
type: 'turn.tool_start',
|
|
665
|
+
payload: {
|
|
666
|
+
turnId,
|
|
667
|
+
runId: state.runId,
|
|
668
|
+
toolCallId: event.call.id,
|
|
669
|
+
toolName: event.call.name,
|
|
670
|
+
},
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
break;
|
|
674
|
+
}
|
|
675
|
+
case 'tool.result': {
|
|
676
|
+
if (state.runId !== undefined) {
|
|
677
|
+
const startedAt = state.toolStartedAt.get(event.result.callId);
|
|
678
|
+
state.toolStartedAt.delete(event.result.callId);
|
|
679
|
+
this.safePublish({
|
|
680
|
+
type: 'turn.tool_end',
|
|
681
|
+
payload: {
|
|
682
|
+
turnId,
|
|
683
|
+
runId: state.runId,
|
|
684
|
+
toolCallId: event.result.callId,
|
|
685
|
+
toolName: event.result.name,
|
|
686
|
+
durationMs: startedAt === undefined ? 0 : Math.max(0, Date.now() - startedAt),
|
|
687
|
+
status: event.result.error ? 'error' : 'ok',
|
|
688
|
+
},
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
case 'policy.deny': {
|
|
694
|
+
// Hard policy denials surface as warnings. Approval-required calls
|
|
695
|
+
// use the separate `approval.pending` path and never enter this case.
|
|
696
|
+
this.safePublish({
|
|
697
|
+
type: 'agent.turn.error',
|
|
698
|
+
payload: {
|
|
699
|
+
turnId,
|
|
700
|
+
error: `policy: ${event.reason} — tool '${event.call.name}' was blocked (accident-prevention)`,
|
|
701
|
+
retryable: false,
|
|
702
|
+
},
|
|
703
|
+
});
|
|
704
|
+
break;
|
|
705
|
+
}
|
|
706
|
+
case 'memory.hits': {
|
|
707
|
+
state.memoryHits = event.count;
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
case 'turn.end':
|
|
711
|
+
default: {
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
publishTerminal(turnId, result) {
|
|
717
|
+
if (result.status === 'error') {
|
|
718
|
+
this.safePublish({
|
|
719
|
+
type: 'agent.turn.error',
|
|
720
|
+
payload: {
|
|
721
|
+
turnId,
|
|
722
|
+
error: result.error ?? 'turn failed',
|
|
723
|
+
retryable: true,
|
|
724
|
+
},
|
|
725
|
+
});
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
const payload = { turnId, status: result.status };
|
|
729
|
+
const output = lastAssistantText(result.messages);
|
|
730
|
+
if (output !== '') {
|
|
731
|
+
payload.output = output;
|
|
732
|
+
}
|
|
733
|
+
if (result.usage !== undefined)
|
|
734
|
+
payload.usage = result.usage;
|
|
735
|
+
this.safePublish({ type: 'agent.turn.done', payload });
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Persist the messages the turn produced. `buildPrompt` unshifts exactly
|
|
739
|
+
* one derived system message when memory hits exist — skip it so derived
|
|
740
|
+
* memory context is not duplicated into history on every turn.
|
|
741
|
+
*/
|
|
742
|
+
async persistNewMessages(turnId, sessionId, baseMessages, memoryHits, resultMessages, wasCompacted = false, effectiveContextLength = 0) {
|
|
743
|
+
const withoutInjected = memoryHits > 0 ? resultMessages.slice(1) : resultMessages;
|
|
744
|
+
let added;
|
|
745
|
+
if (wasCompacted && effectiveContextLength > 0) {
|
|
746
|
+
added = withoutInjected.slice(effectiveContextLength);
|
|
747
|
+
// Fallback: đảm bảo không bao giờ bị rơi mất câu trả lời của assistant
|
|
748
|
+
if (added.length === 0 && withoutInjected.length > 0) {
|
|
749
|
+
const last = withoutInjected[withoutInjected.length - 1];
|
|
750
|
+
if (last && last.role === 'assistant')
|
|
751
|
+
added = [last];
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
else {
|
|
755
|
+
added = withoutInjected.slice(baseMessages.length);
|
|
756
|
+
}
|
|
757
|
+
if (added.length === 0) {
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
try {
|
|
761
|
+
await this.deps.sessions.appendMessages(sessionId, added);
|
|
762
|
+
}
|
|
763
|
+
catch (err) {
|
|
764
|
+
this.safePublish({
|
|
765
|
+
type: 'agent.turn.error',
|
|
766
|
+
payload: {
|
|
767
|
+
turnId,
|
|
768
|
+
error: `failed to persist turn messages: ${err instanceof Error ? err.message : 'unknown error'}`,
|
|
769
|
+
retryable: false,
|
|
770
|
+
},
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/** The event bus must never break a turn; terminal state still settles. */
|
|
775
|
+
safePublish(event) {
|
|
776
|
+
if (event.type === 'turn.start' ||
|
|
777
|
+
event.type === 'turn.tool_start' ||
|
|
778
|
+
event.type === 'turn.tool_end' ||
|
|
779
|
+
// P1-2: a late subscriber to the turn stream must see the pending
|
|
780
|
+
// approval (live pause or boot re-announcement), not wait forever.
|
|
781
|
+
event.type === 'turn.approval_required') {
|
|
782
|
+
const { turnId } = event.payload;
|
|
783
|
+
const history = this.progressEvents.get(turnId);
|
|
784
|
+
history?.push(event);
|
|
785
|
+
// ponytail: latest 257 lifecycle events for 200 turns; durable replay needs a store.
|
|
786
|
+
if (history && history.length > 257)
|
|
787
|
+
history.shift();
|
|
788
|
+
}
|
|
789
|
+
try {
|
|
790
|
+
this.deps.publish(event);
|
|
791
|
+
}
|
|
792
|
+
catch {
|
|
793
|
+
// Intentionally swallowed: persistence and turn settlement continue.
|
|
794
|
+
}
|
|
795
|
+
this.rememberTerminal(event);
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Retain terminal events for late SSE subscribers. Runs after the publish
|
|
799
|
+
* attempt so a broken bus still leaves the replay available.
|
|
800
|
+
*/
|
|
801
|
+
rememberTerminal(event) {
|
|
802
|
+
if (event.type !== 'agent.turn.done' && event.type !== 'agent.turn.error') {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
const turnId = event.payload.turnId;
|
|
806
|
+
if (typeof turnId !== 'string' || turnId.length === 0)
|
|
807
|
+
return;
|
|
808
|
+
if (this.terminalEvents.size >= GatewayTurnService.MAX_TERMINAL_EVENTS) {
|
|
809
|
+
const oldest = this.terminalEvents.keys().next();
|
|
810
|
+
if (!oldest.done)
|
|
811
|
+
this.terminalEvents.delete(oldest.value);
|
|
812
|
+
}
|
|
813
|
+
this.terminalEvents.set(turnId, event);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Parse persisted approval args for the re-announced SSE payload.
|
|
818
|
+
* Corrupt JSON degrades to `{}` — the approver still sees the tool name
|
|
819
|
+
* and reason, and the row keeps the original bytes.
|
|
820
|
+
*/
|