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,316 @@
|
|
|
1
|
+
// Scheduler — persistent cron-driven Job creation.
|
|
2
|
+
//
|
|
3
|
+
// Schedules live in the same SQLite DB as Jobs (extra table). A 30s tick
|
|
4
|
+
// scans for due rows, creates a Job for each, and bumps `next_run`. When
|
|
5
|
+
// SQLite is unavailable (bun runtime / disk error) the scheduler exposes
|
|
6
|
+
// the same API but persists nothing — exact same fail-soft pattern as
|
|
7
|
+
// audit-log + job-board.
|
|
8
|
+
import { homedir } from 'os';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { logger as rootLogger } from './logger.js';
|
|
11
|
+
import { parseCron, nextOccurrence } from './cron.js';
|
|
12
|
+
import { createJob, runJob } from './job-board.js';
|
|
13
|
+
import { registry } from './registry.js';
|
|
14
|
+
import { AgentBase } from './agent-base.js';
|
|
15
|
+
import { createSqliteHelper } from './sqlite-helper.js';
|
|
16
|
+
const SCHED_DB = join(homedir(), '.im-hub', 'schedules.db');
|
|
17
|
+
const TICK_INTERVAL_MS = 30 * 1000;
|
|
18
|
+
const NOTIFY_TIMEOUT_MS = 10 * 1000;
|
|
19
|
+
/** Hard cap on webhook response body — we only read a small ack. */
|
|
20
|
+
const NOTIFY_MAX_RESPONSE_BYTES = 64 * 1024;
|
|
21
|
+
const log = rootLogger.child({ component: 'schedule' });
|
|
22
|
+
let tickTimer = null;
|
|
23
|
+
/**
|
|
24
|
+
* Validate a user-supplied webhook URL before the scheduler POSTs to it.
|
|
25
|
+
* Defends against accidental misconfig and basic SSRF: no protocols other
|
|
26
|
+
* than http(s); no loopback / RFC1918 / link-local destinations unless the
|
|
27
|
+
* operator explicitly opts in via IMHUB_ALLOW_PRIVATE_WEBHOOKS=1.
|
|
28
|
+
*
|
|
29
|
+
* Note: this is a lexical check (IP literals + "localhost"). DNS-rebinding
|
|
30
|
+
* mitigation requires an at-fetch-time guard; tracked separately.
|
|
31
|
+
*/
|
|
32
|
+
export function validateWebhookUrl(raw) {
|
|
33
|
+
let u;
|
|
34
|
+
try {
|
|
35
|
+
u = new URL(raw);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return { ok: false, reason: 'malformed URL' };
|
|
39
|
+
}
|
|
40
|
+
if (u.protocol !== 'http:' && u.protocol !== 'https:') {
|
|
41
|
+
return { ok: false, reason: `protocol ${u.protocol} not allowed (use http: or https:)` };
|
|
42
|
+
}
|
|
43
|
+
if (process.env.IMHUB_ALLOW_PRIVATE_WEBHOOKS === '1')
|
|
44
|
+
return { ok: true, url: u };
|
|
45
|
+
const host = u.hostname.toLowerCase();
|
|
46
|
+
if (host === 'localhost' || host === '0.0.0.0' || host === '::' || host === '[::]') {
|
|
47
|
+
return { ok: false, reason: `host ${host} not allowed` };
|
|
48
|
+
}
|
|
49
|
+
// IPv4 literals
|
|
50
|
+
const v4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
51
|
+
if (v4) {
|
|
52
|
+
const o = v4.slice(1, 5).map(Number);
|
|
53
|
+
if (o.some((b) => !Number.isFinite(b) || b < 0 || b > 255)) {
|
|
54
|
+
return { ok: false, reason: 'malformed IPv4 literal' };
|
|
55
|
+
}
|
|
56
|
+
const [a, b] = o;
|
|
57
|
+
// 127/8 loopback, 10/8, 192.168/16, 172.16-31/12, 169.254/16 link-local,
|
|
58
|
+
// 0/8 "this network", 100.64/10 CGNAT.
|
|
59
|
+
if (a === 127 || a === 10 || a === 0
|
|
60
|
+
|| (a === 192 && b === 168)
|
|
61
|
+
|| (a === 172 && b >= 16 && b <= 31)
|
|
62
|
+
|| (a === 169 && b === 254)
|
|
63
|
+
|| (a === 100 && b >= 64 && b <= 127)) {
|
|
64
|
+
return { ok: false, reason: `private/loopback IPv4 ${host} not allowed` };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// IPv6 literals come wrapped in [] in URL.hostname strips them; bracket
|
|
68
|
+
// forms still surface in u.host though. Block well-known unsafe prefixes.
|
|
69
|
+
if (host === '::1' || host.startsWith('fc') || host.startsWith('fd') || host.startsWith('fe80')) {
|
|
70
|
+
return { ok: false, reason: `private/loopback IPv6 ${host} not allowed` };
|
|
71
|
+
}
|
|
72
|
+
return { ok: true, url: u };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Idempotent column-add for ownership tracking. Same shape as job-board's
|
|
76
|
+
* migrateOwnership — survive multiple boots without breaking.
|
|
77
|
+
*/
|
|
78
|
+
function migrateOwnership(d) {
|
|
79
|
+
for (const col of ['creator_id', 'workspace_id']) {
|
|
80
|
+
try {
|
|
81
|
+
d.prepare(`ALTER TABLE schedules ADD COLUMN ${col} TEXT NOT NULL DEFAULT ''`).run();
|
|
82
|
+
rootLogger.info({ component: 'schedule', column: col, event: 'schedule.migration' }, `Added ${col} column to schedules table`);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// duplicate column — already migrated
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const helper = createSqliteHelper({
|
|
90
|
+
file: SCHED_DB,
|
|
91
|
+
component: 'schedule',
|
|
92
|
+
logger: rootLogger,
|
|
93
|
+
schema: `
|
|
94
|
+
CREATE TABLE IF NOT EXISTS schedules (
|
|
95
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
96
|
+
name TEXT NOT NULL,
|
|
97
|
+
agent TEXT NOT NULL,
|
|
98
|
+
prompt TEXT NOT NULL,
|
|
99
|
+
cron TEXT NOT NULL,
|
|
100
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
101
|
+
next_run TEXT NOT NULL,
|
|
102
|
+
last_run TEXT,
|
|
103
|
+
notify_url TEXT,
|
|
104
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
105
|
+
creator_id TEXT NOT NULL DEFAULT '',
|
|
106
|
+
workspace_id TEXT NOT NULL DEFAULT ''
|
|
107
|
+
);
|
|
108
|
+
CREATE INDEX IF NOT EXISTS idx_sched_next ON schedules(next_run, enabled);
|
|
109
|
+
CREATE INDEX IF NOT EXISTS idx_sched_creator ON schedules(creator_id);
|
|
110
|
+
`,
|
|
111
|
+
init: (d) => migrateOwnership(d),
|
|
112
|
+
});
|
|
113
|
+
function getDb() {
|
|
114
|
+
return helper.get();
|
|
115
|
+
}
|
|
116
|
+
export function createSchedule(input) {
|
|
117
|
+
const d = getDb();
|
|
118
|
+
if (!d)
|
|
119
|
+
throw new Error('Scheduler unavailable (SQLite not initialized)');
|
|
120
|
+
const spec = parseCron(input.cron); // throws on bad expr
|
|
121
|
+
const next = nextOccurrence(spec, new Date());
|
|
122
|
+
if (!next)
|
|
123
|
+
throw new Error('Cron expression has no future occurrence');
|
|
124
|
+
if (input.notifyUrl) {
|
|
125
|
+
const v = validateWebhookUrl(input.notifyUrl);
|
|
126
|
+
if (!v.ok)
|
|
127
|
+
throw new Error(`notifyUrl rejected: ${v.reason}`);
|
|
128
|
+
}
|
|
129
|
+
const info = d.prepare('INSERT INTO schedules (name, agent, prompt, cron, enabled, next_run, notify_url, creator_id, workspace_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)').run(input.name, input.agent, input.prompt, input.cron, input.enabled === false ? 0 : 1, next.toISOString(), input.notifyUrl || null, input.creatorId || '', input.workspaceId || '');
|
|
130
|
+
return Number(info.lastInsertRowid);
|
|
131
|
+
}
|
|
132
|
+
export function listSchedules(limit = 50, opts = {}) {
|
|
133
|
+
const d = getDb();
|
|
134
|
+
if (!d)
|
|
135
|
+
return [];
|
|
136
|
+
const conditions = [];
|
|
137
|
+
const params = [];
|
|
138
|
+
if (opts.creatorId !== undefined) {
|
|
139
|
+
conditions.push("(creator_id = ? OR creator_id = '')");
|
|
140
|
+
params.push(opts.creatorId);
|
|
141
|
+
}
|
|
142
|
+
if (opts.agent) {
|
|
143
|
+
conditions.push('agent = ?');
|
|
144
|
+
params.push(opts.agent);
|
|
145
|
+
}
|
|
146
|
+
const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
147
|
+
return d.prepare(`SELECT * FROM schedules ${where} ORDER BY id DESC LIMIT ?`)
|
|
148
|
+
.all(...params, limit);
|
|
149
|
+
}
|
|
150
|
+
export function getSchedule(id, opts = {}) {
|
|
151
|
+
const d = getDb();
|
|
152
|
+
if (!d)
|
|
153
|
+
return null;
|
|
154
|
+
const row = (opts.creatorId !== undefined
|
|
155
|
+
? d.prepare("SELECT * FROM schedules WHERE id = ? AND (creator_id = ? OR creator_id = '')")
|
|
156
|
+
.get(id, opts.creatorId)
|
|
157
|
+
: d.prepare('SELECT * FROM schedules WHERE id = ?').get(id));
|
|
158
|
+
return row || null;
|
|
159
|
+
}
|
|
160
|
+
export function deleteSchedule(id, opts = {}) {
|
|
161
|
+
const d = getDb();
|
|
162
|
+
if (!d)
|
|
163
|
+
return false;
|
|
164
|
+
if (opts.creatorId !== undefined) {
|
|
165
|
+
return d.prepare("DELETE FROM schedules WHERE id = ? AND (creator_id = ? OR creator_id = '')")
|
|
166
|
+
.run(id, opts.creatorId).changes > 0;
|
|
167
|
+
}
|
|
168
|
+
return d.prepare('DELETE FROM schedules WHERE id = ?').run(id).changes > 0;
|
|
169
|
+
}
|
|
170
|
+
export function setEnabled(id, enabled, opts = {}) {
|
|
171
|
+
const d = getDb();
|
|
172
|
+
if (!d)
|
|
173
|
+
return false;
|
|
174
|
+
if (opts.creatorId !== undefined) {
|
|
175
|
+
return d.prepare("UPDATE schedules SET enabled = ? WHERE id = ? AND (creator_id = ? OR creator_id = '')").run(enabled ? 1 : 0, id, opts.creatorId).changes > 0;
|
|
176
|
+
}
|
|
177
|
+
return d.prepare('UPDATE schedules SET enabled = ? WHERE id = ?')
|
|
178
|
+
.run(enabled ? 1 : 0, id).changes > 0;
|
|
179
|
+
}
|
|
180
|
+
/** Find schedules whose next_run is at or before `now` and are enabled. */
|
|
181
|
+
function dueSchedules(d, now) {
|
|
182
|
+
return d.prepare("SELECT * FROM schedules WHERE enabled = 1 AND next_run <= ?").all(now.toISOString());
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Execute a single scheduled run: create a Job, kick it off through Job
|
|
186
|
+
* Board (which honors the concurrency cap), bump next_run.
|
|
187
|
+
*/
|
|
188
|
+
async function fireSchedule(s, logger) {
|
|
189
|
+
const d = getDb();
|
|
190
|
+
if (!d)
|
|
191
|
+
return;
|
|
192
|
+
const agent = registry.findAgent(s.agent);
|
|
193
|
+
if (!agent) {
|
|
194
|
+
logger.warn({ scheduleId: s.id, agent: s.agent }, 'Schedule agent not registered, skipping');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const jobId = createJob(s.agent, s.prompt);
|
|
198
|
+
logger.info({ scheduleId: s.id, jobId, name: s.name }, 'Schedule fired');
|
|
199
|
+
// Compute next_run BEFORE running so even a long-running job doesn't
|
|
200
|
+
// block the next tick.
|
|
201
|
+
try {
|
|
202
|
+
const nextSpec = parseCron(s.cron);
|
|
203
|
+
const next = nextOccurrence(nextSpec, new Date());
|
|
204
|
+
if (next) {
|
|
205
|
+
d.prepare('UPDATE schedules SET last_run = ?, next_run = ? WHERE id = ?')
|
|
206
|
+
.run(new Date().toISOString(), next.toISOString(), s.id);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Bad cron at this point would only happen if it was edited externally.
|
|
211
|
+
// Disable rather than re-fire forever.
|
|
212
|
+
d.prepare('UPDATE schedules SET enabled = 0 WHERE id = ?').run(s.id);
|
|
213
|
+
}
|
|
214
|
+
// Run via Job Board so the result is persisted + bounded concurrency.
|
|
215
|
+
void runJob(jobId, async function* (job, jobLogger, signal) {
|
|
216
|
+
if (agent instanceof AgentBase) {
|
|
217
|
+
const text = await agent.spawnAndCollect(job.prompt, signal);
|
|
218
|
+
if (text)
|
|
219
|
+
yield text;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
for await (const chunk of agent.sendPrompt(`schedule-${s.id}-${jobId}`, job.prompt, [])) {
|
|
223
|
+
if (signal.aborted)
|
|
224
|
+
break;
|
|
225
|
+
yield chunk;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}, logger).then(async () => {
|
|
229
|
+
if (!s.notify_url)
|
|
230
|
+
return;
|
|
231
|
+
// Defense in depth: re-validate at fire time in case the row was edited
|
|
232
|
+
// out-of-band (manual SQL, future v0.x importer) — never trust DB content
|
|
233
|
+
// as an authorization bypass for the SSRF gate.
|
|
234
|
+
const v = validateWebhookUrl(s.notify_url);
|
|
235
|
+
if (!v.ok) {
|
|
236
|
+
logger.warn({ scheduleId: s.id, reason: v.reason, event: 'schedule.notify.rejected' }, 'Schedule notify webhook rejected by URL validator');
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
const job = (await import('./job-board.js')).getJob(jobId);
|
|
241
|
+
if (!job)
|
|
242
|
+
return;
|
|
243
|
+
const ctrl = new AbortController();
|
|
244
|
+
const timer = setTimeout(() => ctrl.abort(), NOTIFY_TIMEOUT_MS);
|
|
245
|
+
try {
|
|
246
|
+
const resp = await fetch(v.url.toString(), {
|
|
247
|
+
method: 'POST',
|
|
248
|
+
headers: { 'content-type': 'application/json' },
|
|
249
|
+
body: JSON.stringify({
|
|
250
|
+
scheduleId: s.id,
|
|
251
|
+
jobId,
|
|
252
|
+
status: job.status,
|
|
253
|
+
result: job.result,
|
|
254
|
+
error: job.error,
|
|
255
|
+
}),
|
|
256
|
+
signal: ctrl.signal,
|
|
257
|
+
// Drop redirects so a 30x can't pivot us back to a private host.
|
|
258
|
+
redirect: 'manual',
|
|
259
|
+
});
|
|
260
|
+
// Drain (capped) so the connection can be reused / closed cleanly.
|
|
261
|
+
if (resp.body) {
|
|
262
|
+
let read = 0;
|
|
263
|
+
const reader = resp.body.getReader();
|
|
264
|
+
while (read < NOTIFY_MAX_RESPONSE_BYTES) {
|
|
265
|
+
const { done, value } = await reader.read();
|
|
266
|
+
if (done)
|
|
267
|
+
break;
|
|
268
|
+
read += value?.byteLength ?? 0;
|
|
269
|
+
}
|
|
270
|
+
await reader.cancel().catch(() => { });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
clearTimeout(timer);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch (err) {
|
|
278
|
+
logger.warn({ scheduleId: s.id, err: err instanceof Error ? err.message : String(err) }, 'Schedule notify webhook failed');
|
|
279
|
+
}
|
|
280
|
+
}).catch(() => { });
|
|
281
|
+
}
|
|
282
|
+
/** Run one tick — exposed for tests so they don't have to wait for setInterval. */
|
|
283
|
+
export async function tick(now = new Date()) {
|
|
284
|
+
const d = getDb();
|
|
285
|
+
if (!d)
|
|
286
|
+
return 0;
|
|
287
|
+
const due = dueSchedules(d, now);
|
|
288
|
+
for (const s of due) {
|
|
289
|
+
await fireSchedule(s, log).catch((err) => {
|
|
290
|
+
log.error({ scheduleId: s.id, err: err instanceof Error ? err.message : String(err) }, 'fireSchedule threw');
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return due.length;
|
|
294
|
+
}
|
|
295
|
+
/** Start the periodic ticker. Idempotent. */
|
|
296
|
+
export function startScheduler() {
|
|
297
|
+
if (tickTimer)
|
|
298
|
+
return;
|
|
299
|
+
tickTimer = setInterval(() => { void tick(); }, TICK_INTERVAL_MS);
|
|
300
|
+
if (typeof tickTimer === 'object' && tickTimer && 'unref' in tickTimer) {
|
|
301
|
+
tickTimer.unref();
|
|
302
|
+
}
|
|
303
|
+
log.info({ tickMs: TICK_INTERVAL_MS }, 'Scheduler started');
|
|
304
|
+
}
|
|
305
|
+
export function stopScheduler() {
|
|
306
|
+
if (tickTimer) {
|
|
307
|
+
clearInterval(tickTimer);
|
|
308
|
+
tickTimer = null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/** Close the underlying SQLite handle. Called by cli.ts on graceful
|
|
312
|
+
* shutdown so the WAL is checkpointed before exit (L2). */
|
|
313
|
+
export function closeScheduleDb() {
|
|
314
|
+
helper.close();
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=schedule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule.js","sourceRoot":"","sources":["../../src/core/schedule.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-subtasks.test.d.ts","sourceRoot":"","sources":["../../src/core/session-subtasks.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// listAllSubtasks — covers the dashboard's flattened-subtask view. We can't
|
|
2
|
+
// monkey-patch SESSIONS_DIR (it's a const), so this test goes via the real
|
|
3
|
+
// public API: nextSubtaskId + updateSubtask write to ~/.im-hub/sessions, and
|
|
4
|
+
// we then read them back and clean up.
|
|
5
|
+
//
|
|
6
|
+
// We use a unique platform/channelId/threadId triple so we don't collide with
|
|
7
|
+
// any real sessions and we delete them afterwards.
|
|
8
|
+
import { describe, it, expect, beforeAll, afterEach } from 'bun:test';
|
|
9
|
+
import { homedir } from 'os';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { mkdir, unlink, readdir } from 'fs/promises';
|
|
12
|
+
import { sessionManager } from './session.js';
|
|
13
|
+
const PLATFORM = 'subtest';
|
|
14
|
+
const CHANNEL = 'c-' + Math.random().toString(36).slice(2, 8);
|
|
15
|
+
const THREAD = 't-' + Math.random().toString(36).slice(2, 8);
|
|
16
|
+
const SESSIONS_DIR = join(homedir(), '.im-hub', 'sessions');
|
|
17
|
+
// CI runs on a fresh runner where ~/.im-hub/sessions/ doesn't exist yet.
|
|
18
|
+
// Without this beforeAll, saveSessionMeta's atomicWrite hits ENOENT, the
|
|
19
|
+
// outer try/catch swallows it (correct for runtime — never crash the
|
|
20
|
+
// session manager on a disk hiccup) and the test sees 0 subtasks instead
|
|
21
|
+
// of 2. Local boxes happened to have the dir from prior runs, masking
|
|
22
|
+
// the issue until a clean CI environment surfaced it.
|
|
23
|
+
beforeAll(async () => {
|
|
24
|
+
await mkdir(SESSIONS_DIR, { recursive: true });
|
|
25
|
+
});
|
|
26
|
+
async function cleanupTestSessions() {
|
|
27
|
+
// The session manager sanitizes the key, so we can't compute the filename.
|
|
28
|
+
// Instead, scan and delete any file whose JSON has our test platform.
|
|
29
|
+
let names;
|
|
30
|
+
try {
|
|
31
|
+
names = await readdir(SESSIONS_DIR);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const { readFile } = await import('fs/promises');
|
|
37
|
+
for (const name of names) {
|
|
38
|
+
if (!name.endsWith('.json'))
|
|
39
|
+
continue;
|
|
40
|
+
try {
|
|
41
|
+
const raw = await readFile(join(SESSIONS_DIR, name), 'utf-8');
|
|
42
|
+
const parsed = JSON.parse(raw);
|
|
43
|
+
if (parsed.platform === PLATFORM) {
|
|
44
|
+
await unlink(join(SESSIONS_DIR, name)).catch(() => { });
|
|
45
|
+
await unlink(join(SESSIONS_DIR, name.replace(/\.json$/, '.log'))).catch(() => { });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch { /* ignore */ }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
afterEach(async () => {
|
|
52
|
+
await cleanupTestSessions();
|
|
53
|
+
});
|
|
54
|
+
describe('sessionManager.listAllSubtasks', () => {
|
|
55
|
+
it('returns subtasks across all sessions, flattened with parent context', async () => {
|
|
56
|
+
// Spawn two subtasks in our test session.
|
|
57
|
+
const id1 = await sessionManager.nextSubtaskId(PLATFORM, CHANNEL, THREAD, 'claude-code');
|
|
58
|
+
await sessionManager.updateSubtask(PLATFORM, CHANNEL, THREAD, id1, {
|
|
59
|
+
id: id1, agent: 'claude-code', prompt: 'hello',
|
|
60
|
+
status: 'running', createdAt: new Date('2026-05-01T09:00:00Z'),
|
|
61
|
+
});
|
|
62
|
+
const id2 = await sessionManager.nextSubtaskId(PLATFORM, CHANNEL, THREAD, 'claude-code');
|
|
63
|
+
await sessionManager.updateSubtask(PLATFORM, CHANNEL, THREAD, id2, {
|
|
64
|
+
id: id2, agent: 'opencode', prompt: 'world',
|
|
65
|
+
status: 'completed', createdAt: new Date('2026-05-01T10:00:00Z'),
|
|
66
|
+
});
|
|
67
|
+
const all = await sessionManager.listAllSubtasks();
|
|
68
|
+
const ours = all.filter((s) => s.platform === PLATFORM);
|
|
69
|
+
expect(ours.length).toBe(2);
|
|
70
|
+
// newest first
|
|
71
|
+
expect(ours[0].createdAt.getTime()).toBeGreaterThan(ours[1].createdAt.getTime());
|
|
72
|
+
// parent context attached
|
|
73
|
+
for (const s of ours) {
|
|
74
|
+
expect(s.platform).toBe(PLATFORM);
|
|
75
|
+
expect(s.channelId).toBe(CHANNEL);
|
|
76
|
+
expect(s.threadId).toBe(THREAD);
|
|
77
|
+
expect(s.parentSessionId).toContain(PLATFORM);
|
|
78
|
+
}
|
|
79
|
+
expect(ours.find((s) => s.agent === 'opencode')?.status).toBe('completed');
|
|
80
|
+
});
|
|
81
|
+
it('returns empty array when no sessions on disk match', async () => {
|
|
82
|
+
// No subtasks written for our (unique) platform. Other real sessions
|
|
83
|
+
// may exist; our filter shows ours is zero.
|
|
84
|
+
const all = await sessionManager.listAllSubtasks();
|
|
85
|
+
expect(all.filter((s) => s.platform === PLATFORM).length).toBe(0);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=session-subtasks.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-subtasks.test.js","sourceRoot":"","sources":["../../src/core/session-subtasks.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type { Session, ChatMessage, SubtaskMeta } from './types.js';
|
|
2
|
+
declare class SessionManager {
|
|
3
|
+
private sessions;
|
|
4
|
+
private cleanupTimer?;
|
|
5
|
+
/**
|
|
6
|
+
* Per-key promise chain used to serialize writes that perform a
|
|
7
|
+
* read-modify-write on the in-memory session + persistent JSONL log.
|
|
8
|
+
*
|
|
9
|
+
* Without this, two concurrent {@link addMessage} calls on the same key
|
|
10
|
+
* could interleave: A reads cached session, B reads cached session, both
|
|
11
|
+
* push a different message into the same array, then both write — the
|
|
12
|
+
* later write wins and the earlier message is lost from disk (the
|
|
13
|
+
* in-memory copy is fine because both pushes happened on the same object
|
|
14
|
+
* reference, but the JSONL log only reflects the most recent appendFile +
|
|
15
|
+
* meta save).
|
|
16
|
+
*
|
|
17
|
+
* The lock is keyed by `${platform}:${channelId}:${threadId}`; different
|
|
18
|
+
* threads still proceed in parallel.
|
|
19
|
+
*/
|
|
20
|
+
private writeQueues;
|
|
21
|
+
start(): Promise<void>;
|
|
22
|
+
stop(): void;
|
|
23
|
+
/**
|
|
24
|
+
* Run `fn` while holding a per-key serial lock. Subsequent calls with the
|
|
25
|
+
* same key wait until prior ones settle (success OR failure). Returns the
|
|
26
|
+
* value of `fn`. Internal use only — keeps {@link addMessage} race-free
|
|
27
|
+
* without introducing an external dep like p-queue.
|
|
28
|
+
*/
|
|
29
|
+
private withLock;
|
|
30
|
+
/**
|
|
31
|
+
* Get or create a session for a conversation
|
|
32
|
+
* Session key: `${platform}:${channelId}:${threadId}`
|
|
33
|
+
*/
|
|
34
|
+
getOrCreateSession(platform: string, channelId: string, threadId: string, agent: string): Promise<Session>;
|
|
35
|
+
/**
|
|
36
|
+
* Get existing session without creating a new one
|
|
37
|
+
* Returns undefined if no session exists or it's expired
|
|
38
|
+
*/
|
|
39
|
+
getExistingSession(platform: string, channelId: string, threadId: string): Promise<Session | undefined>;
|
|
40
|
+
/**
|
|
41
|
+
* Switch the agent for a session.
|
|
42
|
+
*
|
|
43
|
+
* Generates a new session id but preserves thread identity AND every
|
|
44
|
+
* thread-level field that isn't agent-specific:
|
|
45
|
+
* - usage (per-thread /stats roll-up)
|
|
46
|
+
* - subtasks/active (subtask state lives at thread level)
|
|
47
|
+
* - claudeSessionId (Claude UUID survives /oc → /cc round-trips so the
|
|
48
|
+
* underlying ~/.claude/projects jsonl keeps continuing
|
|
49
|
+
* when the user comes back to claude)
|
|
50
|
+
*
|
|
51
|
+
* `model` and `variant` are reset because they live in different namespaces
|
|
52
|
+
* across CLIs (`opencode` model ≠ `claude` model); carrying them across
|
|
53
|
+
* would just feed the new agent an unrecognized argument.
|
|
54
|
+
*/
|
|
55
|
+
switchAgent(platform: string, channelId: string, threadId: string, newAgent: string): Promise<Session>;
|
|
56
|
+
/**
|
|
57
|
+
* Append a message to the session history.
|
|
58
|
+
*
|
|
59
|
+
* Performance: instead of re-serializing the entire session JSON every
|
|
60
|
+
* turn, the message body is appended to a JSONL log file alongside the
|
|
61
|
+
* metadata (which gets a tiny atomic update for `lastActivity`).
|
|
62
|
+
*
|
|
63
|
+
* Concurrency: the entire read-modify-write is serialized per session key
|
|
64
|
+
* via {@link withLock} so two concurrent calls on the same thread (e.g.
|
|
65
|
+
* the IM message landing event AND a tool-result event arriving within
|
|
66
|
+
* the same ms) cannot lose either message to a lost-update race.
|
|
67
|
+
*/
|
|
68
|
+
addMessage(platform: string, channelId: string, threadId: string, message: ChatMessage): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Persist `model` / `variant` / arbitrary patchable fields. Used by
|
|
71
|
+
* `/model`, `/think` etc so the change survives a restart between turns.
|
|
72
|
+
* Mutates the in-memory session in place AND writes metadata atomically.
|
|
73
|
+
*/
|
|
74
|
+
patchSession(platform: string, channelId: string, threadId: string, patch: Partial<Pick<Session, 'model' | 'variant' | 'agent' | 'planMode'>>): Promise<Session | undefined>;
|
|
75
|
+
/**
|
|
76
|
+
* Persist claude-code resumable session bookkeeping (UUID + primed flag).
|
|
77
|
+
* Returns the updated session, or undefined if no session exists yet for
|
|
78
|
+
* this thread. Caller is expected to ensure the session exists first.
|
|
79
|
+
*/
|
|
80
|
+
setClaudeSessionId(platform: string, channelId: string, threadId: string, claudeSessionId: string): Promise<Session | undefined>;
|
|
81
|
+
markClaudeSessionPrimed(platform: string, channelId: string, threadId: string): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Persist opencode's native session id (`ses_…`) once we've seen it in the
|
|
84
|
+
* adapter's stream. Idempotent — calling with the same id is a no-op so
|
|
85
|
+
* the per-event callback can fire as many times as opencode sends events.
|
|
86
|
+
*/
|
|
87
|
+
setOpencodeSessionId(platform: string, channelId: string, threadId: string, opencodeSessionId: string): Promise<Session | undefined>;
|
|
88
|
+
/**
|
|
89
|
+
* Persist codex's native thread id (UUID) once we've seen it in the
|
|
90
|
+
* adapter's `thread.started` event. Idempotent — same id may fire multiple
|
|
91
|
+
* times per spawn. Mirrors setOpencodeSessionId.
|
|
92
|
+
*/
|
|
93
|
+
setCodexSessionId(platform: string, channelId: string, threadId: string, codexSessionId: string): Promise<Session | undefined>;
|
|
94
|
+
/**
|
|
95
|
+
* Increment the per-session usage roll-up after a successful agent
|
|
96
|
+
* invocation. Used by router.callAgentWithHistory to power /stats.
|
|
97
|
+
*/
|
|
98
|
+
recordUsage(platform: string, channelId: string, threadId: string, delta: {
|
|
99
|
+
costUsd: number;
|
|
100
|
+
promptChars: number;
|
|
101
|
+
responseChars: number;
|
|
102
|
+
durationMs: number;
|
|
103
|
+
}): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Reset conversation history (keep session but clear messages)
|
|
106
|
+
*/
|
|
107
|
+
resetConversation(platform: string, channelId: string, threadId: string): Promise<Session | undefined>;
|
|
108
|
+
/**
|
|
109
|
+
* Get session with messages (convenience method)
|
|
110
|
+
*/
|
|
111
|
+
getSessionWithHistory(platform: string, channelId: string, threadId: string): Promise<{
|
|
112
|
+
session: Session;
|
|
113
|
+
messages: ChatMessage[];
|
|
114
|
+
} | undefined>;
|
|
115
|
+
/**
|
|
116
|
+
* Create or get a subtask session (independent from parent).
|
|
117
|
+
*/
|
|
118
|
+
getOrCreateSubSession(platform: string, channelId: string, threadId: string, subtaskId: number, agent: string): Promise<Session>;
|
|
119
|
+
/**
|
|
120
|
+
* Set active subtask id on parent session — subsequent messages route to the subtask.
|
|
121
|
+
*/
|
|
122
|
+
setActiveSubtask(platform: string, channelId: string, threadId: string, taskId: number | null): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Get subtask metadata list from parent session.
|
|
125
|
+
*/
|
|
126
|
+
getSubtasks(platform: string, channelId: string, threadId: string): Promise<SubtaskMeta[]>;
|
|
127
|
+
/**
|
|
128
|
+
* Scan all session files on disk and return every subtask, flattened, with
|
|
129
|
+
* its parent platform/channelId/threadId/agent attached so the dashboard
|
|
130
|
+
* can render subtasks across all conversations.
|
|
131
|
+
*
|
|
132
|
+
* Session files live as `<sanitized-key>.json` under SESSIONS_DIR. The
|
|
133
|
+
* sanitized key is one-way (sha256-prefix per non-alnum char), so we
|
|
134
|
+
* cannot reverse it — but each session file preserves the original
|
|
135
|
+
* platform/channelId/threadId fields, which is what we need.
|
|
136
|
+
*/
|
|
137
|
+
listAllSubtasks(opts?: {
|
|
138
|
+
agent?: string;
|
|
139
|
+
}): Promise<Array<SubtaskMeta & {
|
|
140
|
+
platform: string;
|
|
141
|
+
channelId: string;
|
|
142
|
+
threadId: string;
|
|
143
|
+
parentAgent: string;
|
|
144
|
+
parentSessionId: string;
|
|
145
|
+
}>>;
|
|
146
|
+
/**
|
|
147
|
+
* Update subtask metadata in parent session.
|
|
148
|
+
*/
|
|
149
|
+
updateSubtask(platform: string, channelId: string, threadId: string, taskId: number, patch: Partial<SubtaskMeta>): Promise<void>;
|
|
150
|
+
/**
|
|
151
|
+
* Get next subtask id and persist the increment.
|
|
152
|
+
*
|
|
153
|
+
* Previously returned 1 when the parent session didn't exist yet, but
|
|
154
|
+
* never created one — second call returned 1 again, leading to subtask
|
|
155
|
+
* id collisions. Now we lazy-create the parent session so the counter
|
|
156
|
+
* increments durably from the first call.
|
|
157
|
+
*/
|
|
158
|
+
nextSubtaskId(platform: string, channelId: string, threadId: string, agent?: string): Promise<number>;
|
|
159
|
+
/**
|
|
160
|
+
* Persist the full session (metadata + messages). Used for the legacy
|
|
161
|
+
* one-file format on resetConversation() and switchAgent() — anywhere
|
|
162
|
+
* the messages array itself was rewritten. Atomic via tmp+rename.
|
|
163
|
+
*/
|
|
164
|
+
private saveSession;
|
|
165
|
+
/** Persist metadata only (no messages payload), atomically. */
|
|
166
|
+
private saveSessionMeta;
|
|
167
|
+
/** Crash-safe write: tmp file + atomic rename.
|
|
168
|
+
*
|
|
169
|
+
* Recovers from ENOENT (parent dir missing) by mkdir-recursive + retry
|
|
170
|
+
* exactly once. This keeps the manager robust to environments where
|
|
171
|
+
* start() wasn't called yet — tests in particular tend to import
|
|
172
|
+
* sessionManager without going through the start() lifecycle, and
|
|
173
|
+
* saveSessionMeta's outer try/catch would otherwise swallow the ENOENT
|
|
174
|
+
* and silently produce a no-op write (the bug that caused
|
|
175
|
+
* session-subtasks.test.ts to fail on a fresh CI runner). */
|
|
176
|
+
private atomicWrite;
|
|
177
|
+
private loadSession;
|
|
178
|
+
private cleanup;
|
|
179
|
+
}
|
|
180
|
+
export declare const sessionManager: SessionManager;
|
|
181
|
+
export {};
|
|
182
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"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"}
|