im-hub-pro 0.2.29
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/LICENSE +21 -0
- package/README.md +497 -0
- package/README.zh-CN.md +496 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +921 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/acp-server.d.ts +8 -0
- package/dist/core/acp-server.d.ts.map +1 -0
- package/dist/core/acp-server.js +266 -0
- package/dist/core/acp-server.js.map +1 -0
- package/dist/core/agent-base.d.ts +94 -0
- package/dist/core/agent-base.d.ts.map +1 -0
- package/dist/core/agent-base.js +374 -0
- package/dist/core/agent-base.js.map +1 -0
- package/dist/core/agent-cwd.d.ts +45 -0
- package/dist/core/agent-cwd.d.ts.map +1 -0
- package/dist/core/agent-cwd.js +178 -0
- package/dist/core/agent-cwd.js.map +1 -0
- package/dist/core/agent-cwd.test.d.ts +2 -0
- package/dist/core/agent-cwd.test.d.ts.map +1 -0
- package/dist/core/agent-cwd.test.js +149 -0
- package/dist/core/agent-cwd.test.js.map +1 -0
- package/dist/core/approval-bus.d.ts +232 -0
- package/dist/core/approval-bus.d.ts.map +1 -0
- package/dist/core/approval-bus.js +703 -0
- package/dist/core/approval-bus.js.map +1 -0
- package/dist/core/approval-bus.synthetic.test.d.ts +2 -0
- package/dist/core/approval-bus.synthetic.test.d.ts.map +1 -0
- package/dist/core/approval-bus.synthetic.test.js +182 -0
- package/dist/core/approval-bus.synthetic.test.js.map +1 -0
- package/dist/core/approval-bus.test.d.ts +2 -0
- package/dist/core/approval-bus.test.d.ts.map +1 -0
- package/dist/core/approval-bus.test.js +537 -0
- package/dist/core/approval-bus.test.js.map +1 -0
- package/dist/core/approval-router.d.ts +95 -0
- package/dist/core/approval-router.d.ts.map +1 -0
- package/dist/core/approval-router.js +450 -0
- package/dist/core/approval-router.js.map +1 -0
- package/dist/core/approval-router.test.d.ts +2 -0
- package/dist/core/approval-router.test.d.ts.map +1 -0
- package/dist/core/approval-router.test.js +413 -0
- package/dist/core/approval-router.test.js.map +1 -0
- package/dist/core/audit-log.d.ts +55 -0
- package/dist/core/audit-log.d.ts.map +1 -0
- package/dist/core/audit-log.js +203 -0
- package/dist/core/audit-log.js.map +1 -0
- package/dist/core/bgjob-reader.d.ts +65 -0
- package/dist/core/bgjob-reader.d.ts.map +1 -0
- package/dist/core/bgjob-reader.js +212 -0
- package/dist/core/bgjob-reader.js.map +1 -0
- package/dist/core/bgjob-reader.test.d.ts +2 -0
- package/dist/core/bgjob-reader.test.d.ts.map +1 -0
- package/dist/core/bgjob-reader.test.js +178 -0
- package/dist/core/bgjob-reader.test.js.map +1 -0
- package/dist/core/circuit-breaker.d.ts +37 -0
- package/dist/core/circuit-breaker.d.ts.map +1 -0
- package/dist/core/circuit-breaker.js +115 -0
- package/dist/core/circuit-breaker.js.map +1 -0
- package/dist/core/commands/agent.d.ts +4 -0
- package/dist/core/commands/agent.d.ts.map +1 -0
- package/dist/core/commands/agent.js +21 -0
- package/dist/core/commands/agent.js.map +1 -0
- package/dist/core/commands/approval.d.ts +3 -0
- package/dist/core/commands/approval.d.ts.map +1 -0
- package/dist/core/commands/approval.js +44 -0
- package/dist/core/commands/approval.js.map +1 -0
- package/dist/core/commands/approval.test.d.ts +2 -0
- package/dist/core/commands/approval.test.d.ts.map +1 -0
- package/dist/core/commands/approval.test.js +85 -0
- package/dist/core/commands/approval.test.js.map +1 -0
- package/dist/core/commands/audit.d.ts +3 -0
- package/dist/core/commands/audit.d.ts.map +1 -0
- package/dist/core/commands/audit.js +84 -0
- package/dist/core/commands/audit.js.map +1 -0
- package/dist/core/commands/builtin.d.ts +3 -0
- package/dist/core/commands/builtin.d.ts.map +1 -0
- package/dist/core/commands/builtin.js +26 -0
- package/dist/core/commands/builtin.js.map +1 -0
- package/dist/core/commands/job.d.ts +3 -0
- package/dist/core/commands/job.d.ts.map +1 -0
- package/dist/core/commands/job.js +195 -0
- package/dist/core/commands/job.js.map +1 -0
- package/dist/core/commands/model.d.ts +9 -0
- package/dist/core/commands/model.d.ts.map +1 -0
- package/dist/core/commands/model.js +183 -0
- package/dist/core/commands/model.js.map +1 -0
- package/dist/core/commands/plan.d.ts +3 -0
- package/dist/core/commands/plan.d.ts.map +1 -0
- package/dist/core/commands/plan.js +75 -0
- package/dist/core/commands/plan.js.map +1 -0
- package/dist/core/commands/plan.test.d.ts +2 -0
- package/dist/core/commands/plan.test.d.ts.map +1 -0
- package/dist/core/commands/plan.test.js +122 -0
- package/dist/core/commands/plan.test.js.map +1 -0
- package/dist/core/commands/router.d.ts +3 -0
- package/dist/core/commands/router.d.ts.map +1 -0
- package/dist/core/commands/router.js +71 -0
- package/dist/core/commands/router.js.map +1 -0
- package/dist/core/commands/schedule.d.ts +3 -0
- package/dist/core/commands/schedule.d.ts.map +1 -0
- package/dist/core/commands/schedule.js +123 -0
- package/dist/core/commands/schedule.js.map +1 -0
- package/dist/core/commands/sessions.d.ts +3 -0
- package/dist/core/commands/sessions.d.ts.map +1 -0
- package/dist/core/commands/sessions.js +88 -0
- package/dist/core/commands/sessions.js.map +1 -0
- package/dist/core/commands/stats.d.ts +3 -0
- package/dist/core/commands/stats.d.ts.map +1 -0
- package/dist/core/commands/stats.js +73 -0
- package/dist/core/commands/stats.js.map +1 -0
- package/dist/core/commands/think.d.ts +3 -0
- package/dist/core/commands/think.d.ts.map +1 -0
- package/dist/core/commands/think.js +28 -0
- package/dist/core/commands/think.js.map +1 -0
- package/dist/core/commands/workspaces.d.ts +3 -0
- package/dist/core/commands/workspaces.d.ts.map +1 -0
- package/dist/core/commands/workspaces.js +47 -0
- package/dist/core/commands/workspaces.js.map +1 -0
- package/dist/core/config-schema.d.ts +58 -0
- package/dist/core/config-schema.d.ts.map +1 -0
- package/dist/core/config-schema.js +63 -0
- package/dist/core/config-schema.js.map +1 -0
- package/dist/core/cron.d.ts +29 -0
- package/dist/core/cron.d.ts.map +1 -0
- package/dist/core/cron.js +184 -0
- package/dist/core/cron.js.map +1 -0
- package/dist/core/event-bus.d.ts +80 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +62 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/intent-llm.d.ts +27 -0
- package/dist/core/intent-llm.d.ts.map +1 -0
- package/dist/core/intent-llm.js +170 -0
- package/dist/core/intent-llm.js.map +1 -0
- package/dist/core/intent.d.ts +12 -0
- package/dist/core/intent.d.ts.map +1 -0
- package/dist/core/intent.js +187 -0
- package/dist/core/intent.js.map +1 -0
- package/dist/core/job-board.d.ts +84 -0
- package/dist/core/job-board.d.ts.map +1 -0
- package/dist/core/job-board.js +379 -0
- package/dist/core/job-board.js.map +1 -0
- package/dist/core/logger.d.ts +6 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +54 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/metrics.d.ts +55 -0
- package/dist/core/metrics.d.ts.map +1 -0
- package/dist/core/metrics.js +291 -0
- package/dist/core/metrics.js.map +1 -0
- package/dist/core/onboarding.d.ts +94 -0
- package/dist/core/onboarding.d.ts.map +1 -0
- package/dist/core/onboarding.js +426 -0
- package/dist/core/onboarding.js.map +1 -0
- package/dist/core/onboarding.test.d.ts +2 -0
- package/dist/core/onboarding.test.d.ts.map +1 -0
- package/dist/core/onboarding.test.js +112 -0
- package/dist/core/onboarding.test.js.map +1 -0
- package/dist/core/rate-limiter.d.ts +44 -0
- package/dist/core/rate-limiter.d.ts.map +1 -0
- package/dist/core/rate-limiter.js +115 -0
- package/dist/core/rate-limiter.js.map +1 -0
- package/dist/core/registry.d.ts +32 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +122 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/router.d.ts +41 -0
- package/dist/core/router.d.ts.map +1 -0
- package/dist/core/router.js +431 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/schedule.d.ts +65 -0
- package/dist/core/schedule.d.ts.map +1 -0
- package/dist/core/schedule.js +316 -0
- package/dist/core/schedule.js.map +1 -0
- package/dist/core/session-subtasks.test.d.ts +2 -0
- package/dist/core/session-subtasks.test.d.ts.map +1 -0
- package/dist/core/session-subtasks.test.js +88 -0
- package/dist/core/session-subtasks.test.js.map +1 -0
- package/dist/core/session.d.ts +182 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +774 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/sqlite-helper.d.ts +37 -0
- package/dist/core/sqlite-helper.d.ts.map +1 -0
- package/dist/core/sqlite-helper.js +79 -0
- package/dist/core/sqlite-helper.js.map +1 -0
- package/dist/core/transcribe.d.ts +25 -0
- package/dist/core/transcribe.d.ts.map +1 -0
- package/dist/core/transcribe.js +217 -0
- package/dist/core/transcribe.js.map +1 -0
- package/dist/core/transcribe.test.d.ts +2 -0
- package/dist/core/transcribe.test.d.ts.map +1 -0
- package/dist/core/transcribe.test.js +163 -0
- package/dist/core/transcribe.test.js.map +1 -0
- package/dist/core/types.d.ts +352 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/workspace.d.ts +67 -0
- package/dist/core/workspace.d.ts.map +1 -0
- package/dist/core/workspace.js +113 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/agents/acp/acp-adapter.d.ts +16 -0
- package/dist/plugins/agents/acp/acp-adapter.d.ts.map +1 -0
- package/dist/plugins/agents/acp/acp-adapter.js +49 -0
- package/dist/plugins/agents/acp/acp-adapter.js.map +1 -0
- package/dist/plugins/agents/acp/acp-client.d.ts +32 -0
- package/dist/plugins/agents/acp/acp-client.d.ts.map +1 -0
- package/dist/plugins/agents/acp/acp-client.js +175 -0
- package/dist/plugins/agents/acp/acp-client.js.map +1 -0
- package/dist/plugins/agents/acp/discovery.d.ts +19 -0
- package/dist/plugins/agents/acp/discovery.d.ts.map +1 -0
- package/dist/plugins/agents/acp/discovery.js +109 -0
- package/dist/plugins/agents/acp/discovery.js.map +1 -0
- package/dist/plugins/agents/acp/index.d.ts +4 -0
- package/dist/plugins/agents/acp/index.d.ts.map +1 -0
- package/dist/plugins/agents/acp/index.js +4 -0
- package/dist/plugins/agents/acp/index.js.map +1 -0
- package/dist/plugins/agents/acp/types.d.ts +62 -0
- package/dist/plugins/agents/acp/types.d.ts.map +1 -0
- package/dist/plugins/agents/acp/types.js +5 -0
- package/dist/plugins/agents/acp/types.js.map +1 -0
- package/dist/plugins/agents/claude-code/adapter.test.d.ts +2 -0
- package/dist/plugins/agents/claude-code/adapter.test.d.ts.map +1 -0
- package/dist/plugins/agents/claude-code/adapter.test.js +195 -0
- package/dist/plugins/agents/claude-code/adapter.test.js.map +1 -0
- package/dist/plugins/agents/claude-code/index.d.ts +25 -0
- package/dist/plugins/agents/claude-code/index.d.ts.map +1 -0
- package/dist/plugins/agents/claude-code/index.js +184 -0
- package/dist/plugins/agents/claude-code/index.js.map +1 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.d.ts +42 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.d.ts.map +1 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.js +235 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.js.map +1 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.test.d.ts +2 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.test.d.ts.map +1 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.test.js +188 -0
- package/dist/plugins/agents/claude-code/mcp-approval-server.test.js.map +1 -0
- package/dist/plugins/agents/codex/adapter.test.d.ts +2 -0
- package/dist/plugins/agents/codex/adapter.test.d.ts.map +1 -0
- package/dist/plugins/agents/codex/adapter.test.js +192 -0
- package/dist/plugins/agents/codex/adapter.test.js.map +1 -0
- package/dist/plugins/agents/codex/index.d.ts +37 -0
- package/dist/plugins/agents/codex/index.d.ts.map +1 -0
- package/dist/plugins/agents/codex/index.js +254 -0
- package/dist/plugins/agents/codex/index.js.map +1 -0
- package/dist/plugins/agents/copilot/index.d.ts +35 -0
- package/dist/plugins/agents/copilot/index.d.ts.map +1 -0
- package/dist/plugins/agents/copilot/index.js +182 -0
- package/dist/plugins/agents/copilot/index.js.map +1 -0
- package/dist/plugins/agents/opencode/adapter.test.d.ts +2 -0
- package/dist/plugins/agents/opencode/adapter.test.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/adapter.test.js +139 -0
- package/dist/plugins/agents/opencode/adapter.test.js.map +1 -0
- package/dist/plugins/agents/opencode/http-adapter.test.d.ts +2 -0
- package/dist/plugins/agents/opencode/http-adapter.test.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/http-adapter.test.js +492 -0
- package/dist/plugins/agents/opencode/http-adapter.test.js.map +1 -0
- package/dist/plugins/agents/opencode/index.d.ts +5 -0
- package/dist/plugins/agents/opencode/index.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/index.js +30 -0
- package/dist/plugins/agents/opencode/index.js.map +1 -0
- package/dist/plugins/agents/opencode/opencode-http-adapter.d.ts +138 -0
- package/dist/plugins/agents/opencode/opencode-http-adapter.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/opencode-http-adapter.js +549 -0
- package/dist/plugins/agents/opencode/opencode-http-adapter.js.map +1 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.d.ts +24 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.js +103 -0
- package/dist/plugins/agents/opencode/opencode-stdio-adapter.js.map +1 -0
- package/dist/plugins/agents/opencode/serve-manager.d.ts +27 -0
- package/dist/plugins/agents/opencode/serve-manager.d.ts.map +1 -0
- package/dist/plugins/agents/opencode/serve-manager.js +190 -0
- package/dist/plugins/agents/opencode/serve-manager.js.map +1 -0
- package/dist/plugins/messengers/discord/discord-adapter.d.ts +22 -0
- package/dist/plugins/messengers/discord/discord-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/discord/discord-adapter.js +241 -0
- package/dist/plugins/messengers/discord/discord-adapter.js.map +1 -0
- package/dist/plugins/messengers/discord/discord-adapter.test.d.ts +2 -0
- package/dist/plugins/messengers/discord/discord-adapter.test.d.ts.map +1 -0
- package/dist/plugins/messengers/discord/discord-adapter.test.js +332 -0
- package/dist/plugins/messengers/discord/discord-adapter.test.js.map +1 -0
- package/dist/plugins/messengers/discord/index.d.ts +4 -0
- package/dist/plugins/messengers/discord/index.d.ts.map +1 -0
- package/dist/plugins/messengers/discord/index.js +4 -0
- package/dist/plugins/messengers/discord/index.js.map +1 -0
- package/dist/plugins/messengers/discord/markdown-to-discord.d.ts +11 -0
- package/dist/plugins/messengers/discord/markdown-to-discord.d.ts.map +1 -0
- package/dist/plugins/messengers/discord/markdown-to-discord.js +59 -0
- package/dist/plugins/messengers/discord/markdown-to-discord.js.map +1 -0
- package/dist/plugins/messengers/discord/types.d.ts +9 -0
- package/dist/plugins/messengers/discord/types.d.ts.map +1 -0
- package/dist/plugins/messengers/discord/types.js +3 -0
- package/dist/plugins/messengers/discord/types.js.map +1 -0
- package/dist/plugins/messengers/feishu/card-builder.d.ts +23 -0
- package/dist/plugins/messengers/feishu/card-builder.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/card-builder.js +89 -0
- package/dist/plugins/messengers/feishu/card-builder.js.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-adapter.d.ts +33 -0
- package/dist/plugins/messengers/feishu/feishu-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-adapter.js +195 -0
- package/dist/plugins/messengers/feishu/feishu-adapter.js.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-client.d.ts +44 -0
- package/dist/plugins/messengers/feishu/feishu-client.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-client.js +120 -0
- package/dist/plugins/messengers/feishu/feishu-client.js.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-dedup.test.d.ts +2 -0
- package/dist/plugins/messengers/feishu/feishu-dedup.test.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/feishu-dedup.test.js +70 -0
- package/dist/plugins/messengers/feishu/feishu-dedup.test.js.map +1 -0
- package/dist/plugins/messengers/feishu/index.d.ts +4 -0
- package/dist/plugins/messengers/feishu/index.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/index.js +4 -0
- package/dist/plugins/messengers/feishu/index.js.map +1 -0
- package/dist/plugins/messengers/feishu/types.d.ts +113 -0
- package/dist/plugins/messengers/feishu/types.d.ts.map +1 -0
- package/dist/plugins/messengers/feishu/types.js +4 -0
- package/dist/plugins/messengers/feishu/types.js.map +1 -0
- package/dist/plugins/messengers/telegram/index.d.ts +4 -0
- package/dist/plugins/messengers/telegram/index.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/index.js +4 -0
- package/dist/plugins/messengers/telegram/index.js.map +1 -0
- package/dist/plugins/messengers/telegram/markdown-to-html.d.ts +5 -0
- package/dist/plugins/messengers/telegram/markdown-to-html.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/markdown-to-html.js +186 -0
- package/dist/plugins/messengers/telegram/markdown-to-html.js.map +1 -0
- package/dist/plugins/messengers/telegram/media-download.d.ts +51 -0
- package/dist/plugins/messengers/telegram/media-download.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/media-download.js +224 -0
- package/dist/plugins/messengers/telegram/media-download.js.map +1 -0
- package/dist/plugins/messengers/telegram/media-download.test.d.ts +2 -0
- package/dist/plugins/messengers/telegram/media-download.test.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/media-download.test.js +125 -0
- package/dist/plugins/messengers/telegram/media-download.test.js.map +1 -0
- package/dist/plugins/messengers/telegram/telegram-adapter.d.ts +62 -0
- package/dist/plugins/messengers/telegram/telegram-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/telegram-adapter.js +653 -0
- package/dist/plugins/messengers/telegram/telegram-adapter.js.map +1 -0
- package/dist/plugins/messengers/telegram/types.d.ts +47 -0
- package/dist/plugins/messengers/telegram/types.d.ts.map +1 -0
- package/dist/plugins/messengers/telegram/types.js +3 -0
- package/dist/plugins/messengers/telegram/types.js.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-adapter.d.ts +68 -0
- package/dist/plugins/messengers/wechat/ilink-adapter.d.ts.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-adapter.js +483 -0
- package/dist/plugins/messengers/wechat/ilink-adapter.js.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-client.d.ts +66 -0
- package/dist/plugins/messengers/wechat/ilink-client.d.ts.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-client.js +288 -0
- package/dist/plugins/messengers/wechat/ilink-client.js.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-types.d.ts +173 -0
- package/dist/plugins/messengers/wechat/ilink-types.d.ts.map +1 -0
- package/dist/plugins/messengers/wechat/ilink-types.js +12 -0
- package/dist/plugins/messengers/wechat/ilink-types.js.map +1 -0
- package/dist/utils/backoff.d.ts +35 -0
- package/dist/utils/backoff.d.ts.map +1 -0
- package/dist/utils/backoff.js +59 -0
- package/dist/utils/backoff.js.map +1 -0
- package/dist/utils/cross-platform.d.ts +26 -0
- package/dist/utils/cross-platform.d.ts.map +1 -0
- package/dist/utils/cross-platform.js +58 -0
- package/dist/utils/cross-platform.js.map +1 -0
- package/dist/utils/message-split.d.ts +14 -0
- package/dist/utils/message-split.d.ts.map +1 -0
- package/dist/utils/message-split.js +65 -0
- package/dist/utils/message-split.js.map +1 -0
- package/dist/utils/safe-equal.d.ts +2 -0
- package/dist/utils/safe-equal.d.ts.map +1 -0
- package/dist/utils/safe-equal.js +11 -0
- package/dist/utils/safe-equal.js.map +1 -0
- package/dist/web/public/_app.js +196 -0
- package/dist/web/public/index.html +935 -0
- package/dist/web/public/settings.html +1181 -0
- package/dist/web/public/tasks.html +1827 -0
- package/dist/web/server.d.ts +11 -0
- package/dist/web/server.d.ts.map +1 -0
- package/dist/web/server.js +1820 -0
- package/dist/web/server.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// im-hub event bus — fan-out for cross-cutting state changes that the
|
|
2
|
+
// dashboard / future plugins want to observe in real time.
|
|
3
|
+
//
|
|
4
|
+
// Why a dedicated bus instead of just exposing each module's
|
|
5
|
+
// EventEmitter?
|
|
6
|
+
//
|
|
7
|
+
// 1. Type uniformity. Subscribers want to handle *any* event with one
|
|
8
|
+
// handler shape; otherwise tasks.html ends up wiring six different
|
|
9
|
+
// EventEmitters by hand.
|
|
10
|
+
// 2. Cross-module decoupling. audit-log / approval-bus / job-board
|
|
11
|
+
// should not know that a Web SSE consumer exists. They emit on the
|
|
12
|
+
// bus; whoever wants to listen does.
|
|
13
|
+
// 3. Backpressure / fan-out. The /events SSE endpoint multiplexes a
|
|
14
|
+
// single bus subscription out to N browser clients. If a single
|
|
15
|
+
// client is slow we don't want to drag the entire process; the bus
|
|
16
|
+
// only does fan-out, per-connection write-buffering is the SSE
|
|
17
|
+
// handler's responsibility.
|
|
18
|
+
//
|
|
19
|
+
// IMPORTANT: emit is best-effort. Never throw out of an emit() — the
|
|
20
|
+
// caller (audit-log.logInvocation, approvalBus.handleApproval, etc.) is
|
|
21
|
+
// in a hot path. We swallow listener errors here so a buggy subscriber
|
|
22
|
+
// can't break the originating action.
|
|
23
|
+
import { EventEmitter } from 'events';
|
|
24
|
+
class ImhubEventBus extends EventEmitter {
|
|
25
|
+
/** Bounded recent-event log so a freshly-connected SSE client can
|
|
26
|
+
* catch up on what happened in the last few seconds without needing
|
|
27
|
+
* a separate REST poll. Cap at 200 entries — enough for the dashboard
|
|
28
|
+
* to show a meaningful "what just happened" feed without bloating
|
|
29
|
+
* process memory. */
|
|
30
|
+
recent = [];
|
|
31
|
+
static RECENT_CAP = 200;
|
|
32
|
+
constructor() {
|
|
33
|
+
super();
|
|
34
|
+
// EventEmitter defaults to 10 listeners before warning; we expect one
|
|
35
|
+
// handler per SSE connection plus a few internal subscribers, so 100
|
|
36
|
+
// is a comfortable cap that still flags accidental leaks.
|
|
37
|
+
this.setMaxListeners(100);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Publish a typed event. Listeners are called synchronously inside
|
|
41
|
+
* EventEmitter.emit; errors thrown from a listener are caught here so
|
|
42
|
+
* a bad subscriber can't break audit-log / approval-bus / job-board.
|
|
43
|
+
*/
|
|
44
|
+
publish(event) {
|
|
45
|
+
this.recent.push(event);
|
|
46
|
+
while (this.recent.length > ImhubEventBus.RECENT_CAP)
|
|
47
|
+
this.recent.shift();
|
|
48
|
+
try {
|
|
49
|
+
this.emit('event', event);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// EventEmitter normally rethrows — caller is in a hot path so we swallow.
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Snapshot of the recent ring buffer. Used by SSE handler on
|
|
56
|
+
* connection to replay the last N events to a fresh client. */
|
|
57
|
+
getRecent() {
|
|
58
|
+
return this.recent.slice();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export const eventBus = new ImhubEventBus();
|
|
62
|
+
//# sourceMappingURL=event-bus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../../src/core/event-bus.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,2DAA2D;AAC3D,EAAE;AACF,6DAA6D;AAC7D,gBAAgB;AAChB,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,8BAA8B;AAC9B,qEAAqE;AACrE,wEAAwE;AACxE,0CAA0C;AAC1C,sEAAsE;AACtE,qEAAqE;AACrE,wEAAwE;AACxE,oEAAoE;AACpE,iCAAiC;AACjC,EAAE;AACF,qEAAqE;AACrE,wEAAwE;AACxE,uEAAuE;AACvE,sCAAsC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAgErC,MAAM,aAAc,SAAQ,YAAY;IACtC;;;;0BAIsB;IACd,MAAM,GAAiB,EAAE,CAAA;IACzB,MAAM,CAAU,UAAU,GAAG,GAAG,CAAA;IAExC;QACE,KAAK,EAAE,CAAA;QACP,sEAAsE;QACtE,qEAAqE;QACrE,0DAA0D;QAC1D,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAA;IAC3B,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,KAAiB;QACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACvB,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,aAAa,CAAC,UAAU;YAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;QACzE,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;QAC5E,CAAC;IACH,CAAC;IAED;oEACgE;IAChE,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;IAC5B,CAAC;;AAGH,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AgentAdapter } from './types.js';
|
|
2
|
+
export interface LLMJudgeConfig {
|
|
3
|
+
/** Agent name used as the judge. Must already be registered. */
|
|
4
|
+
agentName: string;
|
|
5
|
+
/** Score threshold below which the LLM is consulted (default 1.0). */
|
|
6
|
+
threshold?: number;
|
|
7
|
+
/** Override the default 5s response timeout. */
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function configureLLMJudge(cfg: LLMJudgeConfig | null): void;
|
|
11
|
+
/** Read the judge configuration. Used by classifyIntent to know whether
|
|
12
|
+
* to attempt a fallback at all. */
|
|
13
|
+
export declare function getLLMJudge(): LLMJudgeConfig | null;
|
|
14
|
+
/**
|
|
15
|
+
* Ask the judge agent which candidate to use. Returns null on any
|
|
16
|
+
* failure path (timeout, judge unavailable, malformed response) so
|
|
17
|
+
* the caller can keep its rule-based decision.
|
|
18
|
+
*/
|
|
19
|
+
export declare function classifyWithLLM(text: string, candidates: string[], resolveAgent: (name: string) => AgentAdapter | undefined): Promise<{
|
|
20
|
+
agent: string;
|
|
21
|
+
reason: string;
|
|
22
|
+
} | null>;
|
|
23
|
+
/** Test-only: clear cache so memoization doesn't leak between cases. */
|
|
24
|
+
export declare function _clearLLMCache(): void;
|
|
25
|
+
/** Test/diagnostic: current cache size. */
|
|
26
|
+
export declare function _cacheSize(): number;
|
|
27
|
+
//# sourceMappingURL=intent-llm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent-llm.d.ts","sourceRoot":"","sources":["../../src/core/intent-llm.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAsD9C,MAAM,WAAW,cAAc;IAC7B,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAA;IACjB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAID,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI,CAGlE;AAED;oCACoC;AACpC,wBAAgB,WAAW,IAAI,cAAc,GAAG,IAAI,CAEnD;AAgDD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAAE,EACpB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,GAAG,SAAS,GACvD,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAqDnD;AAED,wEAAwE;AACxE,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,2CAA2C;AAC3C,wBAAgB,UAAU,IAAI,MAAM,CAAsB"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// LLM-backed intent fallback (P2-H).
|
|
2
|
+
//
|
|
3
|
+
// When the rule-based classifier produces a low-confidence result we
|
|
4
|
+
// ask a configured "judge agent" — itself one of the registered agents
|
|
5
|
+
// — to pick the best agent. Opt-in via env (IM_HUB_LLM_JUDGE_AGENT)
|
|
6
|
+
// or programmatic configureLLMJudge() so we never silently spend
|
|
7
|
+
// tokens by default.
|
|
8
|
+
//
|
|
9
|
+
// Caching: identical prompt+candidate-list pairs are memoized within a
|
|
10
|
+
// 10-minute window so repeated chat-room banter doesn't burn budget.
|
|
11
|
+
import { createHash } from 'crypto';
|
|
12
|
+
import { logger as rootLogger } from './logger.js';
|
|
13
|
+
const log = rootLogger.child({ component: 'intent-llm' });
|
|
14
|
+
const CACHE_TTL_MS = 10 * 60 * 1000;
|
|
15
|
+
const CACHE_MAX_ENTRIES = 1000; // LRU upper bound prevents unbounded growth
|
|
16
|
+
const RESPONSE_TIMEOUT_MS = 5_000;
|
|
17
|
+
/**
|
|
18
|
+
* LRU cache: insertion-order Map; on access we delete + re-set to bump
|
|
19
|
+
* the entry to the end. When size exceeds max we evict the head (the
|
|
20
|
+
* oldest unused entry).
|
|
21
|
+
*/
|
|
22
|
+
class LRUCache {
|
|
23
|
+
maxSize;
|
|
24
|
+
ttlMs;
|
|
25
|
+
store = new Map();
|
|
26
|
+
constructor(maxSize, ttlMs) {
|
|
27
|
+
this.maxSize = maxSize;
|
|
28
|
+
this.ttlMs = ttlMs;
|
|
29
|
+
}
|
|
30
|
+
get(key) {
|
|
31
|
+
const v = this.store.get(key);
|
|
32
|
+
if (!v)
|
|
33
|
+
return undefined;
|
|
34
|
+
if (Date.now() - v.ts > this.ttlMs) {
|
|
35
|
+
this.store.delete(key);
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
// Touch — move to end of insertion order
|
|
39
|
+
this.store.delete(key);
|
|
40
|
+
this.store.set(key, v);
|
|
41
|
+
return v;
|
|
42
|
+
}
|
|
43
|
+
set(key, value) {
|
|
44
|
+
if (this.store.has(key))
|
|
45
|
+
this.store.delete(key);
|
|
46
|
+
this.store.set(key, value);
|
|
47
|
+
// Evict from the head until under cap
|
|
48
|
+
while (this.store.size > this.maxSize) {
|
|
49
|
+
const first = this.store.keys().next().value;
|
|
50
|
+
if (first === undefined)
|
|
51
|
+
break;
|
|
52
|
+
this.store.delete(first);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
clear() { this.store.clear(); }
|
|
56
|
+
get size() { return this.store.size; }
|
|
57
|
+
}
|
|
58
|
+
const cache = new LRUCache(CACHE_MAX_ENTRIES, CACHE_TTL_MS);
|
|
59
|
+
let config = null;
|
|
60
|
+
export function configureLLMJudge(cfg) {
|
|
61
|
+
config = cfg;
|
|
62
|
+
cache.clear();
|
|
63
|
+
}
|
|
64
|
+
/** Read the judge configuration. Used by classifyIntent to know whether
|
|
65
|
+
* to attempt a fallback at all. */
|
|
66
|
+
export function getLLMJudge() {
|
|
67
|
+
return config;
|
|
68
|
+
}
|
|
69
|
+
/** Probe env on first import so common deployments get auto-config. */
|
|
70
|
+
function autoFromEnv() {
|
|
71
|
+
const name = process.env.IM_HUB_LLM_JUDGE_AGENT;
|
|
72
|
+
if (name && !config) {
|
|
73
|
+
const threshold = parseFloat(process.env.IM_HUB_LLM_JUDGE_THRESHOLD || '1.0');
|
|
74
|
+
config = {
|
|
75
|
+
agentName: name,
|
|
76
|
+
threshold: Number.isFinite(threshold) && threshold > 0 ? threshold : 1.0,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
autoFromEnv();
|
|
81
|
+
/** Hard cap on the prompt portion of the LRU key. Past this length the
|
|
82
|
+
* key collapses, which is fine for a memo cache (occasional re-call is
|
|
83
|
+
* cheap and doesn't affect correctness — the LLM judge runs again). It
|
|
84
|
+
* matters because the key was previously the full prompt concatenated
|
|
85
|
+
* with `|`-joined candidates, so a 100KB prompt + many candidates blew
|
|
86
|
+
* the LRU's per-entry memory cost. */
|
|
87
|
+
const MAX_KEY_TEXT_LEN = 256;
|
|
88
|
+
function makeKey(text, candidates) {
|
|
89
|
+
// Hash the candidate list so the key length is bounded regardless of
|
|
90
|
+
// how many ACP agents are registered. SHA-256/16-hex (64 bits) gives
|
|
91
|
+
// 1-in-2^64 collision probability across the LRU window — irrelevant.
|
|
92
|
+
const candHash = createHash('sha256')
|
|
93
|
+
.update(candidates.slice().sort().join('|'))
|
|
94
|
+
.digest('hex').slice(0, 16);
|
|
95
|
+
const textKey = text.length > MAX_KEY_TEXT_LEN ? text.slice(0, MAX_KEY_TEXT_LEN) : text;
|
|
96
|
+
return `${candHash}||${textKey}`;
|
|
97
|
+
}
|
|
98
|
+
function buildPrompt(text, candidates) {
|
|
99
|
+
return [
|
|
100
|
+
'You are a router that picks the most appropriate agent for a user request.',
|
|
101
|
+
'Reply with EXACTLY one agent name from the list — no explanation, no punctuation.',
|
|
102
|
+
'',
|
|
103
|
+
'Candidates:',
|
|
104
|
+
...candidates.map((c) => ` - ${c}`),
|
|
105
|
+
'',
|
|
106
|
+
`User request: ${text}`,
|
|
107
|
+
'',
|
|
108
|
+
'Best agent:',
|
|
109
|
+
].join('\n');
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Ask the judge agent which candidate to use. Returns null on any
|
|
113
|
+
* failure path (timeout, judge unavailable, malformed response) so
|
|
114
|
+
* the caller can keep its rule-based decision.
|
|
115
|
+
*/
|
|
116
|
+
export async function classifyWithLLM(text, candidates, resolveAgent) {
|
|
117
|
+
if (!config)
|
|
118
|
+
return null;
|
|
119
|
+
if (candidates.length === 0)
|
|
120
|
+
return null;
|
|
121
|
+
const key = makeKey(text, candidates);
|
|
122
|
+
const cached = cache.get(key);
|
|
123
|
+
if (cached) {
|
|
124
|
+
return { agent: cached.agent, reason: `${cached.reason} (cached)` };
|
|
125
|
+
}
|
|
126
|
+
const judge = resolveAgent(config.agentName);
|
|
127
|
+
if (!judge) {
|
|
128
|
+
log.warn({ judge: config.agentName }, 'LLM judge agent not registered');
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const timeoutMs = config.timeoutMs ?? RESPONSE_TIMEOUT_MS;
|
|
132
|
+
const prompt = buildPrompt(text, candidates);
|
|
133
|
+
try {
|
|
134
|
+
let acc = '';
|
|
135
|
+
const generator = judge.sendPrompt(`llm-judge`, prompt, []);
|
|
136
|
+
const collect = (async () => {
|
|
137
|
+
for await (const chunk of generator) {
|
|
138
|
+
acc += chunk;
|
|
139
|
+
if (acc.length > 4096)
|
|
140
|
+
break; // guard runaway output
|
|
141
|
+
}
|
|
142
|
+
})();
|
|
143
|
+
const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('LLM judge timed out')), timeoutMs));
|
|
144
|
+
await Promise.race([collect, timeout]);
|
|
145
|
+
// Parse: take the first line, lowercase, strip punctuation, match
|
|
146
|
+
// against candidates case-insensitively.
|
|
147
|
+
const firstLine = acc.split('\n').map((s) => s.trim()).filter(Boolean)[0] || '';
|
|
148
|
+
const cleaned = firstLine.toLowerCase().replace(/[^a-z0-9_-]/g, '');
|
|
149
|
+
const match = candidates.find((c) => c.toLowerCase() === cleaned)
|
|
150
|
+
?? candidates.find((c) => cleaned.includes(c.toLowerCase()));
|
|
151
|
+
if (!match) {
|
|
152
|
+
log.warn({ raw: firstLine, candidates }, 'LLM judge produced unrecognized output');
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
const decision = { agent: match, reason: `LLM judge picked ${match}` };
|
|
156
|
+
cache.set(key, { ...decision, ts: Date.now() });
|
|
157
|
+
return decision;
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
log.warn({ err: err instanceof Error ? err.message : String(err) }, 'LLM judge call failed');
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/** Test-only: clear cache so memoization doesn't leak between cases. */
|
|
165
|
+
export function _clearLLMCache() {
|
|
166
|
+
cache.clear();
|
|
167
|
+
}
|
|
168
|
+
/** Test/diagnostic: current cache size. */
|
|
169
|
+
export function _cacheSize() { return cache.size; }
|
|
170
|
+
//# sourceMappingURL=intent-llm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent-llm.js","sourceRoot":"","sources":["../../src/core/intent-llm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Logger } from 'pino';
|
|
2
|
+
export interface IntentResult {
|
|
3
|
+
agent: string;
|
|
4
|
+
score: number;
|
|
5
|
+
reason: string;
|
|
6
|
+
triggeredBy: 'explicit' | 'sticky' | 'keyword' | 'topic' | 'fallback';
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Classify a user message and return the best agent to handle it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function classifyIntent(text: string, stickyAgent?: string, logger?: Logger): IntentResult;
|
|
12
|
+
//# sourceMappingURL=intent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../src/core/intent.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAgFlC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,UAAU,CAAA;CACtE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,MAAM,GACd,YAAY,CA4Hd"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// Intent classifier — rule-based agent routing
|
|
2
|
+
//
|
|
3
|
+
// Phase 2: keyword matching with sticky session bias.
|
|
4
|
+
// Phase 2b (future): small LLM for topic classification.
|
|
5
|
+
import { registry } from './registry.js';
|
|
6
|
+
import { circuitBreaker } from './circuit-breaker.js';
|
|
7
|
+
const PROFILES = {
|
|
8
|
+
'opencode': {
|
|
9
|
+
keywords: ['代码', 'code', '编程', 'refactor', '重构', 'bug', '修复', 'fix', 'git', 'commit', 'deploy', 'build',
|
|
10
|
+
'test', '测试', 'api', 'server', '后端', 'frontend', '前端', 'sql', '数据库', 'database',
|
|
11
|
+
'python', 'typescript', 'javascript', 'rust', 'go', 'java',
|
|
12
|
+
'shell', 'bash', '命令行', 'terminal', 'config', '配置', 'docker', 'container'],
|
|
13
|
+
// Was 1.2 — same keywords also match claude-code, so the 0.2 advantage
|
|
14
|
+
// would push routing to opencode whenever the message mentioned any coding
|
|
15
|
+
// term. With sticky now an absolute lock (see classifyIntent below) this
|
|
16
|
+
// weight only matters for the very first message on a fresh thread, but we
|
|
17
|
+
// keep weights neutral so ordering comes from topic rules + defaultAgent.
|
|
18
|
+
weight: 1.0,
|
|
19
|
+
},
|
|
20
|
+
'claude-code': {
|
|
21
|
+
keywords: ['代码', 'code', '编程', 'refactor', '分析', 'analyze', 'architecture', '架构',
|
|
22
|
+
'design', '设计', 'review', '审查', 'explain', '解释', '文档', 'documentation',
|
|
23
|
+
'reasoning', '推理', 'complex', '复杂', 'research', '研究'],
|
|
24
|
+
weight: 1.0,
|
|
25
|
+
},
|
|
26
|
+
'codex': {
|
|
27
|
+
keywords: ['代码', 'code', 'bug', 'fix', '修复', 'quick', '快速', 'small', '简单', 'simple'],
|
|
28
|
+
weight: 0.9,
|
|
29
|
+
},
|
|
30
|
+
'copilot': {
|
|
31
|
+
keywords: ['代码', 'code', 'suggest', '建议', 'autocomplete', '补全', 'copilot'],
|
|
32
|
+
weight: 0.8,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
// `\b` only matches between ASCII word chars and other chars — it does
|
|
36
|
+
// nothing for CJK. We split keywords into ASCII (uses \b) and CJK
|
|
37
|
+
// (boundary-free includes). Both branches are OR'd into one regex so
|
|
38
|
+
// callers still get a single .test() call.
|
|
39
|
+
function isCJK(s) {
|
|
40
|
+
for (const ch of s) {
|
|
41
|
+
const cp = ch.codePointAt(0) || 0;
|
|
42
|
+
if (cp >= 0x3000)
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
function escapeReg(s) {
|
|
48
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
49
|
+
}
|
|
50
|
+
function topic(words, agent, description) {
|
|
51
|
+
const ascii = words.filter((w) => !isCJK(w)).map(escapeReg);
|
|
52
|
+
const cjk = words.filter((w) => isCJK(w)).map(escapeReg);
|
|
53
|
+
const parts = [];
|
|
54
|
+
if (ascii.length)
|
|
55
|
+
parts.push(`\\b(?:${ascii.join('|')})\\b`);
|
|
56
|
+
if (cjk.length)
|
|
57
|
+
parts.push(`(?:${cjk.join('|')})`);
|
|
58
|
+
return { pattern: new RegExp(parts.join('|'), 'iu'), agent, description };
|
|
59
|
+
}
|
|
60
|
+
const TOPIC_RULES = [
|
|
61
|
+
topic(['git', 'commit', 'push', 'pull', 'merge', 'rebase', 'branch', 'stash'], 'opencode', 'git operations'),
|
|
62
|
+
topic(['test', '测试', 'spec', 'coverage', 'mock', 'stub'], 'opencode', 'testing'),
|
|
63
|
+
topic(['docker', 'container', 'kubernetes', 'k8s', 'deploy', 'ci', 'cd'], 'opencode', 'devops'),
|
|
64
|
+
topic(['sql', 'database', 'db', '查询', 'select', 'insert', 'update', 'delete'], 'opencode', 'database'),
|
|
65
|
+
topic(['review', '审查', 'audit', '检查'], 'claude-code', 'code review'),
|
|
66
|
+
topic(['explain', '解释', 'what is', '为什么', 'how does', '怎么回事'], 'claude-code', 'explanation'),
|
|
67
|
+
];
|
|
68
|
+
/**
|
|
69
|
+
* Classify a user message and return the best agent to handle it.
|
|
70
|
+
*/
|
|
71
|
+
export function classifyIntent(text, stickyAgent, logger) {
|
|
72
|
+
const available = registry.listAgents().filter(a => !circuitBreaker.isOpen(a));
|
|
73
|
+
if (available.length === 0) {
|
|
74
|
+
throw new Error('No agents available');
|
|
75
|
+
}
|
|
76
|
+
// Sticky is an absolute lock: once a thread is bound to an agent, only an
|
|
77
|
+
// explicit /<agent> command (or /new) can change it. Without this, a single
|
|
78
|
+
// keyword in a long-running thread could outscore the sticky bonus and
|
|
79
|
+
// silently swap agents — the "agent drift" symptom users hit after long
|
|
80
|
+
// pauses or across days. See router.ts:default → classifyIntent caller.
|
|
81
|
+
if (stickyAgent && available.includes(stickyAgent)) {
|
|
82
|
+
logger?.debug({ event: 'intent.sticky_lock', agent: stickyAgent }, `[intent] sticky lock honored (no classification)`);
|
|
83
|
+
return {
|
|
84
|
+
agent: stickyAgent,
|
|
85
|
+
score: 0,
|
|
86
|
+
reason: 'sticky lock (no classification)',
|
|
87
|
+
triggeredBy: 'sticky',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// Score per agent. Agents missing from PROFILES (e.g. user-installed
|
|
91
|
+
// custom ACP agents) get a default neutral weight so they remain
|
|
92
|
+
// selectable when no explicit signal favors a profiled agent.
|
|
93
|
+
const DEFAULT_WEIGHT = 0.5;
|
|
94
|
+
const scores = new Map();
|
|
95
|
+
for (const a of available) {
|
|
96
|
+
const profile = PROFILES[a];
|
|
97
|
+
scores.set(a, {
|
|
98
|
+
score: 0,
|
|
99
|
+
reasons: profile ? [] : ['default weight (no profile)'],
|
|
100
|
+
weight: profile?.weight ?? DEFAULT_WEIGHT,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const lower = text.toLowerCase();
|
|
104
|
+
// 1. Topic rule matching
|
|
105
|
+
for (const rule of TOPIC_RULES) {
|
|
106
|
+
if (rule.pattern.test(text)) {
|
|
107
|
+
const s = scores.get(rule.agent);
|
|
108
|
+
if (s) {
|
|
109
|
+
s.score += 2;
|
|
110
|
+
s.reasons.push(`topic: ${rule.description}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// 2. Keyword matching (uses each agent's own weight; profile-less agents
|
|
115
|
+
// can't keyword-match because they have no keywords list, but they still
|
|
116
|
+
// earn their base weight below).
|
|
117
|
+
for (const [agent, profile] of Object.entries(PROFILES)) {
|
|
118
|
+
const s = scores.get(agent);
|
|
119
|
+
if (!s)
|
|
120
|
+
continue;
|
|
121
|
+
for (const kw of profile.keywords) {
|
|
122
|
+
if (lower.includes(kw.toLowerCase())) {
|
|
123
|
+
s.score += 1 * profile.weight;
|
|
124
|
+
s.reasons.push(`keyword: "${kw}"`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// 3. Base weight per agent (so a profile-less custom agent has a non-zero
|
|
129
|
+
// floor and won't be permanently dominated).
|
|
130
|
+
for (const [, s] of scores) {
|
|
131
|
+
s.score += s.weight * 0.5;
|
|
132
|
+
}
|
|
133
|
+
// 4. Sticky session bias
|
|
134
|
+
if (stickyAgent && scores.has(stickyAgent)) {
|
|
135
|
+
const s = scores.get(stickyAgent);
|
|
136
|
+
s.score += 3;
|
|
137
|
+
s.reasons.push('sticky: last used agent');
|
|
138
|
+
}
|
|
139
|
+
// Tie-break order:
|
|
140
|
+
// 1. higher score wins
|
|
141
|
+
// 2. sticky agent (if present in scores)
|
|
142
|
+
// 3. PROFILES declaration order (preserves intentional priority)
|
|
143
|
+
// 4. alphabetical, for full determinism
|
|
144
|
+
const profileOrder = Object.keys(PROFILES);
|
|
145
|
+
const rankOf = (agent) => {
|
|
146
|
+
if (stickyAgent && agent === stickyAgent)
|
|
147
|
+
return -1;
|
|
148
|
+
const idx = profileOrder.indexOf(agent);
|
|
149
|
+
return idx === -1 ? profileOrder.length : idx;
|
|
150
|
+
};
|
|
151
|
+
let best = null;
|
|
152
|
+
for (const [agent, s] of scores) {
|
|
153
|
+
if (!best) {
|
|
154
|
+
best = { agent, score: s.score, reasons: s.reasons };
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (s.score > best.score) {
|
|
158
|
+
best = { agent, score: s.score, reasons: s.reasons };
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (s.score === best.score) {
|
|
162
|
+
const cmp = rankOf(agent) - rankOf(best.agent);
|
|
163
|
+
if (cmp < 0 || (cmp === 0 && agent < best.agent)) {
|
|
164
|
+
best = { agent, score: s.score, reasons: s.reasons };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (!best) {
|
|
169
|
+
// fallback to first available
|
|
170
|
+
const agent = available[0];
|
|
171
|
+
return { agent, score: 0, reason: 'fallback (no matches)', triggeredBy: 'fallback' };
|
|
172
|
+
}
|
|
173
|
+
const isSticky = stickyAgent === best.agent && best.reasons.includes('sticky: last used agent');
|
|
174
|
+
const hasTopic = best.reasons.some(r => r.startsWith('topic:'));
|
|
175
|
+
const hasKeyword = best.reasons.some(r => r.startsWith('keyword:'));
|
|
176
|
+
const triggeredBy = hasTopic ? 'topic'
|
|
177
|
+
: hasKeyword ? 'keyword'
|
|
178
|
+
: isSticky ? 'sticky'
|
|
179
|
+
: 'fallback';
|
|
180
|
+
return {
|
|
181
|
+
agent: best.agent,
|
|
182
|
+
score: Math.round(best.score * 100) / 100,
|
|
183
|
+
reason: best.reasons.join(', ') || `base score ${best.score.toFixed(1)}`,
|
|
184
|
+
triggeredBy,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=intent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent.js","sourceRoot":"","sources":["../../src/core/intent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
import type { Logger } from 'pino';
|
|
3
|
+
export type JobStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
4
|
+
export interface Job {
|
|
5
|
+
id: number;
|
|
6
|
+
status: JobStatus;
|
|
7
|
+
agent: string;
|
|
8
|
+
prompt: string;
|
|
9
|
+
result: string | null;
|
|
10
|
+
error: string | null;
|
|
11
|
+
created_at: string;
|
|
12
|
+
completed_at: string | null;
|
|
13
|
+
/** IM userId of the originating caller, or '' for legacy / system rows. */
|
|
14
|
+
creator_id: string;
|
|
15
|
+
/** Workspace this job was created under, or '' if unknown. */
|
|
16
|
+
workspace_id: string;
|
|
17
|
+
}
|
|
18
|
+
interface JobRunner {
|
|
19
|
+
(job: Job, logger: Logger, signal: AbortSignal): AsyncGenerator<string>;
|
|
20
|
+
}
|
|
21
|
+
/** Stop the retention sweep (used by tests + graceful shutdown). */
|
|
22
|
+
export declare function stopJobRetentionSweep(): void;
|
|
23
|
+
/** Close the underlying SQLite handle. Called by cli.ts on graceful
|
|
24
|
+
* shutdown so the WAL is checkpointed before exit (L2). */
|
|
25
|
+
export declare function closeJobBoardDb(): void;
|
|
26
|
+
/**
|
|
27
|
+
* Filter / scoping options accepted by every owner-sensitive job-board API.
|
|
28
|
+
*
|
|
29
|
+
* - When `creatorId` is set, the query is scoped to rows whose `creator_id`
|
|
30
|
+
* matches the caller OR whose `creator_id` is '' (legacy / ownerless).
|
|
31
|
+
* This preserves visibility of pre-migration jobs while preventing one
|
|
32
|
+
* user from poking at another's freshly-created ones.
|
|
33
|
+
* - When `creatorId` is unset (undefined), no ownership filter is applied
|
|
34
|
+
* — used by REST / ACP / scheduler entry-points that are guarded by the
|
|
35
|
+
* web token, not by per-user identity.
|
|
36
|
+
*/
|
|
37
|
+
export interface OwnerOpts {
|
|
38
|
+
creatorId?: string;
|
|
39
|
+
/** Optional agent-name filter. When set, only rows whose `agent` column
|
|
40
|
+
* matches are returned. Used by the dashboard to give per-agent views
|
|
41
|
+
* (claude-code / opencode / codex tabs); IM commands generally don't
|
|
42
|
+
* pass it (they want the user's full job history). */
|
|
43
|
+
agent?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface CreateJobOpts {
|
|
46
|
+
creatorId?: string;
|
|
47
|
+
workspaceId?: string;
|
|
48
|
+
}
|
|
49
|
+
export declare function createJob(agent: string, prompt: string, opts?: CreateJobOpts): number;
|
|
50
|
+
export declare function getJob(id: number, opts?: OwnerOpts): Job | null;
|
|
51
|
+
export declare function listJobs(limit?: number, status?: JobStatus, opts?: OwnerOpts): Job[];
|
|
52
|
+
/**
|
|
53
|
+
* Acquire a job slot. Returns immediately when capacity available, otherwise
|
|
54
|
+
* queues the caller and returns a promise that resolves when a slot frees.
|
|
55
|
+
*/
|
|
56
|
+
declare function acquireSlot(): Promise<void>;
|
|
57
|
+
declare function releaseSlot(): void;
|
|
58
|
+
export declare function runJob(id: number, runner: JobRunner, logger: Logger): Promise<void>;
|
|
59
|
+
/** Test/diagnostic accessor: how many runJob invocations are currently in flight. */
|
|
60
|
+
export declare function _activeJobCount(): number;
|
|
61
|
+
/** Test/diagnostic accessor: the wait queue depth. */
|
|
62
|
+
export declare function _waitQueueDepth(): number;
|
|
63
|
+
/** Test-only: directly exercise the concurrency gate without SQLite. */
|
|
64
|
+
export declare const _testSlot: {
|
|
65
|
+
acquire: typeof acquireSlot;
|
|
66
|
+
release: typeof releaseSlot;
|
|
67
|
+
};
|
|
68
|
+
export declare function cancelJob(id: number, opts?: OwnerOpts): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Delete jobs that finished (completed / failed / cancelled) more than
|
|
71
|
+
* `daysRetention` days ago. 'pending' / 'running' rows are never pruned —
|
|
72
|
+
* a stalled-but-resumable job is the operator's call. Returns the row
|
|
73
|
+
* count so callers / tests can assert.
|
|
74
|
+
*/
|
|
75
|
+
export declare function pruneOldJobs(d?: Database.Database, daysRetention?: number): number;
|
|
76
|
+
export declare function getJobStats(): {
|
|
77
|
+
total: number;
|
|
78
|
+
pending: number;
|
|
79
|
+
running: number;
|
|
80
|
+
completed: number;
|
|
81
|
+
failed: number;
|
|
82
|
+
};
|
|
83
|
+
export {};
|
|
84
|
+
//# sourceMappingURL=job-board.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-board.d.ts","sourceRoot":"","sources":["../../src/core/job-board.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAA;AAG1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AA8DlC,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAA;AAEpF,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,SAAS,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,UAAU,SAAS;IACjB,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,CAAA;CACxE;AA4ED,oEAAoE;AACpE,wBAAgB,qBAAqB,IAAI,IAAI,CAK5C;AAED;4DAC4D;AAC5D,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAMD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,SAAS;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;2DAGuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CASzF;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,GAAE,SAAc,GAAG,GAAG,GAAG,IAAI,CAQnE;AAED,wBAAgB,QAAQ,CAAC,KAAK,SAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,IAAI,GAAE,SAAc,GAAG,GAAG,EAAE,CAepF;AAcD;;;GAGG;AACH,iBAAe,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ1C;AAED,iBAAS,WAAW,IAAI,IAAI,CAI3B;AAED,wBAAsB,MAAM,CAC1B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CA6Df;AAED,qFAAqF;AACrF,wBAAgB,eAAe,IAAI,MAAM,CAAsB;AAC/D,sDAAsD;AACtD,wBAAgB,eAAe,IAAI,MAAM,CAA4B;AACrE,wEAAwE;AACxE,eAAO,MAAM,SAAS;;;CAAiD,CAAA;AAEvE,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,GAAE,SAAc,GAAG,OAAO,CAWnE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAE,QAAQ,CAAC,QAAmB,EAAE,aAAa,GAAE,MAA+B,GAAG,MAAM,CAsBpH;AAED,wBAAgB,WAAW,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAqBpH"}
|