openryoko 0.9.4-ryoko.2
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/dist/bin/jimmy.d.ts +3 -0
- package/dist/bin/jimmy.d.ts.map +1 -0
- package/dist/bin/jimmy.js +149 -0
- package/dist/bin/jimmy.js.map +1 -0
- package/dist/package.json +54 -0
- package/dist/src/cli/__tests__/migrate.test.d.ts +2 -0
- package/dist/src/cli/__tests__/migrate.test.d.ts.map +1 -0
- package/dist/src/cli/__tests__/migrate.test.js +79 -0
- package/dist/src/cli/__tests__/migrate.test.js.map +1 -0
- package/dist/src/cli/chrome-allow.d.ts +5 -0
- package/dist/src/cli/chrome-allow.d.ts.map +1 -0
- package/dist/src/cli/chrome-allow.js +250 -0
- package/dist/src/cli/chrome-allow.js.map +1 -0
- package/dist/src/cli/create.d.ts +2 -0
- package/dist/src/cli/create.d.ts.map +1 -0
- package/dist/src/cli/create.js +72 -0
- package/dist/src/cli/create.js.map +1 -0
- package/dist/src/cli/instances.d.ts +14 -0
- package/dist/src/cli/instances.d.ts.map +1 -0
- package/dist/src/cli/instances.js +43 -0
- package/dist/src/cli/instances.js.map +1 -0
- package/dist/src/cli/list.d.ts +2 -0
- package/dist/src/cli/list.d.ts.map +1 -0
- package/dist/src/cli/list.js +38 -0
- package/dist/src/cli/list.js.map +1 -0
- package/dist/src/cli/migrate.d.ts +5 -0
- package/dist/src/cli/migrate.d.ts.map +1 -0
- package/dist/src/cli/migrate.js +222 -0
- package/dist/src/cli/migrate.js.map +1 -0
- package/dist/src/cli/nuke.d.ts +2 -0
- package/dist/src/cli/nuke.d.ts.map +1 -0
- package/dist/src/cli/nuke.js +91 -0
- package/dist/src/cli/nuke.js.map +1 -0
- package/dist/src/cli/remove.d.ts +4 -0
- package/dist/src/cli/remove.d.ts.map +1 -0
- package/dist/src/cli/remove.js +47 -0
- package/dist/src/cli/remove.js.map +1 -0
- package/dist/src/cli/setup.d.ts +4 -0
- package/dist/src/cli/setup.d.ts.map +1 -0
- package/dist/src/cli/setup.js +491 -0
- package/dist/src/cli/setup.js.map +1 -0
- package/dist/src/cli/skills.d.ts +28 -0
- package/dist/src/cli/skills.d.ts.map +1 -0
- package/dist/src/cli/skills.js +284 -0
- package/dist/src/cli/skills.js.map +1 -0
- package/dist/src/cli/start.d.ts +5 -0
- package/dist/src/cli/start.d.ts.map +1 -0
- package/dist/src/cli/start.js +34 -0
- package/dist/src/cli/start.js.map +1 -0
- package/dist/src/cli/status.d.ts +2 -0
- package/dist/src/cli/status.d.ts.map +1 -0
- package/dist/src/cli/status.js +69 -0
- package/dist/src/cli/status.js.map +1 -0
- package/dist/src/cli/stop.d.ts +2 -0
- package/dist/src/cli/stop.d.ts.map +1 -0
- package/dist/src/cli/stop.js +11 -0
- package/dist/src/cli/stop.js.map +1 -0
- package/dist/src/connectors/cron/index.d.ts +21 -0
- package/dist/src/connectors/cron/index.d.ts.map +1 -0
- package/dist/src/connectors/cron/index.js +75 -0
- package/dist/src/connectors/cron/index.js.map +1 -0
- package/dist/src/connectors/discord/format.d.ts +3 -0
- package/dist/src/connectors/discord/format.d.ts.map +1 -0
- package/dist/src/connectors/discord/format.js +33 -0
- package/dist/src/connectors/discord/format.js.map +1 -0
- package/dist/src/connectors/discord/index.d.ts +47 -0
- package/dist/src/connectors/discord/index.d.ts.map +1 -0
- package/dist/src/connectors/discord/index.js +308 -0
- package/dist/src/connectors/discord/index.js.map +1 -0
- package/dist/src/connectors/discord/remote.d.ts +33 -0
- package/dist/src/connectors/discord/remote.d.ts.map +1 -0
- package/dist/src/connectors/discord/remote.js +89 -0
- package/dist/src/connectors/discord/remote.js.map +1 -0
- package/dist/src/connectors/discord/threads.d.ts +5 -0
- package/dist/src/connectors/discord/threads.d.ts.map +1 -0
- package/dist/src/connectors/discord/threads.js +22 -0
- package/dist/src/connectors/discord/threads.js.map +1 -0
- package/dist/src/connectors/slack/__tests__/active-threads.test.d.ts +2 -0
- package/dist/src/connectors/slack/__tests__/active-threads.test.d.ts.map +1 -0
- package/dist/src/connectors/slack/__tests__/active-threads.test.js +57 -0
- package/dist/src/connectors/slack/__tests__/active-threads.test.js.map +1 -0
- package/dist/src/connectors/slack/__tests__/format.test.d.ts +2 -0
- package/dist/src/connectors/slack/__tests__/format.test.d.ts.map +1 -0
- package/dist/src/connectors/slack/__tests__/format.test.js +115 -0
- package/dist/src/connectors/slack/__tests__/format.test.js.map +1 -0
- package/dist/src/connectors/slack/__tests__/speaker.test.d.ts +2 -0
- package/dist/src/connectors/slack/__tests__/speaker.test.d.ts.map +1 -0
- package/dist/src/connectors/slack/__tests__/speaker.test.js +59 -0
- package/dist/src/connectors/slack/__tests__/speaker.test.js.map +1 -0
- package/dist/src/connectors/slack/__tests__/triage-prompt.test.d.ts +2 -0
- package/dist/src/connectors/slack/__tests__/triage-prompt.test.d.ts.map +1 -0
- package/dist/src/connectors/slack/__tests__/triage-prompt.test.js +102 -0
- package/dist/src/connectors/slack/__tests__/triage-prompt.test.js.map +1 -0
- package/dist/src/connectors/slack/__tests__/triage.test.d.ts +2 -0
- package/dist/src/connectors/slack/__tests__/triage.test.d.ts.map +1 -0
- package/dist/src/connectors/slack/__tests__/triage.test.js +88 -0
- package/dist/src/connectors/slack/__tests__/triage.test.js.map +1 -0
- package/dist/src/connectors/slack/active-threads.d.ts +39 -0
- package/dist/src/connectors/slack/active-threads.d.ts.map +1 -0
- package/dist/src/connectors/slack/active-threads.js +71 -0
- package/dist/src/connectors/slack/active-threads.js.map +1 -0
- package/dist/src/connectors/slack/format.d.ts +17 -0
- package/dist/src/connectors/slack/format.d.ts.map +1 -0
- package/dist/src/connectors/slack/format.js +85 -0
- package/dist/src/connectors/slack/format.js.map +1 -0
- package/dist/src/connectors/slack/index.d.ts +51 -0
- package/dist/src/connectors/slack/index.d.ts.map +1 -0
- package/dist/src/connectors/slack/index.js +562 -0
- package/dist/src/connectors/slack/index.js.map +1 -0
- package/dist/src/connectors/slack/speaker.d.ts +39 -0
- package/dist/src/connectors/slack/speaker.d.ts.map +1 -0
- package/dist/src/connectors/slack/speaker.js +30 -0
- package/dist/src/connectors/slack/speaker.js.map +1 -0
- package/dist/src/connectors/slack/threads.d.ts +12 -0
- package/dist/src/connectors/slack/threads.d.ts.map +1 -0
- package/dist/src/connectors/slack/threads.js +41 -0
- package/dist/src/connectors/slack/threads.js.map +1 -0
- package/dist/src/connectors/slack/threads.test.d.ts +2 -0
- package/dist/src/connectors/slack/threads.test.d.ts.map +1 -0
- package/dist/src/connectors/slack/threads.test.js +79 -0
- package/dist/src/connectors/slack/threads.test.js.map +1 -0
- package/dist/src/connectors/slack/triage-prompt.d.ts +50 -0
- package/dist/src/connectors/slack/triage-prompt.d.ts.map +1 -0
- package/dist/src/connectors/slack/triage-prompt.js +127 -0
- package/dist/src/connectors/slack/triage-prompt.js.map +1 -0
- package/dist/src/connectors/slack/triage.d.ts +41 -0
- package/dist/src/connectors/slack/triage.d.ts.map +1 -0
- package/dist/src/connectors/slack/triage.js +120 -0
- package/dist/src/connectors/slack/triage.js.map +1 -0
- package/dist/src/connectors/telegram/__tests__/connector.test.d.ts +2 -0
- package/dist/src/connectors/telegram/__tests__/connector.test.d.ts.map +1 -0
- package/dist/src/connectors/telegram/__tests__/connector.test.js +257 -0
- package/dist/src/connectors/telegram/__tests__/connector.test.js.map +1 -0
- package/dist/src/connectors/telegram/__tests__/format.test.d.ts +2 -0
- package/dist/src/connectors/telegram/__tests__/format.test.d.ts.map +1 -0
- package/dist/src/connectors/telegram/__tests__/format.test.js +79 -0
- package/dist/src/connectors/telegram/__tests__/format.test.js.map +1 -0
- package/dist/src/connectors/telegram/__tests__/threads.test.d.ts +2 -0
- package/dist/src/connectors/telegram/__tests__/threads.test.d.ts.map +1 -0
- package/dist/src/connectors/telegram/__tests__/threads.test.js +51 -0
- package/dist/src/connectors/telegram/__tests__/threads.test.js.map +1 -0
- package/dist/src/connectors/telegram/format.d.ts +12 -0
- package/dist/src/connectors/telegram/format.d.ts.map +1 -0
- package/dist/src/connectors/telegram/format.js +61 -0
- package/dist/src/connectors/telegram/format.js.map +1 -0
- package/dist/src/connectors/telegram/index.d.ts +28 -0
- package/dist/src/connectors/telegram/index.d.ts.map +1 -0
- package/dist/src/connectors/telegram/index.js +213 -0
- package/dist/src/connectors/telegram/index.js.map +1 -0
- package/dist/src/connectors/telegram/threads.d.ts +29 -0
- package/dist/src/connectors/telegram/threads.d.ts.map +1 -0
- package/dist/src/connectors/telegram/threads.js +26 -0
- package/dist/src/connectors/telegram/threads.js.map +1 -0
- package/dist/src/connectors/whatsapp/__tests__/format.test.d.ts +2 -0
- package/dist/src/connectors/whatsapp/__tests__/format.test.d.ts.map +1 -0
- package/dist/src/connectors/whatsapp/__tests__/format.test.js +70 -0
- package/dist/src/connectors/whatsapp/__tests__/format.test.js.map +1 -0
- package/dist/src/connectors/whatsapp/format.d.ts +12 -0
- package/dist/src/connectors/whatsapp/format.d.ts.map +1 -0
- package/dist/src/connectors/whatsapp/format.js +53 -0
- package/dist/src/connectors/whatsapp/format.js.map +1 -0
- package/dist/src/connectors/whatsapp/index.d.ts +42 -0
- package/dist/src/connectors/whatsapp/index.d.ts.map +1 -0
- package/dist/src/connectors/whatsapp/index.js +279 -0
- package/dist/src/connectors/whatsapp/index.js.map +1 -0
- package/dist/src/cron/jobs.d.ts +5 -0
- package/dist/src/cron/jobs.d.ts.map +1 -0
- package/dist/src/cron/jobs.js +23 -0
- package/dist/src/cron/jobs.js.map +1 -0
- package/dist/src/cron/runner.d.ts +4 -0
- package/dist/src/cron/runner.d.ts.map +1 -0
- package/dist/src/cron/runner.js +88 -0
- package/dist/src/cron/runner.js.map +1 -0
- package/dist/src/cron/scheduler.d.ts +8 -0
- package/dist/src/cron/scheduler.d.ts.map +1 -0
- package/dist/src/cron/scheduler.js +64 -0
- package/dist/src/cron/scheduler.js.map +1 -0
- package/dist/src/engines/__tests__/gemini.test.d.ts +2 -0
- package/dist/src/engines/__tests__/gemini.test.d.ts.map +1 -0
- package/dist/src/engines/__tests__/gemini.test.js +404 -0
- package/dist/src/engines/__tests__/gemini.test.js.map +1 -0
- package/dist/src/engines/claude.d.ts +20 -0
- package/dist/src/engines/claude.d.ts.map +1 -0
- package/dist/src/engines/claude.js +520 -0
- package/dist/src/engines/claude.js.map +1 -0
- package/dist/src/engines/codex.d.ts +15 -0
- package/dist/src/engines/codex.d.ts.map +1 -0
- package/dist/src/engines/codex.js +351 -0
- package/dist/src/engines/codex.js.map +1 -0
- package/dist/src/engines/gemini.d.ts +32 -0
- package/dist/src/engines/gemini.d.ts.map +1 -0
- package/dist/src/engines/gemini.js +328 -0
- package/dist/src/engines/gemini.js.map +1 -0
- package/dist/src/engines/mock.d.ts +16 -0
- package/dist/src/engines/mock.d.ts.map +1 -0
- package/dist/src/engines/mock.js +47 -0
- package/dist/src/engines/mock.js.map +1 -0
- package/dist/src/gateway/__tests__/api-last-n.test.d.ts +2 -0
- package/dist/src/gateway/__tests__/api-last-n.test.d.ts.map +1 -0
- package/dist/src/gateway/__tests__/api-last-n.test.js +32 -0
- package/dist/src/gateway/__tests__/api-last-n.test.js.map +1 -0
- package/dist/src/gateway/__tests__/costs.test.d.ts +2 -0
- package/dist/src/gateway/__tests__/costs.test.d.ts.map +1 -0
- package/dist/src/gateway/__tests__/costs.test.js +32 -0
- package/dist/src/gateway/__tests__/costs.test.js.map +1 -0
- package/dist/src/gateway/__tests__/org-hierarchy.test.d.ts +2 -0
- package/dist/src/gateway/__tests__/org-hierarchy.test.d.ts.map +1 -0
- package/dist/src/gateway/__tests__/org-hierarchy.test.js +161 -0
- package/dist/src/gateway/__tests__/org-hierarchy.test.js.map +1 -0
- package/dist/src/gateway/__tests__/org-update.test.d.ts +2 -0
- package/dist/src/gateway/__tests__/org-update.test.d.ts.map +1 -0
- package/dist/src/gateway/__tests__/org-update.test.js +99 -0
- package/dist/src/gateway/__tests__/org-update.test.js.map +1 -0
- package/dist/src/gateway/__tests__/org.test.d.ts +2 -0
- package/dist/src/gateway/__tests__/org.test.d.ts.map +1 -0
- package/dist/src/gateway/__tests__/org.test.js +77 -0
- package/dist/src/gateway/__tests__/org.test.js.map +1 -0
- package/dist/src/gateway/__tests__/services.test.d.ts +2 -0
- package/dist/src/gateway/__tests__/services.test.d.ts.map +1 -0
- package/dist/src/gateway/__tests__/services.test.js +153 -0
- package/dist/src/gateway/__tests__/services.test.js.map +1 -0
- package/dist/src/gateway/api.d.ts +19 -0
- package/dist/src/gateway/api.d.ts.map +1 -0
- package/dist/src/gateway/api.js +2261 -0
- package/dist/src/gateway/api.js.map +1 -0
- package/dist/src/gateway/budgets.d.ts +16 -0
- package/dist/src/gateway/budgets.d.ts.map +1 -0
- package/dist/src/gateway/budgets.js +39 -0
- package/dist/src/gateway/budgets.js.map +1 -0
- package/dist/src/gateway/costs.d.ts +19 -0
- package/dist/src/gateway/costs.d.ts.map +1 -0
- package/dist/src/gateway/costs.js +39 -0
- package/dist/src/gateway/costs.js.map +1 -0
- package/dist/src/gateway/daemon-entry.d.ts +2 -0
- package/dist/src/gateway/daemon-entry.d.ts.map +1 -0
- package/dist/src/gateway/daemon-entry.js +12 -0
- package/dist/src/gateway/daemon-entry.js.map +1 -0
- package/dist/src/gateway/files.d.ts +6 -0
- package/dist/src/gateway/files.d.ts.map +1 -0
- package/dist/src/gateway/files.js +442 -0
- package/dist/src/gateway/files.js.map +1 -0
- package/dist/src/gateway/goals.d.ts +11 -0
- package/dist/src/gateway/goals.d.ts.map +1 -0
- package/dist/src/gateway/goals.js +72 -0
- package/dist/src/gateway/goals.js.map +1 -0
- package/dist/src/gateway/lifecycle.d.ts +10 -0
- package/dist/src/gateway/lifecycle.d.ts.map +1 -0
- package/dist/src/gateway/lifecycle.js +152 -0
- package/dist/src/gateway/lifecycle.js.map +1 -0
- package/dist/src/gateway/org-hierarchy.d.ts +5 -0
- package/dist/src/gateway/org-hierarchy.d.ts.map +1 -0
- package/dist/src/gateway/org-hierarchy.js +174 -0
- package/dist/src/gateway/org-hierarchy.js.map +1 -0
- package/dist/src/gateway/org.d.ts +17 -0
- package/dist/src/gateway/org.d.ts.map +1 -0
- package/dist/src/gateway/org.js +136 -0
- package/dist/src/gateway/org.js.map +1 -0
- package/dist/src/gateway/server.d.ts +4 -0
- package/dist/src/gateway/server.d.ts.map +1 -0
- package/dist/src/gateway/server.js +764 -0
- package/dist/src/gateway/server.js.map +1 -0
- package/dist/src/gateway/services.d.ts +28 -0
- package/dist/src/gateway/services.d.ts.map +1 -0
- package/dist/src/gateway/services.js +112 -0
- package/dist/src/gateway/services.js.map +1 -0
- package/dist/src/gateway/watcher.d.ts +14 -0
- package/dist/src/gateway/watcher.d.ts.map +1 -0
- package/dist/src/gateway/watcher.js +111 -0
- package/dist/src/gateway/watcher.js.map +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/mcp/gateway-server.d.ts +13 -0
- package/dist/src/mcp/gateway-server.d.ts.map +1 -0
- package/dist/src/mcp/gateway-server.js +376 -0
- package/dist/src/mcp/gateway-server.js.map +1 -0
- package/dist/src/mcp/resolver.d.ts +19 -0
- package/dist/src/mcp/resolver.d.ts.map +1 -0
- package/dist/src/mcp/resolver.js +159 -0
- package/dist/src/mcp/resolver.js.map +1 -0
- package/dist/src/sessions/__tests__/callbacks.test.d.ts +2 -0
- package/dist/src/sessions/__tests__/callbacks.test.d.ts.map +1 -0
- package/dist/src/sessions/__tests__/callbacks.test.js +154 -0
- package/dist/src/sessions/__tests__/callbacks.test.js.map +1 -0
- package/dist/src/sessions/callbacks.d.ts +31 -0
- package/dist/src/sessions/callbacks.d.ts.map +1 -0
- package/dist/src/sessions/callbacks.js +112 -0
- package/dist/src/sessions/callbacks.js.map +1 -0
- package/dist/src/sessions/context.d.ts +36 -0
- package/dist/src/sessions/context.d.ts.map +1 -0
- package/dist/src/sessions/context.js +741 -0
- package/dist/src/sessions/context.js.map +1 -0
- package/dist/src/sessions/fork.d.ts +30 -0
- package/dist/src/sessions/fork.d.ts.map +1 -0
- package/dist/src/sessions/fork.js +178 -0
- package/dist/src/sessions/fork.js.map +1 -0
- package/dist/src/sessions/manager.d.ts +27 -0
- package/dist/src/sessions/manager.d.ts.map +1 -0
- package/dist/src/sessions/manager.js +751 -0
- package/dist/src/sessions/manager.js.map +1 -0
- package/dist/src/sessions/queue.d.ts +36 -0
- package/dist/src/sessions/queue.d.ts.map +1 -0
- package/dist/src/sessions/queue.js +101 -0
- package/dist/src/sessions/queue.js.map +1 -0
- package/dist/src/sessions/queue.test.d.ts +2 -0
- package/dist/src/sessions/queue.test.d.ts.map +1 -0
- package/dist/src/sessions/queue.test.js +28 -0
- package/dist/src/sessions/queue.test.js.map +1 -0
- package/dist/src/sessions/registry.d.ts +116 -0
- package/dist/src/sessions/registry.d.ts.map +1 -0
- package/dist/src/sessions/registry.js +480 -0
- package/dist/src/sessions/registry.js.map +1 -0
- package/dist/src/sessions/registry.test.d.ts +2 -0
- package/dist/src/sessions/registry.test.d.ts.map +1 -0
- package/dist/src/sessions/registry.test.js +41 -0
- package/dist/src/sessions/registry.test.js.map +1 -0
- package/dist/src/shared/__tests__/rateLimit.test.d.ts +2 -0
- package/dist/src/shared/__tests__/rateLimit.test.d.ts.map +1 -0
- package/dist/src/shared/__tests__/rateLimit.test.js +108 -0
- package/dist/src/shared/__tests__/rateLimit.test.js.map +1 -0
- package/dist/src/shared/__tests__/usageAwareness.test.d.ts +2 -0
- package/dist/src/shared/__tests__/usageAwareness.test.d.ts.map +1 -0
- package/dist/src/shared/__tests__/usageAwareness.test.js +58 -0
- package/dist/src/shared/__tests__/usageAwareness.test.js.map +1 -0
- package/dist/src/shared/config.d.ts +3 -0
- package/dist/src/shared/config.d.ts.map +1 -0
- package/dist/src/shared/config.js +11 -0
- package/dist/src/shared/config.js.map +1 -0
- package/dist/src/shared/effort.d.ts +17 -0
- package/dist/src/shared/effort.d.ts.map +1 -0
- package/dist/src/shared/effort.js +44 -0
- package/dist/src/shared/effort.js.map +1 -0
- package/dist/src/shared/logger.d.ts +12 -0
- package/dist/src/shared/logger.d.ts.map +1 -0
- package/dist/src/shared/logger.js +35 -0
- package/dist/src/shared/logger.js.map +1 -0
- package/dist/src/shared/paths.d.ts +31 -0
- package/dist/src/shared/paths.d.ts.map +1 -0
- package/dist/src/shared/paths.js +92 -0
- package/dist/src/shared/paths.js.map +1 -0
- package/dist/src/shared/rateLimit.d.ts +20 -0
- package/dist/src/shared/rateLimit.d.ts.map +1 -0
- package/dist/src/shared/rateLimit.js +61 -0
- package/dist/src/shared/rateLimit.js.map +1 -0
- package/dist/src/shared/types.d.ts +448 -0
- package/dist/src/shared/types.d.ts.map +1 -0
- package/dist/src/shared/types.js +4 -0
- package/dist/src/shared/types.js.map +1 -0
- package/dist/src/shared/usageAwareness.d.ts +10 -0
- package/dist/src/shared/usageAwareness.d.ts.map +1 -0
- package/dist/src/shared/usageAwareness.js +69 -0
- package/dist/src/shared/usageAwareness.js.map +1 -0
- package/dist/src/shared/version.d.ts +15 -0
- package/dist/src/shared/version.d.ts.map +1 -0
- package/dist/src/shared/version.js +56 -0
- package/dist/src/shared/version.js.map +1 -0
- package/dist/src/stt/stt.d.ts +24 -0
- package/dist/src/stt/stt.d.ts.map +1 -0
- package/dist/src/stt/stt.js +203 -0
- package/dist/src/stt/stt.js.map +1 -0
- package/dist/web/404.html +1 -0
- package/dist/web/__next.__PAGE__.txt +9 -0
- package/dist/web/__next._full.txt +19 -0
- package/dist/web/__next._head.txt +6 -0
- package/dist/web/__next._index.txt +6 -0
- package/dist/web/__next._tree.txt +2 -0
- package/dist/web/_next/static/chunks/00yn.jp4tr9l6.js +62 -0
- package/dist/web/_next/static/chunks/0398g9fy2i2l..js +5 -0
- package/dist/web/_next/static/chunks/03~yq9q893hmn.js +1 -0
- package/dist/web/_next/static/chunks/07t-qdutaizlf.js +5 -0
- package/dist/web/_next/static/chunks/0_pxvi.u4tzw1.js +5 -0
- package/dist/web/_next/static/chunks/0dtbl8648b3u9.js +7 -0
- package/dist/web/_next/static/chunks/0e3pdjkorr5zl.js +2 -0
- package/dist/web/_next/static/chunks/0ewpxy-mzj3j3.js +1 -0
- package/dist/web/_next/static/chunks/0ihbb7~gq42g~.js +5 -0
- package/dist/web/_next/static/chunks/0j4uz-g9x1_~s.js +1 -0
- package/dist/web/_next/static/chunks/0mlo32yrc7ziu.css +1 -0
- package/dist/web/_next/static/chunks/0ru56x83x4rkk.js +1 -0
- package/dist/web/_next/static/chunks/0svvev77pb2gu.js +1 -0
- package/dist/web/_next/static/chunks/0tf9kpygstdls.js +1 -0
- package/dist/web/_next/static/chunks/0wzpea61sn2-k.js +5 -0
- package/dist/web/_next/static/chunks/0y3dnnq~kxyz_.js +1 -0
- package/dist/web/_next/static/chunks/0y~qqhmf2o7pt.js +1 -0
- package/dist/web/_next/static/chunks/0~6m1p5hnsx2f.js +1 -0
- package/dist/web/_next/static/chunks/10-liamt-ipcu.js +1 -0
- package/dist/web/_next/static/chunks/10l08hq.acixl.js +5 -0
- package/dist/web/_next/static/chunks/11rbefjn.e-3-.js +1 -0
- package/dist/web/_next/static/chunks/11rodb4s1x4of.js +1 -0
- package/dist/web/_next/static/chunks/12_uxtnrqhoo1.js +1 -0
- package/dist/web/_next/static/chunks/12h4dws.tz7yb.js +5 -0
- package/dist/web/_next/static/chunks/13juizw904g.e.js +1 -0
- package/dist/web/_next/static/chunks/14ge_q22f0n.f.js +1 -0
- package/dist/web/_next/static/chunks/16b0jvgccyt2i.js +5 -0
- package/dist/web/_next/static/chunks/17-u9c_weg12-.js +31 -0
- package/dist/web/_next/static/chunks/turbopack-0dt987bw_1nyf.js +1 -0
- package/dist/web/_next/static/media/apple-icon.0.wpl4.d-5bkf.jpg +0 -0
- package/dist/web/_next/static/media/icon.0.wpl4.d-5bkf.jpg +0 -0
- package/dist/web/_next/static/uwjABwQIFELOHhVlstvOe/_buildManifest.js +11 -0
- package/dist/web/_next/static/uwjABwQIFELOHhVlstvOe/_clientMiddlewareManifest.js +1 -0
- package/dist/web/_next/static/uwjABwQIFELOHhVlstvOe/_ssgManifest.js +1 -0
- package/dist/web/_not-found/__next._full.txt +17 -0
- package/dist/web/_not-found/__next._head.txt +6 -0
- package/dist/web/_not-found/__next._index.txt +6 -0
- package/dist/web/_not-found/__next._not-found.__PAGE__.txt +5 -0
- package/dist/web/_not-found/__next._not-found.txt +5 -0
- package/dist/web/_not-found/__next._tree.txt +2 -0
- package/dist/web/_not-found.html +1 -0
- package/dist/web/_not-found.txt +17 -0
- package/dist/web/apple-icon.jpg +0 -0
- package/dist/web/chat/__next._full.txt +21 -0
- package/dist/web/chat/__next._head.txt +6 -0
- package/dist/web/chat/__next._index.txt +6 -0
- package/dist/web/chat/__next._tree.txt +2 -0
- package/dist/web/chat/__next.chat.__PAGE__.txt +9 -0
- package/dist/web/chat/__next.chat.txt +5 -0
- package/dist/web/chat.html +1 -0
- package/dist/web/chat.txt +21 -0
- package/dist/web/cron/__next._full.txt +21 -0
- package/dist/web/cron/__next._head.txt +6 -0
- package/dist/web/cron/__next._index.txt +6 -0
- package/dist/web/cron/__next._tree.txt +2 -0
- package/dist/web/cron/__next.cron.__PAGE__.txt +9 -0
- package/dist/web/cron/__next.cron.txt +5 -0
- package/dist/web/cron.html +1 -0
- package/dist/web/cron.txt +21 -0
- package/dist/web/icon.jpg +0 -0
- package/dist/web/index.html +1 -0
- package/dist/web/index.txt +19 -0
- package/dist/web/kanban/__next._full.txt +21 -0
- package/dist/web/kanban/__next._head.txt +6 -0
- package/dist/web/kanban/__next._index.txt +6 -0
- package/dist/web/kanban/__next._tree.txt +2 -0
- package/dist/web/kanban/__next.kanban.__PAGE__.txt +9 -0
- package/dist/web/kanban/__next.kanban.txt +5 -0
- package/dist/web/kanban.html +1 -0
- package/dist/web/kanban.txt +21 -0
- package/dist/web/logs/__next._full.txt +21 -0
- package/dist/web/logs/__next._head.txt +6 -0
- package/dist/web/logs/__next._index.txt +6 -0
- package/dist/web/logs/__next._tree.txt +2 -0
- package/dist/web/logs/__next.logs.__PAGE__.txt +9 -0
- package/dist/web/logs/__next.logs.txt +5 -0
- package/dist/web/logs.html +7 -0
- package/dist/web/logs.txt +21 -0
- package/dist/web/org/__next._full.txt +21 -0
- package/dist/web/org/__next._head.txt +6 -0
- package/dist/web/org/__next._index.txt +6 -0
- package/dist/web/org/__next._tree.txt +2 -0
- package/dist/web/org/__next.org.__PAGE__.txt +9 -0
- package/dist/web/org/__next.org.txt +5 -0
- package/dist/web/org.html +1 -0
- package/dist/web/org.txt +21 -0
- package/dist/web/sessions/__next._full.txt +18 -0
- package/dist/web/sessions/__next._head.txt +6 -0
- package/dist/web/sessions/__next._index.txt +6 -0
- package/dist/web/sessions/__next._tree.txt +2 -0
- package/dist/web/sessions/__next.sessions.__PAGE__.txt +6 -0
- package/dist/web/sessions/__next.sessions.txt +5 -0
- package/dist/web/sessions.html +1 -0
- package/dist/web/sessions.txt +18 -0
- package/dist/web/settings/__next._full.txt +21 -0
- package/dist/web/settings/__next._head.txt +6 -0
- package/dist/web/settings/__next._index.txt +6 -0
- package/dist/web/settings/__next._tree.txt +2 -0
- package/dist/web/settings/__next.settings.__PAGE__.txt +9 -0
- package/dist/web/settings/__next.settings.txt +5 -0
- package/dist/web/settings.html +1 -0
- package/dist/web/settings.txt +21 -0
- package/dist/web/skills/__next._full.txt +21 -0
- package/dist/web/skills/__next._head.txt +6 -0
- package/dist/web/skills/__next._index.txt +6 -0
- package/dist/web/skills/__next._tree.txt +2 -0
- package/dist/web/skills/__next.skills.__PAGE__.txt +9 -0
- package/dist/web/skills/__next.skills.txt +5 -0
- package/dist/web/skills.html +1 -0
- package/dist/web/skills.txt +21 -0
- package/package.json +54 -0
- package/template/AGENTS.md +285 -0
- package/template/CLAUDE.md +285 -0
- package/template/config.default.yaml +71 -0
- package/template/docs/architecture.md +74 -0
- package/template/docs/connectors.md +72 -0
- package/template/docs/cron.md +137 -0
- package/template/docs/mcp.md +108 -0
- package/template/docs/org.md +105 -0
- package/template/docs/overview.md +39 -0
- package/template/docs/self-modification.md +65 -0
- package/template/docs/skills.md +58 -0
- package/template/knowledge/.gitkeep +0 -0
- package/template/migrations/.gitkeep +0 -0
- package/template/migrations/0.1.0/MIGRATION.md +25 -0
- package/template/migrations/0.2.0/MIGRATION.md +41 -0
- package/template/migrations/0.2.0/files/config.default.yaml +30 -0
- package/template/migrations/0.3.0/MIGRATION.md +57 -0
- package/template/migrations/0.3.0/files/config.default.yaml +62 -0
- package/template/migrations/0.3.0/files/docs/mcp.md +108 -0
- package/template/migrations/0.7.3/MIGRATION.md +63 -0
- package/template/migrations/0.7.3/files/AGENTS.md +188 -0
- package/template/migrations/0.7.3/files/CLAUDE.md +127 -0
- package/template/migrations/0.7.5/MIGRATION.md +78 -0
- package/template/migrations/0.8.0/MIGRATION.md +73 -0
- package/template/migrations/0.9.0/MIGRATION.md +82 -0
- package/template/skills/cron-manager/SKILL.md +127 -0
- package/template/skills/find-and-install/SKILL.md +92 -0
- package/template/skills/management/SKILL.md +226 -0
- package/template/skills/migrate/SKILL.md +154 -0
- package/template/skills/new/SKILL.md +19 -0
- package/template/skills/onboarding/SKILL.md +106 -0
- package/template/skills/self-heal/SKILL.md +114 -0
- package/template/skills/skill-creator/SKILL.md +112 -0
- package/template/skills/status/SKILL.md +19 -0
- package/template/skills/sync/SKILL.md +67 -0
- package/template/skills.json +3 -0
|
@@ -0,0 +1,751 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { accumulateSessionCost, createSession, deleteSession, getSessionBySessionKey, getMessages, insertMessage, updateSession, } from "./registry.js";
|
|
3
|
+
import { notifyParentSession, notifyRateLimited, notifyRateLimitResumed, notifyDiscordChannel } from "./callbacks.js";
|
|
4
|
+
import { buildContext } from "./context.js";
|
|
5
|
+
import { SessionQueue } from "./queue.js";
|
|
6
|
+
import { JINN_HOME } from "../shared/paths.js";
|
|
7
|
+
import { logger } from "../shared/logger.js";
|
|
8
|
+
import { resolveEffort } from "../shared/effort.js";
|
|
9
|
+
import { computeNextRetryDelayMs, computeRateLimitDeadlineMs, detectRateLimit, isDeadSessionError } from "../shared/rateLimit.js";
|
|
10
|
+
import { getClaudeExpectedResetAt, isLikelyNearClaudeUsageLimit, recordClaudeRateLimit } from "../shared/usageAwareness.js";
|
|
11
|
+
import { loadJobs } from "../cron/jobs.js";
|
|
12
|
+
import { setCronJobEnabled, triggerCronJob } from "../cron/scheduler.js";
|
|
13
|
+
import { checkBudget } from "../gateway/budgets.js";
|
|
14
|
+
import { resolveMcpServers, writeMcpConfigFile, cleanupMcpConfigFile } from "../mcp/resolver.js";
|
|
15
|
+
function maybeRevertEngineOverride(session) {
|
|
16
|
+
const meta = (session.transportMeta || {});
|
|
17
|
+
const override = meta["engineOverride"];
|
|
18
|
+
if (!override)
|
|
19
|
+
return session;
|
|
20
|
+
const originalEngine = typeof override.originalEngine === "string" ? override.originalEngine : null;
|
|
21
|
+
const originalEngineSessionId = typeof override.originalEngineSessionId === "string"
|
|
22
|
+
? override.originalEngineSessionId
|
|
23
|
+
: null;
|
|
24
|
+
const syncSince = typeof override.syncSince === "string" ? override.syncSince : null;
|
|
25
|
+
const untilIso = typeof override.until === "string" ? override.until : null;
|
|
26
|
+
if (!originalEngine || !untilIso)
|
|
27
|
+
return session;
|
|
28
|
+
const until = new Date(untilIso);
|
|
29
|
+
if (Number.isNaN(until.getTime()))
|
|
30
|
+
return session;
|
|
31
|
+
if (until.getTime() > Date.now())
|
|
32
|
+
return session;
|
|
33
|
+
const engineSessionsRaw = meta["engineSessions"];
|
|
34
|
+
const engineSessions = (engineSessionsRaw && typeof engineSessionsRaw === "object" && !Array.isArray(engineSessionsRaw))
|
|
35
|
+
? { ...engineSessionsRaw }
|
|
36
|
+
: {};
|
|
37
|
+
// Preserve the current engine session ID under its engine key
|
|
38
|
+
if (session.engine && session.engineSessionId) {
|
|
39
|
+
engineSessions[String(session.engine)] = session.engineSessionId;
|
|
40
|
+
}
|
|
41
|
+
const restoredSessionId = originalEngineSessionId
|
|
42
|
+
?? (typeof engineSessions[originalEngine] === "string" ? engineSessions[originalEngine] : null);
|
|
43
|
+
const nextMeta = { ...meta, engineSessions };
|
|
44
|
+
if (originalEngine === "claude" && syncSince && session.engine !== "claude") {
|
|
45
|
+
nextMeta["claudeSyncSince"] = syncSince;
|
|
46
|
+
}
|
|
47
|
+
delete nextMeta["engineOverride"];
|
|
48
|
+
return updateSession(session.id, {
|
|
49
|
+
engine: originalEngine,
|
|
50
|
+
engineSessionId: restoredSessionId,
|
|
51
|
+
transportMeta: nextMeta,
|
|
52
|
+
lastError: null,
|
|
53
|
+
}) ?? session;
|
|
54
|
+
}
|
|
55
|
+
function mergeTransportMeta(existing, incoming) {
|
|
56
|
+
const baseExisting = (existing && typeof existing === "object" && !Array.isArray(existing))
|
|
57
|
+
? existing
|
|
58
|
+
: {};
|
|
59
|
+
const baseIncoming = (incoming && typeof incoming === "object" && !Array.isArray(incoming))
|
|
60
|
+
? incoming
|
|
61
|
+
: {};
|
|
62
|
+
const merged = { ...baseExisting, ...baseIncoming };
|
|
63
|
+
// Preserve Jinn internal keys from being overwritten by transport adapters.
|
|
64
|
+
for (const key of ["engineOverride", "engineSessions", "claudeSyncSince"]) {
|
|
65
|
+
if (baseExisting[key] !== undefined)
|
|
66
|
+
merged[key] = baseExisting[key];
|
|
67
|
+
}
|
|
68
|
+
return merged;
|
|
69
|
+
}
|
|
70
|
+
export class SessionManager {
|
|
71
|
+
config;
|
|
72
|
+
engines;
|
|
73
|
+
connectorNames;
|
|
74
|
+
queue = new SessionQueue();
|
|
75
|
+
connectorProvider = () => new Map();
|
|
76
|
+
constructor(config, engines, connectorNames = []) {
|
|
77
|
+
this.config = config;
|
|
78
|
+
this.engines = engines;
|
|
79
|
+
this.connectorNames = connectorNames;
|
|
80
|
+
}
|
|
81
|
+
setConnectorProvider(provider) {
|
|
82
|
+
this.connectorProvider = provider;
|
|
83
|
+
}
|
|
84
|
+
getEngine(name) {
|
|
85
|
+
return this.engines.get(name);
|
|
86
|
+
}
|
|
87
|
+
getQueue() {
|
|
88
|
+
return this.queue;
|
|
89
|
+
}
|
|
90
|
+
async route(msg, connector, opts = {}) {
|
|
91
|
+
if (await this.handleCommand(msg, connector))
|
|
92
|
+
return;
|
|
93
|
+
let session = getSessionBySessionKey(msg.sessionKey);
|
|
94
|
+
if (!session) {
|
|
95
|
+
session = createSession({
|
|
96
|
+
engine: opts.engine ?? opts.employee?.engine ?? this.config.engines.default,
|
|
97
|
+
source: msg.source,
|
|
98
|
+
sourceRef: msg.sessionKey,
|
|
99
|
+
connector: msg.connector,
|
|
100
|
+
sessionKey: msg.sessionKey,
|
|
101
|
+
replyContext: msg.replyContext,
|
|
102
|
+
messageId: msg.messageId,
|
|
103
|
+
transportMeta: msg.transportMeta,
|
|
104
|
+
employee: opts.employee?.name ?? undefined,
|
|
105
|
+
model: opts.model ?? opts.employee?.model ?? undefined,
|
|
106
|
+
title: opts.title,
|
|
107
|
+
prompt: msg.text,
|
|
108
|
+
portalName: this.config.portal?.portalName,
|
|
109
|
+
});
|
|
110
|
+
logger.info(`Created new session ${session.id} for ${msg.sessionKey}` +
|
|
111
|
+
(opts.employee ? ` (employee: ${opts.employee.name})` : ""));
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const mergedMeta = mergeTransportMeta(session.transportMeta, msg.transportMeta);
|
|
115
|
+
session = updateSession(session.id, {
|
|
116
|
+
replyContext: msg.replyContext,
|
|
117
|
+
messageId: msg.messageId ?? null,
|
|
118
|
+
transportMeta: mergedMeta,
|
|
119
|
+
...(opts.model ? { model: opts.model } : {}),
|
|
120
|
+
}) ?? session;
|
|
121
|
+
}
|
|
122
|
+
session = maybeRevertEngineOverride(session);
|
|
123
|
+
const target = connector.reconstructTarget(msg.replyContext);
|
|
124
|
+
target.messageTs ??= msg.messageId;
|
|
125
|
+
const attachmentPaths = msg.attachments
|
|
126
|
+
.map((attachment) => attachment.localPath)
|
|
127
|
+
.filter((filePath) => !!filePath);
|
|
128
|
+
if (session.status === "waiting") {
|
|
129
|
+
const expectedResetAt = getClaudeExpectedResetAt();
|
|
130
|
+
const resumeText = expectedResetAt
|
|
131
|
+
? expectedResetAt.toLocaleString("en-GB", { weekday: "short", day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" })
|
|
132
|
+
: null;
|
|
133
|
+
await connector.replyMessage(target, `⏳ Still paused due to Claude usage limit${resumeText ? ` (resets ${resumeText})` : ""}. I queued this message and will respond automatically.`).catch(() => { });
|
|
134
|
+
}
|
|
135
|
+
if (session.status === "running" && this.queue.isRunning(msg.sessionKey) && connector.getCapabilities().reactions) {
|
|
136
|
+
await connector.addReaction(target, "clock1").catch(() => { });
|
|
137
|
+
}
|
|
138
|
+
const sessionId = session.id;
|
|
139
|
+
await this.queue.enqueue(msg.sessionKey, () => this.runSession(session, msg, attachmentPaths, connector, target, opts.employee));
|
|
140
|
+
return { sessionId };
|
|
141
|
+
}
|
|
142
|
+
async runSession(session, msg, attachments, connector, target, employee) {
|
|
143
|
+
const engine = this.engines.get(session.engine);
|
|
144
|
+
if (!engine) {
|
|
145
|
+
logger.error(`Engine "${session.engine}" not found for session ${session.id}`);
|
|
146
|
+
await connector.replyMessage(target, `Error: engine "${session.engine}" not available.`);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
insertMessage(session.id, "user", msg.text);
|
|
150
|
+
const capabilities = connector.getCapabilities();
|
|
151
|
+
const decorateMessages = session.source !== "cron";
|
|
152
|
+
if (decorateMessages && capabilities.reactions) {
|
|
153
|
+
await connector.addReaction(target, "eyes").catch(() => { });
|
|
154
|
+
}
|
|
155
|
+
// Set native typing indicator (Slack assistant.threads.setStatus)
|
|
156
|
+
const threadTs = target.thread || target.messageTs;
|
|
157
|
+
if (decorateMessages && connector.setTypingStatus) {
|
|
158
|
+
await connector.setTypingStatus(target.channel, threadTs, "is thinking...").catch(() => { });
|
|
159
|
+
}
|
|
160
|
+
updateSession(session.id, {
|
|
161
|
+
status: "running",
|
|
162
|
+
replyContext: msg.replyContext,
|
|
163
|
+
messageId: msg.messageId ?? null,
|
|
164
|
+
transportMeta: mergeTransportMeta(session.transportMeta, msg.transportMeta),
|
|
165
|
+
lastActivity: new Date().toISOString(),
|
|
166
|
+
});
|
|
167
|
+
// Resolve MCP config before try block so it's accessible in catch for cleanup
|
|
168
|
+
let mcpConfigPath;
|
|
169
|
+
let hierarchy;
|
|
170
|
+
try {
|
|
171
|
+
const { scanOrg } = await import("../gateway/org.js");
|
|
172
|
+
const { resolveOrgHierarchy } = await import("../gateway/org-hierarchy.js");
|
|
173
|
+
hierarchy = resolveOrgHierarchy(scanOrg());
|
|
174
|
+
}
|
|
175
|
+
catch { /* fallback to filesystem scan in context builder */ }
|
|
176
|
+
try {
|
|
177
|
+
const meta = msg.transportMeta ?? {};
|
|
178
|
+
const systemPrompt = buildContext({
|
|
179
|
+
source: session.source,
|
|
180
|
+
channel: msg.channel,
|
|
181
|
+
thread: msg.thread,
|
|
182
|
+
user: msg.user,
|
|
183
|
+
employee,
|
|
184
|
+
connectors: this.connectorNames,
|
|
185
|
+
config: this.config,
|
|
186
|
+
sessionId: session.id,
|
|
187
|
+
channelName: meta.channelName || undefined,
|
|
188
|
+
speakerName: meta.speakerName || undefined,
|
|
189
|
+
speakerRealName: meta.speakerRealName || undefined,
|
|
190
|
+
speakerDisplayName: meta.speakerDisplayName || undefined,
|
|
191
|
+
speakerHandle: meta.speakerHandle || undefined,
|
|
192
|
+
speakerSlackId: meta.speakerSlackId || undefined,
|
|
193
|
+
speakerIsBot: meta.speakerIsBot ?? undefined,
|
|
194
|
+
speakerTz: meta.speakerTz || undefined,
|
|
195
|
+
hierarchy,
|
|
196
|
+
});
|
|
197
|
+
const engineConfig = session.engine === "codex"
|
|
198
|
+
? this.config.engines.codex
|
|
199
|
+
: session.engine === "gemini"
|
|
200
|
+
? this.config.engines.gemini ?? this.config.engines.claude
|
|
201
|
+
: this.config.engines.claude;
|
|
202
|
+
if (session.engine === "claude") {
|
|
203
|
+
const mcpConfig = resolveMcpServers(this.config.mcp, employee);
|
|
204
|
+
if (Object.keys(mcpConfig.mcpServers).length > 0) {
|
|
205
|
+
mcpConfigPath = writeMcpConfigFile(mcpConfig, session.id);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const effortLevel = resolveEffort(engineConfig, session, employee);
|
|
209
|
+
// If we previously switched to GPT while Claude was rate-limited, inject a sync transcript
|
|
210
|
+
// so Claude can resume with full context when it comes back online.
|
|
211
|
+
const syncSinceIso = session.transportMeta?.claudeSyncSince;
|
|
212
|
+
let promptToRun = msg.text;
|
|
213
|
+
const syncSinceMs = typeof syncSinceIso === "string" ? new Date(syncSinceIso).getTime() : NaN;
|
|
214
|
+
const syncRequested = session.engine === "claude" && typeof syncSinceIso === "string" && Number.isFinite(syncSinceMs);
|
|
215
|
+
if (syncRequested) {
|
|
216
|
+
const sinceMessages = getMessages(session.id)
|
|
217
|
+
.filter((m) => (m.role === "user" || m.role === "assistant") && m.timestamp >= syncSinceMs)
|
|
218
|
+
.map((m) => `${m.role.toUpperCase()}: ${m.content}`);
|
|
219
|
+
const transcript = sinceMessages.slice(-20).join("\n\n");
|
|
220
|
+
promptToRun =
|
|
221
|
+
`We temporarily switched to GPT due to a Claude usage limit. Sync your context with this transcript (most recent last), then respond to the last USER message.\n\n${transcript}`;
|
|
222
|
+
}
|
|
223
|
+
// Budget enforcement — check BEFORE engine.run()
|
|
224
|
+
if (session.employee) {
|
|
225
|
+
const budgetConfig = this.config.budgets?.employees;
|
|
226
|
+
if (budgetConfig && session.employee in budgetConfig) {
|
|
227
|
+
const budgetStatus = checkBudget(session.employee, budgetConfig);
|
|
228
|
+
if (budgetStatus === 'paused') {
|
|
229
|
+
logger.warn(`Session ${session.id} blocked: employee "${session.employee}" has exceeded their budget`);
|
|
230
|
+
const pausedMsg = `Budget limit exceeded for employee "${session.employee}". Session blocked.`;
|
|
231
|
+
updateSession(session.id, {
|
|
232
|
+
status: 'error',
|
|
233
|
+
lastActivity: new Date().toISOString(),
|
|
234
|
+
lastError: pausedMsg,
|
|
235
|
+
});
|
|
236
|
+
if (decorateMessages && connector.setTypingStatus) {
|
|
237
|
+
await connector.setTypingStatus(target.channel, threadTs, '').catch(() => { });
|
|
238
|
+
}
|
|
239
|
+
await connector.replyMessage(target, `⛔ ${pausedMsg}`).catch(() => { });
|
|
240
|
+
if (decorateMessages && capabilities.reactions) {
|
|
241
|
+
await connector.removeReaction(target, 'eyes').catch(() => { });
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// Heuristic preflight warning: Claude usage limits don't expose a precise "remaining" budget.
|
|
248
|
+
// If we've hit the limit recently and this looks like a heavy turn, warn before we spend time.
|
|
249
|
+
if (decorateMessages && session.engine === "claude" && isLikelyNearClaudeUsageLimit()) {
|
|
250
|
+
const modelName = (session.model ?? engineConfig.model ?? "").toLowerCase();
|
|
251
|
+
const heavyEffort = ["high", "xhigh", "max"].includes((effortLevel || "").toLowerCase());
|
|
252
|
+
const heavyModel = modelName.includes("opus");
|
|
253
|
+
const looksBig = attachments.length > 0 || msg.text.length > 6000;
|
|
254
|
+
if ((heavyEffort || heavyModel) && looksBig) {
|
|
255
|
+
const expectedResetAt = getClaudeExpectedResetAt();
|
|
256
|
+
const resumeText = expectedResetAt
|
|
257
|
+
? expectedResetAt.toLocaleString("en-GB", { weekday: "short", day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" })
|
|
258
|
+
: null;
|
|
259
|
+
await connector.replyMessage(target, `⚠️ Heads up: Claude usage limits were hit recently, and this looks like a bigger task. If you're near the limit, it may pause${resumeText ? ` until ~${resumeText}` : ""}.`).catch(() => { });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
let result = await engine.run({
|
|
263
|
+
prompt: promptToRun,
|
|
264
|
+
resumeSessionId: session.engineSessionId ?? undefined,
|
|
265
|
+
systemPrompt,
|
|
266
|
+
cwd: JINN_HOME,
|
|
267
|
+
bin: engineConfig.bin,
|
|
268
|
+
model: session.model ?? engineConfig.model,
|
|
269
|
+
effortLevel,
|
|
270
|
+
cliFlags: employee?.cliFlags,
|
|
271
|
+
mcpConfigPath,
|
|
272
|
+
attachments: attachments.length > 0 ? attachments : undefined,
|
|
273
|
+
sessionId: session.id,
|
|
274
|
+
});
|
|
275
|
+
let wasInterrupted = result.error?.startsWith("Interrupted");
|
|
276
|
+
// Dead session detection: if the engine session ID is stale (expired/invalid),
|
|
277
|
+
// clear cached engine sessions from transportMeta so the next attempt starts fresh.
|
|
278
|
+
// Also sets a flag so we skip the rate-limit retry loop below (a dead session
|
|
279
|
+
// error can contain text like "429" that would otherwise match RATE_LIMIT_ERROR_RE).
|
|
280
|
+
let isDead = !wasInterrupted && isDeadSessionError(result);
|
|
281
|
+
if (isDead) {
|
|
282
|
+
logger.warn(`Dead session detected for ${session.id} — clearing stale engine IDs`);
|
|
283
|
+
const meta = { ...(session.transportMeta || {}) };
|
|
284
|
+
delete meta["engineSessions"];
|
|
285
|
+
delete meta["engineOverride"];
|
|
286
|
+
updateSession(session.id, {
|
|
287
|
+
engineSessionId: null,
|
|
288
|
+
transportMeta: meta,
|
|
289
|
+
});
|
|
290
|
+
// Update local reference so subsequent code doesn't re-read stale IDs
|
|
291
|
+
session = { ...session, engineSessionId: null, transportMeta: meta };
|
|
292
|
+
// Auto-retry once with a fresh engine session. Without this, a user
|
|
293
|
+
// message that happens to land on a stale resume ID is silently lost
|
|
294
|
+
// (the raw engine error propagates back instead of a real answer).
|
|
295
|
+
logger.info(`Retrying session ${session.id} with fresh engine session after dead-session`);
|
|
296
|
+
result = await engine.run({
|
|
297
|
+
prompt: promptToRun,
|
|
298
|
+
resumeSessionId: undefined,
|
|
299
|
+
systemPrompt,
|
|
300
|
+
cwd: JINN_HOME,
|
|
301
|
+
bin: engineConfig.bin,
|
|
302
|
+
model: session.model ?? engineConfig.model,
|
|
303
|
+
effortLevel,
|
|
304
|
+
cliFlags: employee?.cliFlags,
|
|
305
|
+
mcpConfigPath,
|
|
306
|
+
attachments: attachments.length > 0 ? attachments : undefined,
|
|
307
|
+
sessionId: session.id,
|
|
308
|
+
});
|
|
309
|
+
// Re-evaluate the flags against the retry result. If the retry also
|
|
310
|
+
// comes back dead, something deeper is wrong — log and fall through to
|
|
311
|
+
// normal error handling (which will post the error to the user).
|
|
312
|
+
wasInterrupted = result.error?.startsWith("Interrupted");
|
|
313
|
+
isDead = !wasInterrupted && isDeadSessionError(result);
|
|
314
|
+
if (isDead) {
|
|
315
|
+
logger.error(`Retry with fresh session for ${session.id} also reported dead-session; giving up`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Detect rate limit / usage limit errors and auto-retry.
|
|
319
|
+
// Skip entirely for dead sessions — they are not rate limits.
|
|
320
|
+
const rateLimit = (!wasInterrupted && !isDead) ? detectRateLimit(result) : { limited: false };
|
|
321
|
+
if (rateLimit.limited) {
|
|
322
|
+
recordClaudeRateLimit(rateLimit.resetsAt);
|
|
323
|
+
const strategy = this.config.sessions?.rateLimitStrategy ?? "fallback";
|
|
324
|
+
// Optional fallback: switch to GPT (Codex) while Claude resets
|
|
325
|
+
if (session.engine === "claude" && strategy === "fallback") {
|
|
326
|
+
const fallbackName = this.config.sessions?.fallbackEngine ?? "codex";
|
|
327
|
+
const fallbackEngine = this.engines.get(fallbackName);
|
|
328
|
+
if (fallbackEngine) {
|
|
329
|
+
const { resumeAt } = computeNextRetryDelayMs(rateLimit.resetsAt);
|
|
330
|
+
const until = resumeAt ?? new Date(Date.now() + 6 * 60 * 60_000);
|
|
331
|
+
const syncSince = new Date().toISOString();
|
|
332
|
+
const resumeText = resumeAt
|
|
333
|
+
? resumeAt.toLocaleString("en-GB", { weekday: "short", day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" })
|
|
334
|
+
: null;
|
|
335
|
+
notifyDiscordChannel(`⚠️ Claude usage limit reached. Session ${session.id}${session.employee ? ` (${session.employee})` : ""} switching to GPT.`);
|
|
336
|
+
await connector.replyMessage(target, `⚠️ Claude usage limit reached${resumeText ? `. Resets ${resumeText}` : ""}. Switching to GPT for now.`).catch(() => { });
|
|
337
|
+
const nextMeta = { ...(session.transportMeta || {}) };
|
|
338
|
+
const engineSessionsRaw = nextMeta.engineSessions;
|
|
339
|
+
const engineSessions = (engineSessionsRaw && typeof engineSessionsRaw === "object" && !Array.isArray(engineSessionsRaw))
|
|
340
|
+
? { ...engineSessionsRaw }
|
|
341
|
+
: {};
|
|
342
|
+
if (session.engineSessionId) {
|
|
343
|
+
engineSessions.claude = session.engineSessionId;
|
|
344
|
+
}
|
|
345
|
+
nextMeta.engineSessions = engineSessions;
|
|
346
|
+
nextMeta.engineOverride = { originalEngine: "claude", originalEngineSessionId: session.engineSessionId, until: until.toISOString(), syncSince };
|
|
347
|
+
updateSession(session.id, {
|
|
348
|
+
engine: fallbackName,
|
|
349
|
+
// Keep Claude engine_session_id intact for later restore; Codex will return its own thread id.
|
|
350
|
+
transportMeta: nextMeta,
|
|
351
|
+
status: "running",
|
|
352
|
+
lastActivity: new Date().toISOString(),
|
|
353
|
+
lastError: resumeAt
|
|
354
|
+
? `Claude usage limit — using GPT until ${resumeAt.toISOString()}`
|
|
355
|
+
: "Claude usage limit — using GPT temporarily",
|
|
356
|
+
});
|
|
357
|
+
const fallbackConfig = this.config.engines.codex;
|
|
358
|
+
const fallbackEffort = resolveEffort(fallbackConfig, session, employee);
|
|
359
|
+
const codexResume = typeof engineSessions.codex === "string" ? engineSessions.codex : undefined;
|
|
360
|
+
const history = getMessages(session.id)
|
|
361
|
+
.filter((m) => m.role === "user" || m.role === "assistant")
|
|
362
|
+
.map((m) => `${m.role.toUpperCase()}: ${m.content}`);
|
|
363
|
+
const historyText = history.slice(-12).join("\n\n");
|
|
364
|
+
const fallbackPrompt = codexResume
|
|
365
|
+
? msg.text
|
|
366
|
+
: `Continue this conversation and respond to the last USER message.\n\nConversation so far:\n\n${historyText}`;
|
|
367
|
+
const fallbackResult = await fallbackEngine.run({
|
|
368
|
+
prompt: fallbackPrompt,
|
|
369
|
+
resumeSessionId: codexResume,
|
|
370
|
+
systemPrompt,
|
|
371
|
+
cwd: JINN_HOME,
|
|
372
|
+
bin: fallbackConfig.bin,
|
|
373
|
+
model: session.model ?? fallbackConfig.model,
|
|
374
|
+
effortLevel: fallbackEffort,
|
|
375
|
+
cliFlags: employee?.cliFlags,
|
|
376
|
+
attachments: attachments.length > 0 ? attachments : undefined,
|
|
377
|
+
sessionId: session.id,
|
|
378
|
+
});
|
|
379
|
+
const fallbackText = fallbackResult.result?.trim()
|
|
380
|
+
? fallbackResult.result
|
|
381
|
+
: fallbackResult.error || "(No response from engine)";
|
|
382
|
+
insertMessage(session.id, "assistant", fallbackText);
|
|
383
|
+
if (fallbackResult.cost || fallbackResult.numTurns) {
|
|
384
|
+
accumulateSessionCost(session.id, fallbackResult.cost ?? 0, fallbackResult.numTurns ?? 1);
|
|
385
|
+
}
|
|
386
|
+
// Persist Codex thread id so future fallbacks can resume it
|
|
387
|
+
const nextEngineSessions = { ...engineSessions };
|
|
388
|
+
if (fallbackResult.sessionId) {
|
|
389
|
+
nextEngineSessions.codex = fallbackResult.sessionId;
|
|
390
|
+
}
|
|
391
|
+
const metaAfter = { ...(getSessionBySessionKey(msg.sessionKey)?.transportMeta || nextMeta) };
|
|
392
|
+
metaAfter.engineSessions = nextEngineSessions;
|
|
393
|
+
updateSession(session.id, { transportMeta: metaAfter });
|
|
394
|
+
if (decorateMessages && connector.setTypingStatus) {
|
|
395
|
+
await connector.setTypingStatus(target.channel, threadTs, "").catch(() => { });
|
|
396
|
+
}
|
|
397
|
+
await connector.replyMessage(target, fallbackText).catch(() => { });
|
|
398
|
+
if (decorateMessages && capabilities.reactions) {
|
|
399
|
+
await connector.removeReaction(target, "eyes").catch(() => { });
|
|
400
|
+
}
|
|
401
|
+
const updated = updateSession(session.id, {
|
|
402
|
+
engineSessionId: fallbackResult.sessionId,
|
|
403
|
+
status: fallbackResult.error ? "error" : "idle",
|
|
404
|
+
replyContext: msg.replyContext,
|
|
405
|
+
messageId: msg.messageId ?? null,
|
|
406
|
+
transportMeta: mergeTransportMeta(getSessionBySessionKey(msg.sessionKey)?.transportMeta ?? session.transportMeta, msg.transportMeta),
|
|
407
|
+
lastActivity: new Date().toISOString(),
|
|
408
|
+
lastError: fallbackResult.error ?? null,
|
|
409
|
+
});
|
|
410
|
+
if (updated) {
|
|
411
|
+
notifyParentSession(updated, { result: fallbackResult.result, error: fallbackResult.error ?? null, cost: fallbackResult.cost, durationMs: fallbackResult.durationMs }, { alwaysNotify: employee?.alwaysNotify });
|
|
412
|
+
}
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
const waitEmoji = "hourglass_flowing_sand";
|
|
417
|
+
const { delayMs, resumeAt } = computeNextRetryDelayMs(rateLimit.resetsAt);
|
|
418
|
+
const deadlineMs = computeRateLimitDeadlineMs(rateLimit.resetsAt, rateLimit.resetsAt ? 30 * 60_000 : 6 * 60 * 60_000);
|
|
419
|
+
const resumeText = resumeAt
|
|
420
|
+
? resumeAt.toLocaleString("en-GB", { weekday: "short", day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" })
|
|
421
|
+
: null;
|
|
422
|
+
logger.info(`Session ${session.id} hit Claude usage limit — will auto-retry ${resumeAt ? `at ${resumeAt.toISOString()}` : `in ${Math.round(delayMs / 1000)}s`}`);
|
|
423
|
+
// Send hardcoded Discord notification — does not depend on LLM
|
|
424
|
+
notifyDiscordChannel(`⚠️ Claude usage limit reached. Session ${session.id}${session.employee ? ` (${session.employee})` : ""} paused${resumeText ? ` until ${resumeText}` : ""}.`);
|
|
425
|
+
// Clear "thinking" UI and show waiting state
|
|
426
|
+
if (decorateMessages && connector.setTypingStatus) {
|
|
427
|
+
await connector.setTypingStatus(target.channel, threadTs, "").catch(() => { });
|
|
428
|
+
}
|
|
429
|
+
if (decorateMessages && capabilities.reactions) {
|
|
430
|
+
await connector.removeReaction(target, "eyes").catch(() => { });
|
|
431
|
+
await connector.addReaction(target, waitEmoji).catch(() => { });
|
|
432
|
+
}
|
|
433
|
+
const waitingSession = updateSession(session.id, {
|
|
434
|
+
...(result.sessionId?.trim() ? { engineSessionId: result.sessionId } : {}),
|
|
435
|
+
status: "waiting",
|
|
436
|
+
lastActivity: new Date().toISOString(),
|
|
437
|
+
lastError: resumeAt
|
|
438
|
+
? `Claude usage limit — resumes ${resumeAt.toISOString()}`
|
|
439
|
+
: "Claude usage limit — waiting for reset",
|
|
440
|
+
}) ?? session;
|
|
441
|
+
notifyRateLimited(waitingSession, resumeAt
|
|
442
|
+
? resumeAt.toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" })
|
|
443
|
+
: undefined);
|
|
444
|
+
await connector.replyMessage(target, `⏳ Claude usage limit reached${resumeText ? `. Resets ${resumeText}` : ""} — I'll continue automatically.`).catch(() => { });
|
|
445
|
+
// Keep lastActivity fresh while waiting (UI / status endpoints)
|
|
446
|
+
const heartbeat = setInterval(() => {
|
|
447
|
+
updateSession(session.id, { status: "waiting", lastActivity: new Date().toISOString() });
|
|
448
|
+
}, 60_000);
|
|
449
|
+
try {
|
|
450
|
+
let attempt = 0;
|
|
451
|
+
let nextDelayMs = delayMs;
|
|
452
|
+
while (Date.now() < deadlineMs) {
|
|
453
|
+
await new Promise(r => setTimeout(r, nextDelayMs));
|
|
454
|
+
attempt++;
|
|
455
|
+
// Check if session was stopped while waiting
|
|
456
|
+
const currentSession = getSessionBySessionKey(msg.sessionKey);
|
|
457
|
+
if (!currentSession || currentSession.status === "error") {
|
|
458
|
+
logger.info(`Session ${session.id} stopped while waiting for usage reset`);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
// Show active processing again
|
|
462
|
+
if (decorateMessages && connector.setTypingStatus) {
|
|
463
|
+
await connector.setTypingStatus(target.channel, threadTs, "is thinking...").catch(() => { });
|
|
464
|
+
}
|
|
465
|
+
if (decorateMessages && capabilities.reactions) {
|
|
466
|
+
await connector.removeReaction(target, waitEmoji).catch(() => { });
|
|
467
|
+
await connector.addReaction(target, "eyes").catch(() => { });
|
|
468
|
+
}
|
|
469
|
+
logger.info(`Session ${session.id} retrying after usage limit (attempt ${attempt})`);
|
|
470
|
+
const retryResult = await engine.run({
|
|
471
|
+
prompt: msg.text,
|
|
472
|
+
resumeSessionId: currentSession.engineSessionId ?? undefined,
|
|
473
|
+
systemPrompt,
|
|
474
|
+
cwd: JINN_HOME,
|
|
475
|
+
bin: engineConfig.bin,
|
|
476
|
+
model: currentSession.model ?? engineConfig.model,
|
|
477
|
+
effortLevel,
|
|
478
|
+
cliFlags: employee?.cliFlags,
|
|
479
|
+
mcpConfigPath,
|
|
480
|
+
attachments: attachments.length > 0 ? attachments : undefined,
|
|
481
|
+
sessionId: session.id,
|
|
482
|
+
});
|
|
483
|
+
const retryInterrupted = retryResult.error?.startsWith("Interrupted");
|
|
484
|
+
const retryRateLimit = !retryInterrupted ? detectRateLimit(retryResult) : { limited: false };
|
|
485
|
+
if (retryRateLimit.limited) {
|
|
486
|
+
recordClaudeRateLimit(retryRateLimit.resetsAt);
|
|
487
|
+
logger.info(`Session ${session.id} still rate limited (attempt ${attempt})`);
|
|
488
|
+
const next = computeNextRetryDelayMs(retryRateLimit.resetsAt);
|
|
489
|
+
nextDelayMs = next.delayMs;
|
|
490
|
+
// Return to waiting UI state
|
|
491
|
+
if (decorateMessages && connector.setTypingStatus) {
|
|
492
|
+
await connector.setTypingStatus(target.channel, threadTs, "").catch(() => { });
|
|
493
|
+
}
|
|
494
|
+
if (decorateMessages && capabilities.reactions) {
|
|
495
|
+
await connector.removeReaction(target, "eyes").catch(() => { });
|
|
496
|
+
await connector.addReaction(target, waitEmoji).catch(() => { });
|
|
497
|
+
}
|
|
498
|
+
updateSession(session.id, {
|
|
499
|
+
...(retryResult.sessionId?.trim() ? { engineSessionId: retryResult.sessionId } : {}),
|
|
500
|
+
status: "waiting",
|
|
501
|
+
lastActivity: new Date().toISOString(),
|
|
502
|
+
lastError: next.resumeAt
|
|
503
|
+
? `Claude usage limit — resumes ${next.resumeAt.toISOString()}`
|
|
504
|
+
: "Claude usage limit — waiting for reset",
|
|
505
|
+
});
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
// Success or different error — handle normally
|
|
509
|
+
const retryText = retryResult.result?.trim()
|
|
510
|
+
? retryResult.result
|
|
511
|
+
: retryResult.error || "(No response from engine)";
|
|
512
|
+
insertMessage(session.id, "assistant", retryText);
|
|
513
|
+
if (retryResult.cost || retryResult.numTurns) {
|
|
514
|
+
accumulateSessionCost(session.id, retryResult.cost ?? 0, retryResult.numTurns ?? 1);
|
|
515
|
+
}
|
|
516
|
+
// Clear typing indicator & reactions
|
|
517
|
+
if (decorateMessages && connector.setTypingStatus) {
|
|
518
|
+
await connector.setTypingStatus(target.channel, threadTs, "").catch(() => { });
|
|
519
|
+
}
|
|
520
|
+
if (decorateMessages && capabilities.reactions) {
|
|
521
|
+
await connector.removeReaction(target, "eyes").catch(() => { });
|
|
522
|
+
await connector.removeReaction(target, waitEmoji).catch(() => { });
|
|
523
|
+
}
|
|
524
|
+
await connector.replyMessage(target, retryText).catch(() => { });
|
|
525
|
+
const retryUpdated = updateSession(session.id, {
|
|
526
|
+
...(retryResult.sessionId?.trim() ? { engineSessionId: retryResult.sessionId } : {}),
|
|
527
|
+
status: retryResult.error ? "error" : "idle",
|
|
528
|
+
replyContext: msg.replyContext,
|
|
529
|
+
messageId: msg.messageId ?? null,
|
|
530
|
+
transportMeta: msg.transportMeta ?? null,
|
|
531
|
+
lastActivity: new Date().toISOString(),
|
|
532
|
+
lastError: retryResult.error ?? null,
|
|
533
|
+
});
|
|
534
|
+
if (retryUpdated) {
|
|
535
|
+
notifyRateLimitResumed(retryUpdated);
|
|
536
|
+
notifyDiscordChannel(`✅ Claude usage limit cleared. Session ${session.id}${session.employee ? ` (${session.employee})` : ""} resumed.`);
|
|
537
|
+
notifyParentSession(retryUpdated, { result: retryResult.result, error: retryResult.error ?? null, cost: retryResult.cost, durationMs: retryResult.durationMs }, { alwaysNotify: employee?.alwaysNotify });
|
|
538
|
+
}
|
|
539
|
+
logger.info(`Session ${session.id} resumed after usage reset`);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
// Exhausted waiting window
|
|
543
|
+
notifyDiscordChannel(`❌ Claude usage limit did not clear in time. Session ${session.id}${session.employee ? ` (${session.employee})` : ""} has been stopped.`);
|
|
544
|
+
await connector.replyMessage(target, "Usage limit didn't reset in time. Please try again later.").catch(() => { });
|
|
545
|
+
updateSession(session.id, {
|
|
546
|
+
status: "error",
|
|
547
|
+
lastActivity: new Date().toISOString(),
|
|
548
|
+
lastError: "Claude usage limit did not clear in time",
|
|
549
|
+
});
|
|
550
|
+
// Clear reactions on failure
|
|
551
|
+
if (decorateMessages && capabilities.reactions) {
|
|
552
|
+
await connector.removeReaction(target, "eyes").catch(() => { });
|
|
553
|
+
await connector.removeReaction(target, waitEmoji).catch(() => { });
|
|
554
|
+
}
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
finally {
|
|
558
|
+
clearInterval(heartbeat);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
const responseText = result.result?.trim()
|
|
562
|
+
? result.result
|
|
563
|
+
: result.error || "(No response from engine)";
|
|
564
|
+
insertMessage(session.id, "assistant", responseText);
|
|
565
|
+
if (result.cost || result.numTurns) {
|
|
566
|
+
accumulateSessionCost(session.id, result.cost ?? 0, result.numTurns ?? 1);
|
|
567
|
+
}
|
|
568
|
+
if (decorateMessages && connector.setTypingStatus) {
|
|
569
|
+
await connector.setTypingStatus(target.channel, threadTs, "").catch(() => { });
|
|
570
|
+
}
|
|
571
|
+
if (!wasInterrupted) {
|
|
572
|
+
await connector.replyMessage(target, responseText);
|
|
573
|
+
}
|
|
574
|
+
if (decorateMessages && capabilities.reactions) {
|
|
575
|
+
await connector.removeReaction(target, "eyes").catch(() => { });
|
|
576
|
+
}
|
|
577
|
+
const updatedSession = updateSession(session.id, {
|
|
578
|
+
...(result.sessionId?.trim() ? { engineSessionId: result.sessionId } : {}),
|
|
579
|
+
status: wasInterrupted ? "idle" : (result.error ? "error" : "idle"),
|
|
580
|
+
replyContext: msg.replyContext,
|
|
581
|
+
messageId: msg.messageId ?? null,
|
|
582
|
+
transportMeta: (() => {
|
|
583
|
+
const merged = mergeTransportMeta(getSessionBySessionKey(msg.sessionKey)?.transportMeta ?? session.transportMeta, msg.transportMeta);
|
|
584
|
+
if (syncRequested && !rateLimit.limited && !wasInterrupted) {
|
|
585
|
+
delete merged["claudeSyncSince"];
|
|
586
|
+
}
|
|
587
|
+
return merged;
|
|
588
|
+
})(),
|
|
589
|
+
lastActivity: new Date().toISOString(),
|
|
590
|
+
lastError: wasInterrupted ? null : (result.error ?? null),
|
|
591
|
+
});
|
|
592
|
+
if (updatedSession) {
|
|
593
|
+
notifyParentSession(updatedSession, { result: result.result, error: wasInterrupted ? null : (result.error ?? null), cost: result.cost, durationMs: result.durationMs }, { alwaysNotify: employee?.alwaysNotify });
|
|
594
|
+
}
|
|
595
|
+
logger.info(`Session ${session.id} completed in ${result.durationMs ?? 0}ms` +
|
|
596
|
+
(result.cost ? ` ($${result.cost.toFixed(4)})` : ""));
|
|
597
|
+
}
|
|
598
|
+
catch (err) {
|
|
599
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
600
|
+
logger.error(`Session ${session.id} error: ${errMsg}`);
|
|
601
|
+
const erroredSession = updateSession(session.id, {
|
|
602
|
+
status: "error",
|
|
603
|
+
lastActivity: new Date().toISOString(),
|
|
604
|
+
lastError: errMsg,
|
|
605
|
+
});
|
|
606
|
+
if (erroredSession) {
|
|
607
|
+
notifyParentSession(erroredSession, { error: errMsg }, { alwaysNotify: employee?.alwaysNotify });
|
|
608
|
+
}
|
|
609
|
+
// Clear typing indicator on error
|
|
610
|
+
if (decorateMessages && connector.setTypingStatus) {
|
|
611
|
+
await connector.setTypingStatus(target.channel, threadTs, "").catch(() => { });
|
|
612
|
+
}
|
|
613
|
+
await connector.replyMessage(target, `Error: ${errMsg}`).catch(() => { });
|
|
614
|
+
if (decorateMessages && capabilities.reactions) {
|
|
615
|
+
await connector.removeReaction(target, "eyes").catch(() => { });
|
|
616
|
+
await connector.removeReaction(target, "hourglass_flowing_sand").catch(() => { });
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
finally {
|
|
620
|
+
// Clean up temp attachment files downloaded from Slack
|
|
621
|
+
for (const filePath of attachments) {
|
|
622
|
+
try {
|
|
623
|
+
fs.rmSync(filePath, { force: true });
|
|
624
|
+
}
|
|
625
|
+
catch {
|
|
626
|
+
// Ignore cleanup errors — best effort
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
if (mcpConfigPath)
|
|
630
|
+
cleanupMcpConfigFile(session.id);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
async handleCommand(msg, connector) {
|
|
634
|
+
const text = msg.text.trim();
|
|
635
|
+
const target = connector.reconstructTarget(msg.replyContext);
|
|
636
|
+
target.messageTs ??= msg.messageId;
|
|
637
|
+
if (text === "/new" || text.startsWith("/new ")) {
|
|
638
|
+
this.resetSession(msg.sessionKey);
|
|
639
|
+
await connector.replyMessage(target, "Session reset. Starting fresh.");
|
|
640
|
+
logger.info(`Session reset for ${msg.sessionKey}`);
|
|
641
|
+
return true;
|
|
642
|
+
}
|
|
643
|
+
if (text === "/status" || text.startsWith("/status ")) {
|
|
644
|
+
const session = getSessionBySessionKey(msg.sessionKey);
|
|
645
|
+
if (!session) {
|
|
646
|
+
await connector.replyMessage(target, "No active session for this conversation.");
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
649
|
+
const queueDepth = this.queue.getPendingCount(session.sessionKey);
|
|
650
|
+
const transportState = this.queue.getTransportState(session.sessionKey, session.status);
|
|
651
|
+
const info = [
|
|
652
|
+
`Session: ${session.id}`,
|
|
653
|
+
`Engine: ${session.engine}`,
|
|
654
|
+
`Connector: ${session.connector || session.source}`,
|
|
655
|
+
`Model: ${session.model || this.config.engines[session.engine]?.model || "default"}`,
|
|
656
|
+
`State: ${transportState}`,
|
|
657
|
+
`Queue depth: ${queueDepth}`,
|
|
658
|
+
`Created: ${session.createdAt}`,
|
|
659
|
+
`Last activity: ${session.lastActivity}`,
|
|
660
|
+
session.lastError ? `Last error: ${session.lastError}` : null,
|
|
661
|
+
].filter(Boolean).join("\n");
|
|
662
|
+
await connector.replyMessage(target, info);
|
|
663
|
+
return true;
|
|
664
|
+
}
|
|
665
|
+
if (text.startsWith("/model")) {
|
|
666
|
+
const nextModel = text.slice("/model".length).trim();
|
|
667
|
+
if (!nextModel) {
|
|
668
|
+
await connector.replyMessage(target, "Usage: /model <model-name>");
|
|
669
|
+
return true;
|
|
670
|
+
}
|
|
671
|
+
const session = getSessionBySessionKey(msg.sessionKey);
|
|
672
|
+
if (!session) {
|
|
673
|
+
await connector.replyMessage(target, "No active session for this conversation.");
|
|
674
|
+
return true;
|
|
675
|
+
}
|
|
676
|
+
updateSession(session.id, {
|
|
677
|
+
model: nextModel,
|
|
678
|
+
lastActivity: new Date().toISOString(),
|
|
679
|
+
});
|
|
680
|
+
await connector.replyMessage(target, `Model updated to \`${nextModel}\` for this session.`);
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
683
|
+
if (text === "/doctor" || text.startsWith("/doctor ")) {
|
|
684
|
+
const connectors = Array.from(this.connectorProvider().values());
|
|
685
|
+
const connectorLines = connectors.length > 0
|
|
686
|
+
? connectors.map((candidate) => {
|
|
687
|
+
const health = candidate.getHealth();
|
|
688
|
+
return `- ${candidate.name}: ${health.status}${health.detail ? ` (${health.detail})` : ""}`;
|
|
689
|
+
})
|
|
690
|
+
: ["- none"];
|
|
691
|
+
const info = [
|
|
692
|
+
`Default engine: ${this.config.engines.default}`,
|
|
693
|
+
`Claude: ${this.config.engines.claude.model}`,
|
|
694
|
+
`Codex: ${this.config.engines.codex.model}`,
|
|
695
|
+
...(this.config.engines.gemini ? [`Gemini: ${this.config.engines.gemini.model}`] : []),
|
|
696
|
+
"Connectors:",
|
|
697
|
+
...connectorLines,
|
|
698
|
+
].join("\n");
|
|
699
|
+
await connector.replyMessage(target, info);
|
|
700
|
+
return true;
|
|
701
|
+
}
|
|
702
|
+
if (text.startsWith("/cron")) {
|
|
703
|
+
return this.handleCronCommand(text, connector, target);
|
|
704
|
+
}
|
|
705
|
+
return false;
|
|
706
|
+
}
|
|
707
|
+
resetSession(sessionKey) {
|
|
708
|
+
const session = getSessionBySessionKey(sessionKey);
|
|
709
|
+
if (session) {
|
|
710
|
+
deleteSession(session.id);
|
|
711
|
+
logger.info(`Deleted session ${session.id}`);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
async handleCronCommand(text, connector, target) {
|
|
715
|
+
const [_, subcommand = "", ...rest] = text.split(/\s+/);
|
|
716
|
+
const arg = rest.join(" ").trim();
|
|
717
|
+
if (!subcommand || subcommand === "list") {
|
|
718
|
+
const jobs = loadJobs();
|
|
719
|
+
if (jobs.length === 0) {
|
|
720
|
+
await connector.replyMessage(target, "No cron jobs configured.");
|
|
721
|
+
return true;
|
|
722
|
+
}
|
|
723
|
+
const lines = jobs.map((job) => `- ${job.name} (${job.id}) — ${job.enabled ? "enabled" : "disabled"} — ${job.schedule}`);
|
|
724
|
+
await connector.replyMessage(target, ["Cron jobs:", ...lines].join("\n"));
|
|
725
|
+
return true;
|
|
726
|
+
}
|
|
727
|
+
if (subcommand === "run") {
|
|
728
|
+
if (!arg) {
|
|
729
|
+
await connector.replyMessage(target, "Usage: /cron run <job-id-or-name>");
|
|
730
|
+
return true;
|
|
731
|
+
}
|
|
732
|
+
const job = await triggerCronJob(arg);
|
|
733
|
+
await connector.replyMessage(target, job ? `Triggered cron job "${job.name}".` : `Cron job "${arg}" not found.`);
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
if (subcommand === "enable" || subcommand === "disable") {
|
|
737
|
+
if (!arg) {
|
|
738
|
+
await connector.replyMessage(target, `Usage: /cron ${subcommand} <job-id-or-name>`);
|
|
739
|
+
return true;
|
|
740
|
+
}
|
|
741
|
+
const job = setCronJobEnabled(arg, subcommand === "enable");
|
|
742
|
+
await connector.replyMessage(target, job
|
|
743
|
+
? `Cron job "${job.name}" ${job.enabled ? "enabled" : "disabled"}.`
|
|
744
|
+
: `Cron job "${arg}" not found.`);
|
|
745
|
+
return true;
|
|
746
|
+
}
|
|
747
|
+
await connector.replyMessage(target, "Usage: /cron [list|run|enable|disable] <job-id-or-name>");
|
|
748
|
+
return true;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
//# sourceMappingURL=manager.js.map
|