@geminixiang/mikan 1.0.0-beta.9 → 1.0.0-beta.91
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/CHANGELOG.md +342 -424
- package/README.md +102 -42
- package/dist/adapters/commands/admin.d.ts.map +1 -0
- package/dist/adapters/commands/admin.js +33 -0
- package/dist/adapters/commands/admin.js.map +1 -0
- package/dist/adapters/commands/auto-reply.d.ts +5 -0
- package/dist/adapters/commands/auto-reply.d.ts.map +1 -0
- package/dist/adapters/commands/auto-reply.js +47 -0
- package/dist/adapters/commands/auto-reply.js.map +1 -0
- package/dist/adapters/commands/login.d.ts +6 -0
- package/dist/adapters/commands/login.d.ts.map +1 -0
- package/dist/adapters/commands/login.js +158 -0
- package/dist/adapters/commands/login.js.map +1 -0
- package/dist/adapters/commands/manifest.d.ts +17 -0
- package/dist/adapters/commands/manifest.d.ts.map +1 -0
- package/dist/adapters/commands/manifest.js +121 -0
- package/dist/adapters/commands/manifest.js.map +1 -0
- package/dist/adapters/commands/model.d.ts +9 -0
- package/dist/adapters/commands/model.d.ts.map +1 -0
- package/dist/adapters/commands/model.js +136 -0
- package/dist/adapters/commands/model.js.map +1 -0
- package/dist/adapters/commands/new.d.ts.map +1 -0
- package/dist/adapters/commands/new.js +39 -0
- package/dist/adapters/commands/new.js.map +1 -0
- package/dist/adapters/commands/registry.d.ts +4 -0
- package/dist/adapters/commands/registry.d.ts.map +1 -0
- package/dist/adapters/commands/registry.js +35 -0
- package/dist/adapters/commands/registry.js.map +1 -0
- package/dist/adapters/commands/sandbox.d.ts +6 -0
- package/dist/adapters/commands/sandbox.d.ts.map +1 -0
- package/dist/adapters/commands/sandbox.js +125 -0
- package/dist/adapters/commands/sandbox.js.map +1 -0
- package/dist/adapters/commands/session-view.d.ts.map +1 -0
- package/dist/adapters/commands/session-view.js +46 -0
- package/dist/adapters/commands/session-view.js.map +1 -0
- package/dist/adapters/commands/types.d.ts +105 -0
- package/dist/adapters/commands/types.d.ts.map +1 -0
- package/dist/adapters/commands/types.js.map +1 -0
- package/dist/adapters/commands/utils.d.ts +11 -0
- package/dist/adapters/commands/utils.d.ts.map +1 -0
- package/dist/adapters/commands/utils.js +43 -0
- package/dist/adapters/commands/utils.js.map +1 -0
- package/dist/adapters/discord/bot.d.ts +14 -18
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +234 -303
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/components.d.ts +4 -0
- package/dist/adapters/discord/components.d.ts.map +1 -0
- package/dist/adapters/discord/components.js +9 -0
- package/dist/adapters/discord/components.js.map +1 -0
- package/dist/adapters/discord/context.d.ts +3 -7
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +57 -219
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/discord/format.d.ts +2 -0
- package/dist/adapters/discord/format.d.ts.map +1 -0
- package/dist/adapters/discord/format.js +47 -0
- package/dist/adapters/discord/format.js.map +1 -0
- package/dist/adapters/discord/types.d.ts +91 -1
- package/dist/adapters/discord/types.d.ts.map +1 -1
- package/dist/adapters/discord/types.js.map +1 -1
- package/dist/adapters/github/activity.d.ts +3 -0
- package/dist/adapters/github/activity.d.ts.map +1 -0
- package/dist/adapters/github/activity.js +148 -0
- package/dist/adapters/github/activity.js.map +1 -0
- package/dist/adapters/github/bot.d.ts +24 -37
- package/dist/adapters/github/bot.d.ts.map +1 -1
- package/dist/adapters/github/bot.js +209 -397
- package/dist/adapters/github/bot.js.map +1 -1
- package/dist/adapters/github/client.d.ts +32 -49
- package/dist/adapters/github/client.d.ts.map +1 -1
- package/dist/adapters/github/client.js +108 -118
- package/dist/adapters/github/client.js.map +1 -1
- package/dist/adapters/github/context.d.ts +3 -8
- package/dist/adapters/github/context.d.ts.map +1 -1
- package/dist/adapters/github/context.js +74 -138
- package/dist/adapters/github/context.js.map +1 -1
- package/dist/adapters/github/github-ops.d.ts +14 -0
- package/dist/adapters/github/github-ops.d.ts.map +1 -0
- package/dist/adapters/github/github-ops.js +218 -0
- package/dist/adapters/github/github-ops.js.map +1 -0
- package/dist/adapters/github/ids.d.ts +2 -23
- package/dist/adapters/github/ids.d.ts.map +1 -1
- package/dist/adapters/github/ids.js +6 -36
- package/dist/adapters/github/ids.js.map +1 -1
- package/dist/adapters/github/policy.d.ts +19 -0
- package/dist/adapters/github/policy.d.ts.map +1 -0
- package/dist/adapters/github/policy.js +79 -0
- package/dist/adapters/github/policy.js.map +1 -0
- package/dist/adapters/github/tool-pack.d.ts +4 -0
- package/dist/adapters/github/tool-pack.d.ts.map +1 -0
- package/dist/adapters/github/tool-pack.js +40 -0
- package/dist/adapters/github/tool-pack.js.map +1 -0
- package/dist/adapters/github/tools/checks.d.ts +14 -0
- package/dist/adapters/github/tools/checks.d.ts.map +1 -0
- package/dist/adapters/github/tools/checks.js +78 -0
- package/dist/adapters/github/tools/checks.js.map +1 -0
- package/dist/adapters/github/tools/issue.d.ts +18 -0
- package/dist/adapters/github/tools/issue.d.ts.map +1 -0
- package/dist/adapters/github/tools/issue.js +44 -0
- package/dist/adapters/github/tools/issue.js.map +1 -0
- package/dist/adapters/github/tools/pr.d.ts +17 -0
- package/dist/adapters/github/tools/pr.d.ts.map +1 -0
- package/dist/adapters/github/tools/pr.js +40 -0
- package/dist/adapters/github/tools/pr.js.map +1 -0
- package/dist/adapters/github/tools/read.d.ts +17 -0
- package/dist/adapters/github/tools/read.d.ts.map +1 -0
- package/dist/adapters/github/tools/read.js +134 -0
- package/dist/adapters/github/tools/read.js.map +1 -0
- package/dist/adapters/github/tools/review-reply.d.ts +14 -0
- package/dist/adapters/github/tools/review-reply.d.ts.map +1 -0
- package/dist/adapters/github/tools/review-reply.js +34 -0
- package/dist/adapters/github/tools/review-reply.js.map +1 -0
- package/dist/adapters/github/types.d.ts +200 -57
- package/dist/adapters/github/types.d.ts.map +1 -1
- package/dist/adapters/github/types.js.map +1 -1
- package/dist/adapters/github/webhook.d.ts +7 -0
- package/dist/adapters/github/webhook.d.ts.map +1 -0
- package/dist/adapters/github/webhook.js +104 -0
- package/dist/adapters/github/webhook.js.map +1 -0
- package/dist/adapters/intake.d.ts +4 -10
- package/dist/adapters/intake.d.ts.map +1 -1
- package/dist/adapters/intake.js +85 -32
- package/dist/adapters/intake.js.map +1 -1
- package/dist/adapters/markdown-tables.d.ts +8 -0
- package/dist/adapters/markdown-tables.d.ts.map +1 -0
- package/dist/adapters/markdown-tables.js +111 -0
- package/dist/adapters/markdown-tables.js.map +1 -0
- package/dist/adapters/messages.d.ts +13 -0
- package/dist/adapters/messages.d.ts.map +1 -0
- package/dist/adapters/messages.js +39 -0
- package/dist/adapters/messages.js.map +1 -0
- package/dist/adapters/progressive-renderer.d.ts +7 -0
- package/dist/adapters/progressive-renderer.d.ts.map +1 -0
- package/dist/adapters/progressive-renderer.js +426 -0
- package/dist/adapters/progressive-renderer.js.map +1 -0
- package/dist/adapters/shared.d.ts +27 -53
- package/dist/adapters/shared.d.ts.map +1 -1
- package/dist/adapters/shared.js +167 -91
- package/dist/adapters/shared.js.map +1 -1
- package/dist/adapters/slack/assistant.d.ts +21 -0
- package/dist/adapters/slack/assistant.d.ts.map +1 -0
- package/dist/adapters/slack/assistant.js +103 -0
- package/dist/adapters/slack/assistant.js.map +1 -0
- package/dist/adapters/slack/auto-reply-context.d.ts +14 -0
- package/dist/adapters/slack/auto-reply-context.d.ts.map +1 -0
- package/dist/adapters/slack/auto-reply-context.js +21 -0
- package/dist/adapters/slack/auto-reply-context.js.map +1 -0
- package/dist/adapters/slack/blocks.d.ts +5 -0
- package/dist/adapters/slack/blocks.d.ts.map +1 -1
- package/dist/adapters/slack/blocks.js +144 -145
- package/dist/adapters/slack/blocks.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +60 -40
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +968 -588
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts +3 -9
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +8 -17
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/slack/jev-context.d.ts +17 -0
- package/dist/adapters/slack/jev-context.d.ts.map +1 -0
- package/dist/adapters/slack/jev-context.js +57 -0
- package/dist/adapters/slack/jev-context.js.map +1 -0
- package/dist/adapters/slack/markdown.d.ts +2 -0
- package/dist/adapters/slack/markdown.d.ts.map +1 -0
- package/dist/adapters/slack/markdown.js +43 -0
- package/dist/adapters/slack/markdown.js.map +1 -0
- package/dist/adapters/slack/persona.d.ts +6 -0
- package/dist/adapters/slack/persona.d.ts.map +1 -0
- package/dist/adapters/slack/persona.js +13 -0
- package/dist/adapters/slack/persona.js.map +1 -0
- package/dist/adapters/slack/response-lifecycle.d.ts +3 -4
- package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
- package/dist/adapters/slack/response-lifecycle.js +222 -348
- package/dist/adapters/slack/response-lifecycle.js.map +1 -1
- package/dist/adapters/slack/session.d.ts +3 -2
- package/dist/adapters/slack/session.d.ts.map +1 -1
- package/dist/adapters/slack/session.js +9 -13
- package/dist/adapters/slack/session.js.map +1 -1
- package/dist/adapters/slack/stream-limits.d.ts +10 -0
- package/dist/adapters/slack/stream-limits.d.ts.map +1 -0
- package/dist/adapters/slack/stream-limits.js +22 -0
- package/dist/adapters/slack/stream-limits.js.map +1 -0
- package/dist/adapters/slack/task-intent.d.ts +19 -0
- package/dist/adapters/slack/task-intent.d.ts.map +1 -0
- package/dist/adapters/slack/task-intent.js +65 -0
- package/dist/adapters/slack/task-intent.js.map +1 -0
- package/dist/adapters/slack/task-status.d.ts +7 -0
- package/dist/adapters/slack/task-status.d.ts.map +1 -0
- package/dist/adapters/slack/task-status.js +96 -0
- package/dist/adapters/slack/task-status.js.map +1 -0
- package/dist/adapters/slack/tool-pack.d.ts +4 -0
- package/dist/adapters/slack/tool-pack.d.ts.map +1 -0
- package/dist/adapters/slack/tool-pack.js +40 -0
- package/dist/adapters/slack/tool-pack.js.map +1 -0
- package/dist/adapters/slack/tools/blockkit.d.ts +17 -0
- package/dist/adapters/slack/tools/blockkit.d.ts.map +1 -0
- package/dist/adapters/slack/tools/blockkit.js +98 -0
- package/dist/adapters/slack/tools/blockkit.js.map +1 -0
- package/dist/adapters/slack/types.d.ts +91 -5
- package/dist/adapters/slack/types.d.ts.map +1 -1
- package/dist/adapters/slack/types.js.map +1 -1
- package/dist/adapters/slack/update-diagnostics.d.ts +13 -0
- package/dist/adapters/slack/update-diagnostics.d.ts.map +1 -0
- package/dist/adapters/slack/update-diagnostics.js +128 -0
- package/dist/adapters/slack/update-diagnostics.js.map +1 -0
- package/dist/adapters/telegram/bot.d.ts +12 -19
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +114 -239
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts +3 -7
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +49 -191
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/adapters/telegram/types.d.ts +24 -1
- package/dist/adapters/telegram/types.d.ts.map +1 -1
- package/dist/adapters/telegram/types.js.map +1 -1
- package/dist/adapters/types.d.ts +92 -17
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/adapters/types.js.map +1 -1
- package/dist/adapters/web/admin/client-assets.d.ts +4 -0
- package/dist/adapters/web/admin/client-assets.d.ts.map +1 -0
- package/dist/adapters/web/admin/client-assets.js +1766 -0
- package/dist/adapters/web/admin/client-assets.js.map +1 -0
- package/dist/adapters/web/admin/portal.d.ts +26 -0
- package/dist/adapters/web/admin/portal.d.ts.map +1 -0
- package/dist/adapters/web/admin/portal.js +1490 -0
- package/dist/adapters/web/admin/portal.js.map +1 -0
- package/dist/adapters/web/admin/provider-models.d.ts +10 -0
- package/dist/adapters/web/admin/provider-models.d.ts.map +1 -0
- package/dist/adapters/web/admin/provider-models.js +103 -0
- package/dist/adapters/web/admin/provider-models.js.map +1 -0
- package/dist/adapters/web/admin/types.d.ts +54 -0
- package/dist/adapters/web/admin/types.d.ts.map +1 -0
- package/dist/adapters/web/admin/types.js.map +1 -0
- package/dist/adapters/web/login/oauth.d.ts +4 -0
- package/dist/adapters/web/login/oauth.d.ts.map +1 -0
- package/dist/adapters/web/login/oauth.js +219 -0
- package/dist/adapters/web/login/oauth.js.map +1 -0
- package/dist/adapters/web/login/portal.d.ts +10 -0
- package/dist/adapters/web/login/portal.d.ts.map +1 -0
- package/dist/adapters/web/login/portal.js +1336 -0
- package/dist/adapters/web/login/portal.js.map +1 -0
- package/dist/adapters/web/login/types.d.ts +34 -0
- package/dist/adapters/web/login/types.d.ts.map +1 -0
- package/dist/adapters/web/login/types.js.map +1 -0
- package/dist/adapters/web/portal-shell.d.ts +9 -0
- package/dist/adapters/web/portal-shell.d.ts.map +1 -0
- package/dist/adapters/web/portal-shell.js +428 -0
- package/dist/adapters/web/portal-shell.js.map +1 -0
- package/dist/adapters/web/server.d.ts +35 -0
- package/dist/adapters/web/server.d.ts.map +1 -0
- package/dist/adapters/web/server.js +80 -0
- package/dist/adapters/web/server.js.map +1 -0
- package/dist/adapters/web/session-view/portal.d.ts +19 -0
- package/dist/adapters/web/session-view/portal.d.ts.map +1 -0
- package/dist/adapters/web/session-view/portal.js +1833 -0
- package/dist/adapters/web/session-view/portal.js.map +1 -0
- package/dist/adapters/web/session-view/types.d.ts +45 -0
- package/dist/adapters/web/session-view/types.d.ts.map +1 -0
- package/dist/adapters/web/session-view/types.js.map +1 -0
- package/dist/adapters/web/token-store.d.ts +10 -0
- package/dist/adapters/web/token-store.d.ts.map +1 -0
- package/dist/adapters/web/token-store.js +42 -0
- package/dist/adapters/web/token-store.js.map +1 -0
- package/dist/adapters/web/types.d.ts.map +1 -0
- package/dist/adapters/web/types.js.map +1 -0
- package/dist/cli/arg-grammar.d.ts +8 -0
- package/dist/cli/arg-grammar.d.ts.map +1 -0
- package/dist/cli/arg-grammar.js +47 -0
- package/dist/cli/arg-grammar.js.map +1 -0
- package/dist/cli/boot.d.ts +4 -0
- package/dist/cli/boot.d.ts.map +1 -0
- package/dist/cli/boot.js +79 -0
- package/dist/cli/boot.js.map +1 -0
- package/dist/cli/download.d.ts.map +1 -0
- package/dist/cli/download.js +84 -0
- package/dist/cli/download.js.map +1 -0
- package/dist/cli/migrate.d.ts +3 -0
- package/dist/cli/migrate.d.ts.map +1 -0
- package/dist/cli/migrate.js +74 -0
- package/dist/cli/migrate.js.map +1 -0
- package/dist/cli/office.d.ts +2 -0
- package/dist/cli/office.d.ts.map +1 -0
- package/dist/cli/office.js +30 -0
- package/dist/cli/office.js.map +1 -0
- package/dist/cli/onboard.d.ts +7 -0
- package/dist/cli/onboard.d.ts.map +1 -0
- package/dist/cli/onboard.js +192 -0
- package/dist/cli/onboard.js.map +1 -0
- package/dist/cli/process-lifecycle.d.ts +14 -0
- package/dist/cli/process-lifecycle.d.ts.map +1 -0
- package/dist/cli/process-lifecycle.js +61 -0
- package/dist/cli/process-lifecycle.js.map +1 -0
- package/dist/cli/types.d.ts +20 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/env-manifest.d.ts +14 -0
- package/dist/env-manifest.d.ts.map +1 -0
- package/dist/env-manifest.js +255 -0
- package/dist/env-manifest.js.map +1 -0
- package/dist/events/index.d.ts +105 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +252 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/scheduler.d.ts +24 -0
- package/dist/events/scheduler.d.ts.map +1 -0
- package/dist/events/scheduler.js +221 -0
- package/dist/events/scheduler.js.map +1 -0
- package/dist/file-guards.d.ts +20 -0
- package/dist/file-guards.d.ts.map +1 -0
- package/dist/file-guards.js +166 -0
- package/dist/file-guards.js.map +1 -0
- package/dist/harness/budget-stop.d.ts +3 -0
- package/dist/harness/budget-stop.d.ts.map +1 -0
- package/dist/harness/budget-stop.js +36 -0
- package/dist/harness/budget-stop.js.map +1 -0
- package/dist/harness/execution-env.d.ts +4 -0
- package/dist/harness/execution-env.d.ts.map +1 -0
- package/dist/harness/execution-env.js +245 -0
- package/dist/harness/execution-env.js.map +1 -0
- package/dist/harness/execution-resolver.d.ts +23 -0
- package/dist/harness/execution-resolver.d.ts.map +1 -0
- package/dist/harness/execution-resolver.js +152 -0
- package/dist/harness/execution-resolver.js.map +1 -0
- package/dist/harness/http.d.ts +0 -12
- package/dist/harness/http.d.ts.map +1 -1
- package/dist/harness/http.js +0 -34
- package/dist/harness/http.js.map +1 -1
- package/dist/harness/index.d.ts +18 -17
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +11 -15
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/jev.d.ts +67 -0
- package/dist/harness/jev.d.ts.map +1 -0
- package/dist/harness/jev.js +135 -0
- package/dist/harness/jev.js.map +1 -0
- package/dist/harness/loop-guard.d.ts +10 -0
- package/dist/harness/loop-guard.d.ts.map +1 -0
- package/dist/harness/loop-guard.js +111 -0
- package/dist/harness/loop-guard.js.map +1 -0
- package/dist/harness/mcp-result.d.ts +21 -0
- package/dist/harness/mcp-result.d.ts.map +1 -0
- package/dist/harness/mcp-result.js +204 -0
- package/dist/harness/mcp-result.js.map +1 -0
- package/dist/harness/mcp.d.ts +17 -0
- package/dist/harness/mcp.d.ts.map +1 -0
- package/dist/harness/mcp.js +339 -0
- package/dist/harness/mcp.js.map +1 -0
- package/dist/harness/models.d.ts +2 -19
- package/dist/harness/models.d.ts.map +1 -1
- package/dist/harness/models.js +43 -95
- package/dist/harness/models.js.map +1 -1
- package/dist/harness/open-connector.d.ts +3 -0
- package/dist/harness/open-connector.d.ts.map +1 -0
- package/dist/harness/open-connector.js +106 -0
- package/dist/harness/open-connector.js.map +1 -0
- package/dist/harness/presenter.d.ts +11 -0
- package/dist/harness/presenter.d.ts.map +1 -0
- package/dist/harness/presenter.js +867 -0
- package/dist/harness/presenter.js.map +1 -0
- package/dist/harness/prompt.d.ts +17 -0
- package/dist/harness/prompt.d.ts.map +1 -0
- package/dist/harness/prompt.js +348 -0
- package/dist/harness/prompt.js.map +1 -0
- package/dist/harness/run-events.d.ts +10 -0
- package/dist/harness/run-events.d.ts.map +1 -0
- package/dist/harness/run-events.js +126 -0
- package/dist/harness/run-events.js.map +1 -0
- package/dist/harness/runner.d.ts +2 -123
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +570 -414
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/session.d.ts +102 -0
- package/dist/harness/session.d.ts.map +1 -0
- package/dist/harness/session.js +725 -0
- package/dist/harness/session.js.map +1 -0
- package/dist/harness/skills.d.ts +7 -41
- package/dist/harness/skills.d.ts.map +1 -1
- package/dist/harness/skills.js +120 -69
- package/dist/harness/skills.js.map +1 -1
- package/dist/harness/subagent-profiles.d.ts +3 -0
- package/dist/harness/subagent-profiles.d.ts.map +1 -0
- package/dist/harness/subagent-profiles.js +260 -0
- package/dist/harness/subagent-profiles.js.map +1 -0
- package/dist/harness/subagent.d.ts +39 -0
- package/dist/harness/subagent.d.ts.map +1 -0
- package/dist/harness/subagent.js +613 -0
- package/dist/harness/subagent.js.map +1 -0
- package/dist/harness/tool-args.d.ts +2 -0
- package/dist/harness/tool-args.d.ts.map +1 -0
- package/dist/harness/tool-args.js +20 -0
- package/dist/harness/tool-args.js.map +1 -0
- package/dist/harness/tools/attach.d.ts +13 -0
- package/dist/harness/tools/attach.d.ts.map +1 -0
- package/dist/harness/tools/attach.js +39 -0
- package/dist/harness/tools/attach.js.map +1 -0
- package/dist/harness/tools/codemode.d.ts +5 -0
- package/dist/harness/tools/codemode.d.ts.map +1 -0
- package/dist/harness/tools/codemode.js +229 -0
- package/dist/harness/tools/codemode.js.map +1 -0
- package/dist/harness/tools/event.d.ts +28 -0
- package/dist/harness/tools/event.d.ts.map +1 -0
- package/dist/harness/tools/event.js +183 -0
- package/dist/harness/tools/event.js.map +1 -0
- package/dist/harness/tools/generate-image.d.ts +20 -0
- package/dist/harness/tools/generate-image.d.ts.map +1 -0
- package/dist/harness/tools/generate-image.js +82 -0
- package/dist/harness/tools/generate-image.js.map +1 -0
- package/dist/harness/tools/history.d.ts +20 -0
- package/dist/harness/tools/history.d.ts.map +1 -0
- package/dist/harness/tools/history.js +293 -0
- package/dist/harness/tools/history.js.map +1 -0
- package/dist/harness/tools/host-fn-tool.d.ts +14 -0
- package/dist/harness/tools/host-fn-tool.d.ts.map +1 -0
- package/dist/harness/tools/host-fn-tool.js +39 -0
- package/dist/harness/tools/host-fn-tool.js.map +1 -0
- package/dist/harness/tools/index.d.ts +34 -0
- package/dist/harness/tools/index.d.ts.map +1 -0
- package/dist/harness/tools/index.js +49 -0
- package/dist/harness/tools/index.js.map +1 -0
- package/dist/harness/tools/jev-browser.d.ts +41 -0
- package/dist/harness/tools/jev-browser.d.ts.map +1 -0
- package/dist/harness/tools/jev-browser.js +570 -0
- package/dist/harness/tools/jev-browser.js.map +1 -0
- package/dist/harness/tools/jev.d.ts +15 -0
- package/dist/harness/tools/jev.d.ts.map +1 -0
- package/dist/harness/tools/jev.js +98 -0
- package/dist/harness/tools/jev.js.map +1 -0
- package/dist/harness/tools/pi-tools.d.ts +8 -0
- package/dist/harness/tools/pi-tools.d.ts.map +1 -0
- package/dist/harness/tools/pi-tools.js +83 -0
- package/dist/harness/tools/pi-tools.js.map +1 -0
- package/dist/harness/tools/react.d.ts +12 -0
- package/dist/harness/tools/react.d.ts.map +1 -0
- package/dist/harness/tools/react.js +25 -0
- package/dist/harness/tools/react.js.map +1 -0
- package/dist/harness/tools/sandbox.d.ts +24 -0
- package/dist/harness/tools/sandbox.d.ts.map +1 -0
- package/dist/harness/tools/sandbox.js +79 -0
- package/dist/harness/tools/sandbox.js.map +1 -0
- package/dist/harness/tools/secret-redaction.d.ts +9 -0
- package/dist/harness/tools/secret-redaction.d.ts.map +1 -0
- package/dist/harness/tools/secret-redaction.js +88 -0
- package/dist/harness/tools/secret-redaction.js.map +1 -0
- package/dist/harness/tools/subagent.d.ts +51 -0
- package/dist/harness/tools/subagent.d.ts.map +1 -0
- package/dist/harness/tools/subagent.js +581 -0
- package/dist/harness/tools/subagent.js.map +1 -0
- package/dist/harness/tools/task.d.ts +22 -0
- package/dist/harness/tools/task.d.ts.map +1 -0
- package/dist/harness/tools/task.js +80 -0
- package/dist/harness/tools/task.js.map +1 -0
- package/dist/harness/tools/tool-search.d.ts +6 -0
- package/dist/harness/tools/tool-search.d.ts.map +1 -0
- package/dist/harness/tools/tool-search.js +89 -0
- package/dist/harness/tools/tool-search.js.map +1 -0
- package/dist/harness/tools/types.d.ts +14 -0
- package/dist/harness/tools/types.d.ts.map +1 -0
- package/dist/harness/tools/types.js.map +1 -0
- package/dist/harness/types.d.ts +488 -29
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/index.d.ts +31 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -7
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +0 -4
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +3 -28
- package/dist/log.js.map +1 -1
- package/dist/main.js +282 -258
- package/dist/main.js.map +1 -1
- package/dist/memory-capture/index.d.ts +15 -0
- package/dist/memory-capture/index.d.ts.map +1 -0
- package/dist/memory-capture/index.js +198 -0
- package/dist/memory-capture/index.js.map +1 -0
- package/dist/memory-capture/types.d.ts +27 -0
- package/dist/memory-capture/types.d.ts.map +1 -0
- package/dist/memory-capture/types.js +2 -0
- package/dist/memory-capture/types.js.map +1 -0
- package/dist/migrations/actor-vaults.d.ts +3 -0
- package/dist/migrations/actor-vaults.d.ts.map +1 -0
- package/dist/migrations/actor-vaults.js +43 -0
- package/dist/migrations/actor-vaults.js.map +1 -0
- package/dist/migrations/conversation-settings.d.ts +3 -0
- package/dist/migrations/conversation-settings.d.ts.map +1 -0
- package/dist/migrations/conversation-settings.js +31 -0
- package/dist/migrations/conversation-settings.js.map +1 -0
- package/dist/migrations/dream-checkpoints.d.ts +3 -0
- package/dist/migrations/dream-checkpoints.d.ts.map +1 -0
- package/dist/migrations/dream-checkpoints.js +23 -0
- package/dist/migrations/dream-checkpoints.js.map +1 -0
- package/dist/migrations/index.d.ts +14 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +105 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrations/models-json.d.ts +3 -0
- package/dist/migrations/models-json.d.ts.map +1 -0
- package/dist/migrations/models-json.js +18 -0
- package/dist/migrations/models-json.js.map +1 -0
- package/dist/migrations/office-layout.d.ts +4 -0
- package/dist/migrations/office-layout.d.ts.map +1 -0
- package/dist/migrations/office-layout.js +94 -0
- package/dist/migrations/office-layout.js.map +1 -0
- package/dist/migrations/office-sessions.d.ts +3 -0
- package/dist/migrations/office-sessions.d.ts.map +1 -0
- package/dist/migrations/office-sessions.js +81 -0
- package/dist/migrations/office-sessions.js.map +1 -0
- package/dist/migrations/private-office-dirs.d.ts +3 -0
- package/dist/migrations/private-office-dirs.d.ts.map +1 -0
- package/dist/migrations/private-office-dirs.js +24 -0
- package/dist/migrations/private-office-dirs.js.map +1 -0
- package/dist/migrations/sandbox-containers.d.ts +3 -0
- package/dist/migrations/sandbox-containers.d.ts.map +1 -0
- package/dist/migrations/sandbox-containers.js +36 -0
- package/dist/migrations/sandbox-containers.js.map +1 -0
- package/dist/migrations/session-files.d.ts +28 -0
- package/dist/migrations/session-files.d.ts.map +1 -0
- package/dist/migrations/session-files.js +80 -0
- package/dist/migrations/session-files.js.map +1 -0
- package/dist/migrations/session-v4.d.ts +77 -0
- package/dist/migrations/session-v4.d.ts.map +1 -0
- package/dist/migrations/session-v4.js +177 -0
- package/dist/migrations/session-v4.js.map +1 -0
- package/dist/migrations/sessions-sqlite.d.ts +3 -0
- package/dist/migrations/sessions-sqlite.d.ts.map +1 -0
- package/dist/migrations/sessions-sqlite.js +215 -0
- package/dist/migrations/sessions-sqlite.js.map +1 -0
- package/dist/migrations/sessions-v3.d.ts +8 -0
- package/dist/migrations/sessions-v3.d.ts.map +1 -0
- package/dist/migrations/sessions-v3.js +350 -0
- package/dist/migrations/sessions-v3.js.map +1 -0
- package/dist/migrations/types.d.ts +25 -0
- package/dist/migrations/types.d.ts.map +1 -0
- package/dist/migrations/types.js +2 -0
- package/dist/migrations/types.js.map +1 -0
- package/dist/migrations/workspace-events.d.ts +3 -0
- package/dist/migrations/workspace-events.d.ts.map +1 -0
- package/dist/migrations/workspace-events.js +43 -0
- package/dist/migrations/workspace-events.js.map +1 -0
- package/dist/observability/index.d.ts +30 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +273 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/instrument.js +9 -6
- package/dist/observability/instrument.js.map +1 -1
- package/dist/observability/otel.d.ts +10 -0
- package/dist/observability/otel.d.ts.map +1 -0
- package/dist/observability/otel.js +182 -0
- package/dist/observability/otel.js.map +1 -0
- package/dist/observability/sentry.d.ts +30 -18
- package/dist/observability/sentry.d.ts.map +1 -1
- package/dist/observability/sentry.js +94 -119
- package/dist/observability/sentry.js.map +1 -1
- package/dist/observability/types.d.ts +30 -19
- package/dist/observability/types.d.ts.map +1 -1
- package/dist/observability/types.js.map +1 -1
- package/dist/office/index.d.ts +49 -0
- package/dist/office/index.d.ts.map +1 -0
- package/dist/office/index.js +395 -0
- package/dist/office/index.js.map +1 -0
- package/dist/office/projection.d.ts +11 -0
- package/dist/office/projection.d.ts.map +1 -0
- package/dist/office/projection.js +145 -0
- package/dist/office/projection.js.map +1 -0
- package/dist/office/types.d.ts +44 -0
- package/dist/office/types.d.ts.map +1 -0
- package/dist/office/types.js +2 -0
- package/dist/office/types.js.map +1 -0
- package/dist/runtime/conversation-runtime.d.ts +0 -1
- package/dist/runtime/conversation-runtime.d.ts.map +1 -1
- package/dist/runtime/conversation-runtime.js +365 -155
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/session-lifecycle.d.ts +65 -0
- package/dist/runtime/session-lifecycle.d.ts.map +1 -0
- package/dist/runtime/session-lifecycle.js +384 -0
- package/dist/runtime/session-lifecycle.js.map +1 -0
- package/dist/runtime/types.d.ts +42 -20
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sandbox/cloudflare.d.ts +4 -0
- package/dist/sandbox/cloudflare.d.ts.map +1 -1
- package/dist/sandbox/cloudflare.js +59 -55
- package/dist/sandbox/cloudflare.js.map +1 -1
- package/dist/sandbox/container.d.ts +3 -0
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +39 -20
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sandbox/host.d.ts +3 -0
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +42 -35
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sandbox/identity.d.ts +12 -0
- package/dist/sandbox/identity.d.ts.map +1 -0
- package/dist/sandbox/identity.js +42 -0
- package/dist/sandbox/identity.js.map +1 -0
- package/dist/sandbox/index.d.ts +5 -15
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +5 -51
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/layout.d.ts +5 -0
- package/dist/sandbox/layout.d.ts.map +1 -0
- package/dist/sandbox/layout.js +13 -0
- package/dist/sandbox/layout.js.map +1 -0
- package/dist/sandbox/provisioner.d.ts +64 -0
- package/dist/sandbox/provisioner.d.ts.map +1 -0
- package/dist/sandbox/provisioner.js +546 -0
- package/dist/sandbox/provisioner.js.map +1 -0
- package/dist/sandbox/registry.d.ts +12 -0
- package/dist/sandbox/registry.d.ts.map +1 -0
- package/dist/sandbox/registry.js +93 -0
- package/dist/sandbox/registry.js.map +1 -0
- package/dist/sandbox/types.d.ts +23 -25
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sandbox/types.js.map +1 -1
- package/dist/sandbox/utils.d.ts +21 -0
- package/dist/sandbox/utils.d.ts.map +1 -1
- package/dist/sandbox/utils.js +100 -8
- package/dist/sandbox/utils.js.map +1 -1
- package/dist/sessions/chat-history-sync.d.ts +19 -0
- package/dist/sessions/chat-history-sync.d.ts.map +1 -0
- package/dist/sessions/chat-history-sync.js +459 -0
- package/dist/sessions/chat-history-sync.js.map +1 -0
- package/dist/sessions/compaction-summary.d.ts +4 -0
- package/dist/sessions/compaction-summary.d.ts.map +1 -0
- package/dist/sessions/compaction-summary.js +16 -0
- package/dist/sessions/compaction-summary.js.map +1 -0
- package/dist/sessions/history-line.d.ts +10 -0
- package/dist/sessions/history-line.d.ts.map +1 -0
- package/dist/sessions/history-line.js +29 -0
- package/dist/sessions/history-line.js.map +1 -0
- package/dist/sessions/session-key.d.ts +19 -0
- package/dist/sessions/session-key.d.ts.map +1 -0
- package/dist/sessions/session-key.js +91 -0
- package/dist/sessions/session-key.js.map +1 -0
- package/dist/sessions/session-store.d.ts +73 -0
- package/dist/sessions/session-store.d.ts.map +1 -0
- package/dist/sessions/session-store.js +721 -0
- package/dist/sessions/session-store.js.map +1 -0
- package/dist/sessions/types.d.ts +90 -34
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/sessions/types.js +2 -1
- package/dist/sessions/types.js.map +1 -1
- package/dist/settings/apply.d.ts +9 -0
- package/dist/settings/apply.d.ts.map +1 -0
- package/dist/settings/apply.js +31 -0
- package/dist/settings/apply.js.map +1 -0
- package/dist/settings/index.d.ts +28 -0
- package/dist/settings/index.d.ts.map +1 -0
- package/dist/settings/index.js +322 -0
- package/dist/settings/index.js.map +1 -0
- package/dist/types.d.ts +209 -191
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -3
- package/dist/types.js.map +1 -1
- package/dist/unknown-values.d.ts +3 -0
- package/dist/unknown-values.d.ts.map +1 -0
- package/dist/unknown-values.js +7 -0
- package/dist/unknown-values.js.map +1 -0
- package/dist/vault/index.d.ts +28 -11
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +291 -118
- package/dist/vault/index.js.map +1 -1
- package/dist/vault/types.d.ts +5 -19
- package/dist/vault/types.d.ts.map +1 -1
- package/dist/vault/types.js.map +1 -1
- package/package.json +87 -41
- package/dist/adapter.d.ts +0 -2
- package/dist/adapter.d.ts.map +0 -1
- package/dist/adapter.js +0 -2
- package/dist/adapter.js.map +0 -1
- package/dist/adapters/github/repo.d.ts +0 -19
- package/dist/adapters/github/repo.d.ts.map +0 -1
- package/dist/adapters/github/repo.js +0 -58
- package/dist/adapters/github/repo.js.map +0 -1
- package/dist/adapters/slack/tools/attach.d.ts +0 -12
- package/dist/adapters/slack/tools/attach.d.ts.map +0 -1
- package/dist/adapters/slack/tools/attach.js +0 -39
- package/dist/adapters/slack/tools/attach.js.map +0 -1
- package/dist/adapters/streaming.d.ts +0 -18
- package/dist/adapters/streaming.d.ts.map +0 -1
- package/dist/adapters/streaming.js +0 -44
- package/dist/adapters/streaming.js.map +0 -1
- package/dist/adapters/telegram/html.d.ts +0 -3
- package/dist/adapters/telegram/html.d.ts.map +0 -1
- package/dist/adapters/telegram/html.js +0 -98
- package/dist/adapters/telegram/html.js.map +0 -1
- package/dist/agent-events.d.ts +0 -5
- package/dist/agent-events.d.ts.map +0 -1
- package/dist/agent-events.js +0 -26
- package/dist/agent-events.js.map +0 -1
- package/dist/agent.d.ts +0 -39
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -1299
- package/dist/agent.js.map +0 -1
- package/dist/cli/ext-git.d.ts +0 -22
- package/dist/cli/ext-git.d.ts.map +0 -1
- package/dist/cli/ext-git.js +0 -76
- package/dist/cli/ext-git.js.map +0 -1
- package/dist/cli/ext.d.ts +0 -2
- package/dist/cli/ext.d.ts.map +0 -1
- package/dist/cli/ext.js +0 -217
- package/dist/cli/ext.js.map +0 -1
- package/dist/commands/admin.d.ts.map +0 -1
- package/dist/commands/admin.js +0 -30
- package/dist/commands/admin.js.map +0 -1
- package/dist/commands/auto-reply.d.ts +0 -8
- package/dist/commands/auto-reply.d.ts.map +0 -1
- package/dist/commands/auto-reply.js +0 -76
- package/dist/commands/auto-reply.js.map +0 -1
- package/dist/commands/extensions.d.ts +0 -14
- package/dist/commands/extensions.d.ts.map +0 -1
- package/dist/commands/extensions.js +0 -55
- package/dist/commands/extensions.js.map +0 -1
- package/dist/commands/login.d.ts +0 -5
- package/dist/commands/login.d.ts.map +0 -1
- package/dist/commands/login.js +0 -115
- package/dist/commands/login.js.map +0 -1
- package/dist/commands/model.d.ts +0 -10
- package/dist/commands/model.d.ts.map +0 -1
- package/dist/commands/model.js +0 -102
- package/dist/commands/model.js.map +0 -1
- package/dist/commands/new.d.ts.map +0 -1
- package/dist/commands/new.js +0 -25
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/parse.d.ts +0 -7
- package/dist/commands/parse.d.ts.map +0 -1
- package/dist/commands/parse.js +0 -14
- package/dist/commands/parse.js.map +0 -1
- package/dist/commands/registry.d.ts +0 -5
- package/dist/commands/registry.d.ts.map +0 -1
- package/dist/commands/registry.js +0 -31
- package/dist/commands/registry.js.map +0 -1
- package/dist/commands/sandbox.d.ts +0 -7
- package/dist/commands/sandbox.d.ts.map +0 -1
- package/dist/commands/sandbox.js +0 -78
- package/dist/commands/sandbox.js.map +0 -1
- package/dist/commands/session-view.d.ts.map +0 -1
- package/dist/commands/session-view.js +0 -41
- package/dist/commands/session-view.js.map +0 -1
- package/dist/commands/types.d.ts +0 -66
- package/dist/commands/types.d.ts.map +0 -1
- package/dist/commands/types.js.map +0 -1
- package/dist/commands/utils.d.ts +0 -11
- package/dist/commands/utils.d.ts.map +0 -1
- package/dist/commands/utils.js +0 -29
- package/dist/commands/utils.js.map +0 -1
- package/dist/config.d.ts +0 -78
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -450
- package/dist/config.js.map +0 -1
- package/dist/download.d.ts.map +0 -1
- package/dist/download.js +0 -89
- package/dist/download.js.map +0 -1
- package/dist/events.d.ts +0 -43
- package/dist/events.d.ts.map +0 -1
- package/dist/events.js +0 -456
- package/dist/events.js.map +0 -1
- package/dist/execution-resolver.d.ts +0 -21
- package/dist/execution-resolver.d.ts.map +0 -1
- package/dist/execution-resolver.js +0 -170
- package/dist/execution-resolver.js.map +0 -1
- package/dist/harness/auth.d.ts +0 -16
- package/dist/harness/auth.d.ts.map +0 -1
- package/dist/harness/auth.js +0 -86
- package/dist/harness/auth.js.map +0 -1
- package/dist/harness/extensions/loader.d.ts +0 -86
- package/dist/harness/extensions/loader.d.ts.map +0 -1
- package/dist/harness/extensions/loader.js +0 -525
- package/dist/harness/extensions/loader.js.map +0 -1
- package/dist/harness/extensions/registry.d.ts +0 -23
- package/dist/harness/extensions/registry.d.ts.map +0 -1
- package/dist/harness/extensions/registry.js +0 -45
- package/dist/harness/extensions/registry.js.map +0 -1
- package/dist/harness/extensions/types.d.ts +0 -248
- package/dist/harness/extensions/types.d.ts.map +0 -1
- package/dist/harness/extensions/types.js.map +0 -1
- package/dist/harness/session-store.d.ts +0 -77
- package/dist/harness/session-store.d.ts.map +0 -1
- package/dist/harness/session-store.js +0 -248
- package/dist/harness/session-store.js.map +0 -1
- package/dist/harness/settings.d.ts +0 -47
- package/dist/harness/settings.d.ts.map +0 -1
- package/dist/harness/settings.js +0 -29
- package/dist/harness/settings.js.map +0 -1
- package/dist/model-registry.d.ts +0 -4
- package/dist/model-registry.d.ts.map +0 -1
- package/dist/model-registry.js +0 -7
- package/dist/model-registry.js.map +0 -1
- package/dist/platform-messages.d.ts +0 -12
- package/dist/platform-messages.d.ts.map +0 -1
- package/dist/platform-messages.js +0 -36
- package/dist/platform-messages.js.map +0 -1
- package/dist/portal-shell.d.ts +0 -4
- package/dist/portal-shell.d.ts.map +0 -1
- package/dist/portal-shell.js +0 -366
- package/dist/portal-shell.js.map +0 -1
- package/dist/provisioner.d.ts +0 -61
- package/dist/provisioner.d.ts.map +0 -1
- package/dist/provisioner.js +0 -517
- package/dist/provisioner.js.map +0 -1
- package/dist/runtime/agent-run-controller.d.ts +0 -31
- package/dist/runtime/agent-run-controller.d.ts.map +0 -1
- package/dist/runtime/agent-run-controller.js +0 -195
- package/dist/runtime/agent-run-controller.js.map +0 -1
- package/dist/sandbox/errors.d.ts +0 -6
- package/dist/sandbox/errors.d.ts.map +0 -1
- package/dist/sandbox/errors.js +0 -11
- package/dist/sandbox/errors.js.map +0 -1
- package/dist/sandbox/firecracker.d.ts +0 -15
- package/dist/sandbox/firecracker.d.ts.map +0 -1
- package/dist/sandbox/firecracker.js +0 -212
- package/dist/sandbox/firecracker.js.map +0 -1
- package/dist/sandbox/image.d.ts +0 -3
- package/dist/sandbox/image.d.ts.map +0 -1
- package/dist/sandbox/image.js +0 -27
- package/dist/sandbox/image.js.map +0 -1
- package/dist/sandbox/path-context.d.ts +0 -3
- package/dist/sandbox/path-context.d.ts.map +0 -1
- package/dist/sandbox/path-context.js +0 -20
- package/dist/sandbox/path-context.js.map +0 -1
- package/dist/sessions/agent-memory-file-manager.d.ts +0 -19
- package/dist/sessions/agent-memory-file-manager.d.ts.map +0 -1
- package/dist/sessions/agent-memory-file-manager.js +0 -484
- package/dist/sessions/agent-memory-file-manager.js.map +0 -1
- package/dist/sessions/metadata.d.ts +0 -3
- package/dist/sessions/metadata.d.ts.map +0 -1
- package/dist/sessions/metadata.js +0 -11
- package/dist/sessions/metadata.js.map +0 -1
- package/dist/sessions/policy.d.ts +0 -6
- package/dist/sessions/policy.d.ts.map +0 -1
- package/dist/sessions/policy.js +0 -24
- package/dist/sessions/policy.js.map +0 -1
- package/dist/sessions/store.d.ts +0 -72
- package/dist/sessions/store.d.ts.map +0 -1
- package/dist/sessions/store.js +0 -193
- package/dist/sessions/store.js.map +0 -1
- package/dist/store.d.ts +0 -46
- package/dist/store.d.ts.map +0 -1
- package/dist/store.js +0 -177
- package/dist/store.js.map +0 -1
- package/dist/tools/bash.d.ts +0 -10
- package/dist/tools/bash.d.ts.map +0 -1
- package/dist/tools/bash.js +0 -80
- package/dist/tools/bash.js.map +0 -1
- package/dist/tools/edit.d.ts +0 -11
- package/dist/tools/edit.d.ts.map +0 -1
- package/dist/tools/edit.js +0 -131
- package/dist/tools/edit.js.map +0 -1
- package/dist/tools/event.d.ts +0 -56
- package/dist/tools/event.d.ts.map +0 -1
- package/dist/tools/event.js +0 -252
- package/dist/tools/event.js.map +0 -1
- package/dist/tools/github-pr.d.ts +0 -22
- package/dist/tools/github-pr.d.ts.map +0 -1
- package/dist/tools/github-pr.js +0 -53
- package/dist/tools/github-pr.js.map +0 -1
- package/dist/tools/index.d.ts +0 -25
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js +0 -35
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/react.d.ts +0 -15
- package/dist/tools/react.d.ts.map +0 -1
- package/dist/tools/react.js +0 -40
- package/dist/tools/react.js.map +0 -1
- package/dist/tools/read.d.ts +0 -11
- package/dist/tools/read.d.ts.map +0 -1
- package/dist/tools/read.js +0 -134
- package/dist/tools/read.js.map +0 -1
- package/dist/tools/sandbox.d.ts +0 -22
- package/dist/tools/sandbox.d.ts.map +0 -1
- package/dist/tools/sandbox.js +0 -73
- package/dist/tools/sandbox.js.map +0 -1
- package/dist/tools/truncate.d.ts +0 -33
- package/dist/tools/truncate.d.ts.map +0 -1
- package/dist/tools/truncate.js +0 -184
- package/dist/tools/truncate.js.map +0 -1
- package/dist/tools/types.d.ts +0 -54
- package/dist/tools/types.d.ts.map +0 -1
- package/dist/tools/types.js.map +0 -1
- package/dist/tools/write.d.ts +0 -10
- package/dist/tools/write.d.ts.map +0 -1
- package/dist/tools/write.js +0 -29
- package/dist/tools/write.js.map +0 -1
- package/dist/trigger.d.ts +0 -22
- package/dist/trigger.d.ts.map +0 -1
- package/dist/trigger.js +0 -100
- package/dist/trigger.js.map +0 -1
- package/dist/utils/date.d.ts +0 -10
- package/dist/utils/date.d.ts.map +0 -1
- package/dist/utils/date.js +0 -23
- package/dist/utils/date.js.map +0 -1
- package/dist/utils/env.d.ts +0 -3
- package/dist/utils/env.d.ts.map +0 -1
- package/dist/utils/env.js +0 -12
- package/dist/utils/env.js.map +0 -1
- package/dist/utils/file-guards.d.ts +0 -9
- package/dist/utils/file-guards.d.ts.map +0 -1
- package/dist/utils/file-guards.js +0 -59
- package/dist/utils/file-guards.js.map +0 -1
- package/dist/utils/fs-atomic.d.ts +0 -10
- package/dist/utils/fs-atomic.d.ts.map +0 -1
- package/dist/utils/fs-atomic.js +0 -45
- package/dist/utils/fs-atomic.js.map +0 -1
- package/dist/utils/html.d.ts +0 -2
- package/dist/utils/html.d.ts.map +0 -1
- package/dist/utils/html.js +0 -4
- package/dist/utils/html.js.map +0 -1
- package/dist/utils/http-body.d.ts +0 -10
- package/dist/utils/http-body.d.ts.map +0 -1
- package/dist/utils/http-body.js +0 -34
- package/dist/utils/http-body.js.map +0 -1
- package/dist/vault/routing.d.ts +0 -3
- package/dist/vault/routing.d.ts.map +0 -1
- package/dist/vault/routing.js +0 -13
- package/dist/vault/routing.js.map +0 -1
- package/dist/web/admin/portal.d.ts +0 -5
- package/dist/web/admin/portal.d.ts.map +0 -1
- package/dist/web/admin/portal.js +0 -2572
- package/dist/web/admin/portal.js.map +0 -1
- package/dist/web/admin/provider-models.d.ts +0 -10
- package/dist/web/admin/provider-models.d.ts.map +0 -1
- package/dist/web/admin/provider-models.js +0 -96
- package/dist/web/admin/provider-models.js.map +0 -1
- package/dist/web/admin/store.d.ts +0 -13
- package/dist/web/admin/store.d.ts.map +0 -1
- package/dist/web/admin/store.js +0 -14
- package/dist/web/admin/store.js.map +0 -1
- package/dist/web/admin/types.d.ts +0 -30
- package/dist/web/admin/types.d.ts.map +0 -1
- package/dist/web/admin/types.js.map +0 -1
- package/dist/web/agent-events/portal.d.ts +0 -3
- package/dist/web/agent-events/portal.d.ts.map +0 -1
- package/dist/web/agent-events/portal.js +0 -18
- package/dist/web/agent-events/portal.js.map +0 -1
- package/dist/web/login/oauth.d.ts +0 -6
- package/dist/web/login/oauth.d.ts.map +0 -1
- package/dist/web/login/oauth.js +0 -258
- package/dist/web/login/oauth.js.map +0 -1
- package/dist/web/login/portal.d.ts +0 -17
- package/dist/web/login/portal.d.ts.map +0 -1
- package/dist/web/login/portal.js +0 -1329
- package/dist/web/login/portal.js.map +0 -1
- package/dist/web/login/store.d.ts +0 -8
- package/dist/web/login/store.d.ts.map +0 -1
- package/dist/web/login/store.js +0 -15
- package/dist/web/login/store.js.map +0 -1
- package/dist/web/login/types.d.ts +0 -50
- package/dist/web/login/types.d.ts.map +0 -1
- package/dist/web/login/types.js.map +0 -1
- package/dist/web/request.d.ts +0 -3
- package/dist/web/request.d.ts.map +0 -1
- package/dist/web/request.js +0 -13
- package/dist/web/request.js.map +0 -1
- package/dist/web/server.d.ts +0 -28
- package/dist/web/server.d.ts.map +0 -1
- package/dist/web/server.js +0 -66
- package/dist/web/server.js.map +0 -1
- package/dist/web/session-view/command.d.ts +0 -4
- package/dist/web/session-view/command.d.ts.map +0 -1
- package/dist/web/session-view/command.js +0 -7
- package/dist/web/session-view/command.js.map +0 -1
- package/dist/web/session-view/portal.d.ts +0 -13
- package/dist/web/session-view/portal.d.ts.map +0 -1
- package/dist/web/session-view/portal.js +0 -1651
- package/dist/web/session-view/portal.js.map +0 -1
- package/dist/web/session-view/service.d.ts +0 -6
- package/dist/web/session-view/service.d.ts.map +0 -1
- package/dist/web/session-view/service.js +0 -498
- package/dist/web/session-view/service.js.map +0 -1
- package/dist/web/session-view/store.d.ts +0 -8
- package/dist/web/session-view/store.d.ts.map +0 -1
- package/dist/web/session-view/store.js +0 -15
- package/dist/web/session-view/store.js.map +0 -1
- package/dist/web/session-view/types.d.ts +0 -50
- package/dist/web/session-view/types.d.ts.map +0 -1
- package/dist/web/session-view/types.js.map +0 -1
- package/dist/web/token-store.d.ts +0 -13
- package/dist/web/token-store.d.ts.map +0 -1
- package/dist/web/token-store.js +0 -44
- package/dist/web/token-store.js.map +0 -1
- package/dist/web/types.d.ts.map +0 -1
- package/dist/web/types.js.map +0 -1
- /package/dist/{commands → adapters/commands}/admin.d.ts +0 -0
- /package/dist/{commands → adapters/commands}/new.d.ts +0 -0
- /package/dist/{commands → adapters/commands}/session-view.d.ts +0 -0
- /package/dist/{commands → adapters/commands}/types.js +0 -0
- /package/dist/{harness/extensions → adapters/web/admin}/types.js +0 -0
- /package/dist/{tools → adapters/web/login}/types.js +0 -0
- /package/dist/{web/admin → adapters/web/session-view}/types.js +0 -0
- /package/dist/{web → adapters/web}/types.d.ts +0 -0
- /package/dist/{web/login → adapters/web}/types.js +0 -0
- /package/dist/{download.d.ts → cli/download.d.ts} +0 -0
- /package/dist/{web/session-view → cli}/types.js +0 -0
- /package/dist/{web → harness/tools}/types.js +0 -0
|
@@ -1,22 +1,60 @@
|
|
|
1
1
|
import { SocketModeClient } from "@slack/socket-mode";
|
|
2
|
-
import { WebClient } from "@slack/web-api";
|
|
3
|
-
import { existsSync, readFileSync } from "fs";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { WebAPIRateLimitedError, WebClient } from "@slack/web-api";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { basename } from "node:path";
|
|
5
|
+
import { createConversationEvent, createConversationMessage, createOfficeAddress, listRegisteredOffices, } from "../../office/index.js";
|
|
6
|
+
import { COMMAND_MANIFEST } from "../commands/manifest.js";
|
|
7
|
+
import { slackConversationAutoReplyMode, resolveConversationSettings, } from "../../settings/index.js";
|
|
8
|
+
import { evaluateWithJev, JevNotConfiguredError } from "../../harness/jev.js";
|
|
7
9
|
import * as log from "../../log.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
10
|
+
import { readTextFileNoFollowIfExists } from "../../file-guards.js";
|
|
11
|
+
import { PRODUCT_NAME, formatForceStopped } from "../messages.js";
|
|
12
|
+
import { appendBotResponseLog, appendChannelLog, MessagingEventQueue, MessagingIntakeTracker, AttachmentRejectedError, saveIncomingAttachments, writeResponseToFile, withRetry, } from "../shared.js";
|
|
13
|
+
import { matchMagicWord, processMessageIntake } from "../intake.js";
|
|
14
|
+
import { readPlatformChannelKind, recordPlatformChannelKind } from "../../office/projection.js";
|
|
15
|
+
import { AssistantThreadRegistry, handleAgentContextChanged, handleAgentDmOpened, handleAssistantThreadStarted, titleAssistantThread, } from "./assistant.js";
|
|
11
16
|
import { createSlackAdapters } from "./context.js";
|
|
12
|
-
import { hasMaterializedChatSession, registerThreadSession, } from "../../sessions/
|
|
17
|
+
import { hasMaterializedChatSession, registerThreadSession, } from "../../sessions/chat-history-sync.js";
|
|
18
|
+
import { conversationIdOf } from "../../sessions/session-key.js";
|
|
13
19
|
import { isSlackThreadSessionKey, planSlackAdapterSession, planSlackEventAnchorRun, resolveSlackSessionKey, } from "./session.js";
|
|
14
|
-
import { reportUserFacingError } from "../../observability/
|
|
15
|
-
import {
|
|
20
|
+
import { reportUserFacingError } from "../../observability/index.js";
|
|
21
|
+
import { recordSlackUpdate } from "./update-diagnostics.js";
|
|
22
|
+
import { renderSlackBlocks, resolveSlackMentions } from "./blocks.js";
|
|
23
|
+
import { querySlackTasks, formatTaskStatus, readTaskRoots, isTaskStatusQuestion, } from "./task-status.js";
|
|
24
|
+
import { buildAutoReplyState, JEV_ADDRESSED_INSTRUCTIONS } from "./auto-reply-context.js";
|
|
25
|
+
import { buildTaskIntentState, classifyTaskIntent } from "./task-intent.js";
|
|
26
|
+
import { StreamStartLimiter } from "./stream-limits.js";
|
|
27
|
+
import { errorMessage, isRecord } from "../../unknown-values.js";
|
|
16
28
|
const SLACK_EVENT_ANCHOR_TEXT = "Working on it...";
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
function hasMessageContent(message, includeBlocks = true) {
|
|
30
|
+
const content = [message.text, message.files];
|
|
31
|
+
if (includeBlocks)
|
|
32
|
+
content.push(message.blocks, message.attachments);
|
|
33
|
+
return content.some((part) => !!part?.length);
|
|
34
|
+
}
|
|
35
|
+
function hasBotIdentity(message) {
|
|
36
|
+
return !!message.bot_id || message.subtype === "bot_message";
|
|
37
|
+
}
|
|
38
|
+
const USER_MESSAGE_SUBTYPES = new Set([undefined, "file_share"]);
|
|
39
|
+
const BOT_MESSAGE_SUBTYPES = new Set([...USER_MESSAGE_SUBTYPES, "bot_message"]);
|
|
40
|
+
const MAX_STREAM_TEXT_CHARS = 12_000;
|
|
41
|
+
export function chunkStreamText(text, limit = MAX_STREAM_TEXT_CHARS) {
|
|
42
|
+
if (text.length <= limit)
|
|
43
|
+
return [text];
|
|
44
|
+
const chunks = [];
|
|
45
|
+
for (let index = 0; index < text.length; index += limit) {
|
|
46
|
+
chunks.push(text.slice(index, index + limit));
|
|
47
|
+
}
|
|
48
|
+
return chunks;
|
|
49
|
+
}
|
|
50
|
+
function slackErrorCode(error) {
|
|
51
|
+
if (!isRecord(error) || !isRecord(error.data))
|
|
52
|
+
return undefined;
|
|
53
|
+
return typeof error.data.error === "string" ? error.data.error : undefined;
|
|
54
|
+
}
|
|
19
55
|
function slackIsRateLimited(err) {
|
|
56
|
+
if (err instanceof WebAPIRateLimitedError)
|
|
57
|
+
return true;
|
|
20
58
|
if (err.code === "rate_limited")
|
|
21
59
|
return true;
|
|
22
60
|
const data = err.data;
|
|
@@ -47,75 +85,200 @@ function collectSlackText(value, parts) {
|
|
|
47
85
|
collectSlackText(obj.elements, parts);
|
|
48
86
|
collectSlackText(obj.blocks, parts);
|
|
49
87
|
}
|
|
88
|
+
function unescapeSlackText(text) {
|
|
89
|
+
return text.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
90
|
+
}
|
|
50
91
|
function buildSlackAppMessageText(event) {
|
|
51
92
|
const parts = [];
|
|
52
93
|
collectSlackText(event.text, parts);
|
|
53
94
|
collectSlackText(event.blocks, parts);
|
|
54
95
|
collectSlackText(event.attachments, parts);
|
|
55
96
|
const deduped = parts.filter((part, index) => parts.indexOf(part) === index);
|
|
56
|
-
return deduped.join("\n");
|
|
97
|
+
return unescapeSlackText(deduped.join("\n"));
|
|
98
|
+
}
|
|
99
|
+
export function italicizeLines(text) {
|
|
100
|
+
return text
|
|
101
|
+
.split("\n")
|
|
102
|
+
.map((line) => (line.trim() ? `_${line}_` : line))
|
|
103
|
+
.join("\n");
|
|
104
|
+
}
|
|
105
|
+
const RECENT_MESSAGE_KEYS = 2000;
|
|
106
|
+
function withLoggedAck(event, payload) {
|
|
107
|
+
return {
|
|
108
|
+
...payload,
|
|
109
|
+
ack: () => payload.ack().catch((error) => {
|
|
110
|
+
log.logWarning(`Slack ack failed for ${event}; Slack may redeliver it`, errorMessage(error));
|
|
111
|
+
}),
|
|
112
|
+
};
|
|
57
113
|
}
|
|
58
|
-
// ---------------------------------------------------------------------------
|
|
59
|
-
// Shared mrkdwn truncation helper
|
|
60
|
-
// ---------------------------------------------------------------------------
|
|
61
114
|
const MRKDWN_CONTEXT_TEXT_LIMIT = 3000;
|
|
62
|
-
/**
|
|
63
|
-
* Build a Slack context block whose text is capped at the mrkdwn limit.
|
|
64
|
-
* Used for muted diagnostics and ephemeral command responses.
|
|
65
|
-
*/
|
|
66
115
|
export function buildMrkdwnContextBlock(text) {
|
|
67
116
|
const blockText = text.length > MRKDWN_CONTEXT_TEXT_LIMIT
|
|
68
117
|
? text.substring(0, MRKDWN_CONTEXT_TEXT_LIMIT - 20) + "\n_(truncated)_"
|
|
69
118
|
: text;
|
|
70
119
|
return { type: "context", elements: [{ type: "mrkdwn", text: blockText }] };
|
|
71
120
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
121
|
+
class AttachmentDownloadHttpError extends Error {
|
|
122
|
+
constructor(message, status) {
|
|
123
|
+
super(message);
|
|
124
|
+
this.status = status;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function isRetryableAttachmentDownloadError(error) {
|
|
128
|
+
if (error instanceof AttachmentRejectedError)
|
|
129
|
+
return false;
|
|
130
|
+
if (!(error instanceof AttachmentDownloadHttpError))
|
|
131
|
+
return true;
|
|
132
|
+
return error.status === 408 || error.status === 429 || error.status >= 500;
|
|
133
|
+
}
|
|
134
|
+
function assertSlackFileUrl(url) {
|
|
135
|
+
const { protocol, hostname } = new URL(url);
|
|
136
|
+
if (protocol === "https:" && (hostname === "slack.com" || hostname.endsWith(".slack.com")))
|
|
137
|
+
return;
|
|
138
|
+
throw new AttachmentRejectedError(`refusing to send the bot token to ${hostname}`);
|
|
139
|
+
}
|
|
75
140
|
export class SlackMessagingBot {
|
|
141
|
+
office(channelId) {
|
|
142
|
+
return this.workspace.office(createOfficeAddress("slack", channelId));
|
|
143
|
+
}
|
|
144
|
+
conversationDir(channelId) {
|
|
145
|
+
return this.office(channelId).dir;
|
|
146
|
+
}
|
|
147
|
+
resolveReplyMode(address) {
|
|
148
|
+
return (resolveConversationSettings(this.workspace.office(address)).slack?.replyMode ?? "top-level");
|
|
149
|
+
}
|
|
76
150
|
createContext(event) {
|
|
77
|
-
|
|
78
|
-
replyMode:
|
|
79
|
-
"top-level",
|
|
151
|
+
const context = createSlackAdapters(event, this, {
|
|
152
|
+
replyMode: this.resolveReplyMode(event.address),
|
|
80
153
|
});
|
|
154
|
+
if (event.conversationKind === "direct") {
|
|
155
|
+
context.responder.getTaskStatus = (key) => querySlackTasks(this.office(event.channel), event.channel, this.handler
|
|
156
|
+
.getRunningSessions()
|
|
157
|
+
.filter((s) => s.address.platform === "slack" && s.address.conversationId === event.channel), key);
|
|
158
|
+
}
|
|
159
|
+
if (event.conversationKind === "direct" &&
|
|
160
|
+
event.thread_ts &&
|
|
161
|
+
this.isTaskThread(event.channel, event.thread_ts)) {
|
|
162
|
+
let notified = false;
|
|
163
|
+
context.responder.notifyCompletion = async () => {
|
|
164
|
+
if (notified)
|
|
165
|
+
return;
|
|
166
|
+
const text = `<@${event.user}> 這一輪處理已結束,請查看上方結果。`;
|
|
167
|
+
const ts = await this.postInThread(event.channel, event.thread_ts, text);
|
|
168
|
+
notified = true;
|
|
169
|
+
this.logBotResponse(event.channel, text, ts, event.thread_ts);
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (event.conversationKind === "direct" && !event.thread_ts) {
|
|
173
|
+
context.responder.startTask = async (message, task) => {
|
|
174
|
+
if (this.stopped)
|
|
175
|
+
throw new Error("Slack is shutting down; task was not started.");
|
|
176
|
+
await context.responder.deleteResponse();
|
|
177
|
+
const root = await this.postMessage(event.channel, message);
|
|
178
|
+
this.logToFile(event.channel, {
|
|
179
|
+
date: new Date().toISOString(),
|
|
180
|
+
ts: root,
|
|
181
|
+
user: "bot",
|
|
182
|
+
text: message,
|
|
183
|
+
isMessagingBot: true,
|
|
184
|
+
taskRoot: true,
|
|
185
|
+
});
|
|
186
|
+
const sessionKey = resolveSlackSessionKey(event.channel, root);
|
|
187
|
+
try {
|
|
188
|
+
await registerThreadSession({
|
|
189
|
+
office: this.office(event.channel),
|
|
190
|
+
sessionKey,
|
|
191
|
+
});
|
|
192
|
+
const child = {
|
|
193
|
+
...event,
|
|
194
|
+
ts: `task:${root}`,
|
|
195
|
+
thread_ts: root,
|
|
196
|
+
sessionKey,
|
|
197
|
+
text: task,
|
|
198
|
+
};
|
|
199
|
+
if (!this.getQueue(sessionKey).enqueue(async () => {
|
|
200
|
+
try {
|
|
201
|
+
await this.handler.handleEvent({
|
|
202
|
+
...child,
|
|
203
|
+
attachments: child.attachments?.map((a) => ({
|
|
204
|
+
name: a.original,
|
|
205
|
+
localPath: a.localPath,
|
|
206
|
+
})),
|
|
207
|
+
}, this, this.createContext(child));
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
reportUserFacingError(error, {
|
|
211
|
+
domain: "mikan",
|
|
212
|
+
surface: "task_handoff",
|
|
213
|
+
operation: "start_task_run",
|
|
214
|
+
severity: "error",
|
|
215
|
+
platform: "slack",
|
|
216
|
+
context: { conversationId: event.channel, sessionKey, threadTs: root },
|
|
217
|
+
});
|
|
218
|
+
await this.postInThread(event.channel, root, "Task could not start. Please reply here to try again.");
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
}))
|
|
222
|
+
throw new Error("Task queue is closed.");
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
await this.updateMessage(event.channel, root, `${message}\n\nTask could not start.`);
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
return sessionKey;
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return context;
|
|
81
232
|
}
|
|
82
233
|
constructor(handler, config) {
|
|
234
|
+
this.statusUpdates = new Map();
|
|
83
235
|
this.botUserId = null;
|
|
84
236
|
this.botId = null;
|
|
85
237
|
this.teamId = null;
|
|
86
238
|
this.ownMentionRegex = null;
|
|
87
|
-
this.startupTs = null;
|
|
239
|
+
this.startupTs = null;
|
|
88
240
|
this.users = new Map();
|
|
89
241
|
this.channels = new Map();
|
|
242
|
+
this.assistantThreads = new AssistantThreadRegistry();
|
|
243
|
+
this.streamStarts = new StreamStartLimiter();
|
|
244
|
+
this.stopped = false;
|
|
90
245
|
this.queues = new Map();
|
|
91
|
-
this.
|
|
246
|
+
this.intake = new MessagingIntakeTracker("Slack");
|
|
247
|
+
this.recentMessageKeys = new Set();
|
|
248
|
+
this.agentSurfaceUnavailable = false;
|
|
249
|
+
this.eventScheduler = null;
|
|
92
250
|
this.handler = handler;
|
|
93
|
-
this.
|
|
94
|
-
this.
|
|
95
|
-
this.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
251
|
+
this.workspace = config.workspace;
|
|
252
|
+
this.botToken = config.botToken;
|
|
253
|
+
this.fetchFn = config.fetch ?? ((input, init) => fetch(input, init));
|
|
254
|
+
this.socketClient =
|
|
255
|
+
config.socket ??
|
|
256
|
+
new SocketModeClient({
|
|
257
|
+
appToken: config.appToken,
|
|
258
|
+
clientPingTimeout: 12_000,
|
|
259
|
+
});
|
|
260
|
+
this.webClient = config.webApi ?? new WebClient(config.botToken, { fetch: config.fetch });
|
|
261
|
+
this.statusClient = new WebClient(config.botToken, {
|
|
262
|
+
timeout: 3000,
|
|
263
|
+
retryConfig: { retries: 0 },
|
|
264
|
+
rejectRateLimitedCalls: true,
|
|
265
|
+
fetch: config.fetch,
|
|
100
266
|
});
|
|
101
|
-
this.webClient = new WebClient(config.botToken);
|
|
102
267
|
}
|
|
103
|
-
|
|
104
|
-
this.
|
|
268
|
+
setEventScheduler(scheduler) {
|
|
269
|
+
this.eventScheduler = scheduler;
|
|
105
270
|
}
|
|
106
|
-
// ==========================================================================
|
|
107
|
-
// Public API
|
|
108
|
-
// ==========================================================================
|
|
109
271
|
async start() {
|
|
272
|
+
this.stopped = false;
|
|
110
273
|
const auth = await this.webClient.auth.test();
|
|
111
274
|
this.botUserId = auth.user_id;
|
|
112
275
|
this.botId = typeof auth.bot_id === "string" ? auth.bot_id : null;
|
|
113
276
|
this.teamId = typeof auth.team_id === "string" ? auth.team_id : null;
|
|
114
277
|
await Promise.all([this.fetchUsers(), this.fetchChannels()]);
|
|
278
|
+
if (this.stopped)
|
|
279
|
+
return;
|
|
115
280
|
log.logInfo(`Loaded ${this.channels.size} channels, ${this.users.size} users`);
|
|
116
|
-
|
|
117
|
-
// those should be logged but not processed. Backfill runs in the background up
|
|
118
|
-
// to this timestamp so startup is not blocked by one history call per channel.
|
|
281
|
+
this.backfillChannelKinds();
|
|
119
282
|
this.startupTs = (Date.now() / 1000).toFixed(6);
|
|
120
283
|
this.setupEventHandlers();
|
|
121
284
|
await this.socketClient.start();
|
|
@@ -124,6 +287,16 @@ export class SlackMessagingBot {
|
|
|
124
287
|
log.logWarning("Slack backfill failed", String(error));
|
|
125
288
|
});
|
|
126
289
|
}
|
|
290
|
+
async stop() {
|
|
291
|
+
this.stopped = true;
|
|
292
|
+
try {
|
|
293
|
+
await this.socketClient.disconnect();
|
|
294
|
+
}
|
|
295
|
+
finally {
|
|
296
|
+
await this.intake.close();
|
|
297
|
+
await Promise.all([...this.queues.values()].map((queue) => queue.close()));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
127
300
|
getUser(userId) {
|
|
128
301
|
return this.users.get(userId);
|
|
129
302
|
}
|
|
@@ -137,7 +310,7 @@ export class SlackMessagingBot {
|
|
|
137
310
|
return Array.from(this.channels.values());
|
|
138
311
|
}
|
|
139
312
|
stripOwnMention(text) {
|
|
140
|
-
const source = text ?? "";
|
|
313
|
+
const source = unescapeSlackText(text ?? "");
|
|
141
314
|
if (!this.botUserId)
|
|
142
315
|
return source.trim();
|
|
143
316
|
if (!this.ownMentionRegex || !this.ownMentionRegex.source.includes(this.botUserId)) {
|
|
@@ -145,54 +318,64 @@ export class SlackMessagingBot {
|
|
|
145
318
|
}
|
|
146
319
|
return source.replace(this.ownMentionRegex, "").trim();
|
|
147
320
|
}
|
|
148
|
-
|
|
321
|
+
resolveMentions(text) {
|
|
322
|
+
return resolveSlackMentions(text, this.users.values());
|
|
323
|
+
}
|
|
324
|
+
async postMessage(channel, text, threadTs, identity) {
|
|
149
325
|
return slackRetry(async () => {
|
|
150
|
-
const payload = {
|
|
326
|
+
const payload = {
|
|
327
|
+
channel,
|
|
328
|
+
thread_ts: threadTs,
|
|
329
|
+
username: identity?.username,
|
|
330
|
+
icon_emoji: identity?.iconEmoji,
|
|
331
|
+
...renderSlackBlocks(this.resolveMentions(text)),
|
|
332
|
+
};
|
|
151
333
|
const result = await this.webClient.chat.postMessage(payload);
|
|
152
334
|
return result.ts;
|
|
153
335
|
});
|
|
154
336
|
}
|
|
155
337
|
async addReaction(channel, messageTs, emoji) {
|
|
156
|
-
// Slack reaction names are colon-free short names; strip any wrapping colons.
|
|
157
338
|
const name = emoji.replace(/^:|:$/g, "");
|
|
158
339
|
await slackRetry(async () => {
|
|
159
340
|
try {
|
|
160
341
|
await this.webClient.reactions.add({ channel, timestamp: messageTs, name });
|
|
161
342
|
}
|
|
162
343
|
catch (err) {
|
|
163
|
-
// Re-reacting with the same emoji is not an error worth surfacing.
|
|
164
344
|
if (err?.data?.error === "already_reacted")
|
|
165
345
|
return;
|
|
166
346
|
throw err;
|
|
167
347
|
}
|
|
168
348
|
});
|
|
169
349
|
}
|
|
170
|
-
async postEphemeral(channel, user, text, threadTs) {
|
|
350
|
+
async postEphemeral(channel, user, text, threadTs, blocks) {
|
|
171
351
|
return slackRetry(async () => {
|
|
172
352
|
await this.webClient.chat.postEphemeral({
|
|
173
353
|
channel,
|
|
174
354
|
user,
|
|
175
355
|
text,
|
|
176
|
-
|
|
356
|
+
blocks: blocks,
|
|
357
|
+
thread_ts: threadTs || undefined,
|
|
177
358
|
});
|
|
178
359
|
});
|
|
179
360
|
}
|
|
180
361
|
async postEphemeralBlocks(channel, user, text, blocks, threadTs) {
|
|
362
|
+
return this.postEphemeral(channel, user, text, threadTs, blocks);
|
|
363
|
+
}
|
|
364
|
+
async postMessageBlocks(channel, text, blocks, threadTs) {
|
|
181
365
|
return slackRetry(async () => {
|
|
182
|
-
|
|
366
|
+
const payload = {
|
|
183
367
|
channel,
|
|
184
|
-
user,
|
|
185
368
|
text,
|
|
186
369
|
blocks: blocks,
|
|
187
|
-
|
|
188
|
-
}
|
|
370
|
+
thread_ts: threadTs,
|
|
371
|
+
};
|
|
372
|
+
const result = await this.webClient.chat.postMessage(payload);
|
|
373
|
+
return result.ts;
|
|
189
374
|
});
|
|
190
375
|
}
|
|
191
|
-
async
|
|
376
|
+
async updateMessageBlocks(channel, ts, text, blocks) {
|
|
192
377
|
return slackRetry(async () => {
|
|
193
|
-
|
|
194
|
-
const result = await this.webClient.chat.postMessage(payload);
|
|
195
|
-
return result.ts;
|
|
378
|
+
await this.webClient.chat.update({ channel, ts, text, blocks: blocks });
|
|
196
379
|
});
|
|
197
380
|
}
|
|
198
381
|
async postPrivate(conversationId, userId, text) {
|
|
@@ -200,12 +383,12 @@ export class SlackMessagingBot {
|
|
|
200
383
|
}
|
|
201
384
|
async postPrivateDiagnostic(conversationId, userId, text, options) {
|
|
202
385
|
if (options?.style !== "muted") {
|
|
203
|
-
await this.postEphemeral(conversationId, userId, options?.style === "error" ?
|
|
386
|
+
await this.postEphemeral(conversationId, userId, options?.style === "error" ? italicizeLines(text) : text);
|
|
204
387
|
return;
|
|
205
388
|
}
|
|
206
389
|
await this.postEphemeralBlocks(conversationId, userId, text, [buildMrkdwnContextBlock(text)]);
|
|
207
390
|
}
|
|
208
|
-
async
|
|
391
|
+
async openDirectConversation(userId) {
|
|
209
392
|
return slackRetry(async () => {
|
|
210
393
|
const result = await this.webClient.conversations.open({ users: userId });
|
|
211
394
|
const channelId = result.channel?.id;
|
|
@@ -215,33 +398,101 @@ export class SlackMessagingBot {
|
|
|
215
398
|
return channelId;
|
|
216
399
|
});
|
|
217
400
|
}
|
|
401
|
+
async fetchHistory(channel, options) {
|
|
402
|
+
const limit = Math.min(Math.max(options?.limit ?? 200, 1), 999);
|
|
403
|
+
const threadTs = options?.threadTs;
|
|
404
|
+
return slackRetry(async () => {
|
|
405
|
+
const result = threadTs
|
|
406
|
+
? await this.webClient.conversations.replies({
|
|
407
|
+
channel,
|
|
408
|
+
ts: threadTs,
|
|
409
|
+
oldest: options?.oldest || undefined,
|
|
410
|
+
inclusive: options?.oldest ? false : undefined,
|
|
411
|
+
limit,
|
|
412
|
+
})
|
|
413
|
+
: await this.webClient.conversations.history({
|
|
414
|
+
channel,
|
|
415
|
+
oldest: options?.oldest || undefined,
|
|
416
|
+
inclusive: options?.oldest ? false : undefined,
|
|
417
|
+
limit,
|
|
418
|
+
});
|
|
419
|
+
const messages = (result.messages ?? []);
|
|
420
|
+
const mapped = messages
|
|
421
|
+
.filter((msg) => !!msg.ts)
|
|
422
|
+
.filter((msg) => msg.ts !== threadTs)
|
|
423
|
+
.map((msg) => {
|
|
424
|
+
const user = msg.user ? this.users.get(msg.user) : undefined;
|
|
425
|
+
const message = {
|
|
426
|
+
ts: msg.ts,
|
|
427
|
+
text: unescapeSlackText(msg.text ?? ""),
|
|
428
|
+
isBot: !!msg.bot_id || msg.subtype === "bot_message" || user?.isBot === true,
|
|
429
|
+
};
|
|
430
|
+
if (msg.thread_ts && msg.thread_ts !== msg.ts)
|
|
431
|
+
message.threadTs = msg.thread_ts;
|
|
432
|
+
if (msg.user)
|
|
433
|
+
message.userId = msg.user;
|
|
434
|
+
if (user)
|
|
435
|
+
message.userName = user.userName;
|
|
436
|
+
return message;
|
|
437
|
+
});
|
|
438
|
+
return threadTs ? mapped : mapped.toReversed();
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
async listUsers() {
|
|
442
|
+
await slackRetry(() => this.fetchUsers());
|
|
443
|
+
return this.getAllUsers().map((user) => ({
|
|
444
|
+
id: user.id,
|
|
445
|
+
userName: user.userName,
|
|
446
|
+
displayName: user.displayName,
|
|
447
|
+
isBot: user.isBot === true,
|
|
448
|
+
}));
|
|
449
|
+
}
|
|
218
450
|
async updateMessage(channel, ts, text) {
|
|
219
451
|
return slackRetry(async () => {
|
|
220
|
-
const payload = { channel, ts, ...renderSlackBlocks(text) };
|
|
221
|
-
|
|
452
|
+
const payload = { channel, ts, ...renderSlackBlocks(this.resolveMentions(text)) };
|
|
453
|
+
try {
|
|
454
|
+
await this.webClient.chat.update(payload);
|
|
455
|
+
}
|
|
456
|
+
catch (error) {
|
|
457
|
+
recordSlackUpdate(this, { channel, ts }, text, payload, { error });
|
|
458
|
+
throw error;
|
|
459
|
+
}
|
|
460
|
+
recordSlackUpdate(this, { channel, ts }, text, payload, { success: true });
|
|
222
461
|
});
|
|
223
462
|
}
|
|
463
|
+
tryReserveStreamStart() {
|
|
464
|
+
return this.streamStarts.tryReserve();
|
|
465
|
+
}
|
|
224
466
|
async startMessageStream(channel, text, threadTs, recipientUserId) {
|
|
225
|
-
|
|
467
|
+
const [head, ...tail] = chunkStreamText(text);
|
|
468
|
+
const ts = await slackRetry(async () => {
|
|
226
469
|
const result = await this.webClient.apiCall("chat.startStream", {
|
|
227
470
|
channel,
|
|
228
|
-
markdown_text:
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
471
|
+
markdown_text: this.resolveMentions(head ?? ""),
|
|
472
|
+
thread_ts: threadTs || undefined,
|
|
473
|
+
recipient_team_id: this.teamId || undefined,
|
|
474
|
+
recipient_user_id: recipientUserId || undefined,
|
|
232
475
|
});
|
|
233
|
-
const
|
|
234
|
-
if (!
|
|
476
|
+
const streamTs = result.ts;
|
|
477
|
+
if (!streamTs)
|
|
235
478
|
throw new Error("Slack chat.startStream did not return ts");
|
|
236
|
-
return
|
|
479
|
+
return streamTs;
|
|
237
480
|
});
|
|
481
|
+
for (const part of tail)
|
|
482
|
+
await this.appendStreamChunk(channel, ts, part);
|
|
483
|
+
return ts;
|
|
238
484
|
}
|
|
239
485
|
async appendMessageStream(channel, ts, text) {
|
|
486
|
+
for (const part of chunkStreamText(text)) {
|
|
487
|
+
await this.appendStreamChunk(channel, ts, part);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
async appendStreamChunk(channel, ts, text) {
|
|
240
491
|
return slackRetry(async () => {
|
|
241
492
|
await this.webClient.apiCall("chat.appendStream", {
|
|
242
493
|
channel,
|
|
243
494
|
ts,
|
|
244
|
-
markdown_text: text,
|
|
495
|
+
markdown_text: this.resolveMentions(text),
|
|
245
496
|
});
|
|
246
497
|
});
|
|
247
498
|
}
|
|
@@ -255,38 +506,93 @@ export class SlackMessagingBot {
|
|
|
255
506
|
await this.webClient.chat.delete({ channel, ts });
|
|
256
507
|
});
|
|
257
508
|
}
|
|
258
|
-
// ==========================================================================
|
|
259
|
-
// Slack Assistant API (AI assistant experience)
|
|
260
|
-
// ==========================================================================
|
|
261
|
-
/** Set the status for an assistant thread (shows "thinking" state) */
|
|
262
509
|
async setAssistantStatus(channel, threadTs, status) {
|
|
263
|
-
|
|
264
|
-
|
|
510
|
+
const key = `${channel}:${threadTs}`;
|
|
511
|
+
const previous = this.statusUpdates.get(key) ?? Promise.resolve();
|
|
512
|
+
const update = previous
|
|
513
|
+
.catch(() => undefined)
|
|
514
|
+
.then(async () => {
|
|
515
|
+
await this.statusClient.assistant.threads.setStatus({
|
|
265
516
|
channel_id: channel,
|
|
266
517
|
thread_ts: threadTs,
|
|
267
518
|
status,
|
|
268
519
|
});
|
|
269
520
|
});
|
|
521
|
+
this.statusUpdates.set(key, update);
|
|
522
|
+
try {
|
|
523
|
+
await update;
|
|
524
|
+
}
|
|
525
|
+
finally {
|
|
526
|
+
if (this.statusUpdates.get(key) === update)
|
|
527
|
+
this.statusUpdates.delete(key);
|
|
528
|
+
}
|
|
270
529
|
}
|
|
271
|
-
async
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
530
|
+
async setAssistantSuggestedPrompts(channel, threadTs, prompts) {
|
|
531
|
+
if (this.agentSurfaceUnavailable)
|
|
532
|
+
return;
|
|
533
|
+
try {
|
|
534
|
+
await slackRetry(async () => {
|
|
535
|
+
await this.webClient.assistant.threads.setSuggestedPrompts({
|
|
536
|
+
channel_id: channel,
|
|
537
|
+
thread_ts: threadTs || undefined,
|
|
538
|
+
prompts,
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
catch (error) {
|
|
543
|
+
if (slackErrorCode(error) !== "not_agent_app")
|
|
544
|
+
throw error;
|
|
545
|
+
this.agentSurfaceUnavailable = true;
|
|
546
|
+
log.logInfo("Slack app is not an agent app; suggested prompts are disabled");
|
|
547
|
+
}
|
|
277
548
|
}
|
|
278
|
-
async
|
|
549
|
+
async setAssistantTitle(channel, threadTs, title) {
|
|
279
550
|
return slackRetry(async () => {
|
|
280
|
-
|
|
281
|
-
channel,
|
|
551
|
+
await this.webClient.assistant.threads.setTitle({
|
|
552
|
+
channel_id: channel,
|
|
282
553
|
thread_ts: threadTs,
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
};
|
|
286
|
-
const result = await this.webClient.chat.postMessage(payload);
|
|
287
|
-
return result.ts;
|
|
554
|
+
title,
|
|
555
|
+
});
|
|
288
556
|
});
|
|
289
557
|
}
|
|
558
|
+
assistantOps() {
|
|
559
|
+
return {
|
|
560
|
+
postInThread: (channel, threadTs, text) => this.postInThread(channel, threadTs, text),
|
|
561
|
+
setSuggestedPrompts: (channel, threadTs, prompts) => this.setAssistantSuggestedPrompts(channel, threadTs, prompts),
|
|
562
|
+
setTitle: (channel, threadTs, title) => this.setAssistantTitle(channel, threadTs, title),
|
|
563
|
+
channelName: (channelId) => this.channels.get(channelId)?.name,
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
handleAssistantThreadStarted({ event, ack }) {
|
|
567
|
+
ack();
|
|
568
|
+
const payload = event;
|
|
569
|
+
if (!payload.assistant_thread)
|
|
570
|
+
return;
|
|
571
|
+
void handleAssistantThreadStarted(this.assistantOps(), this.assistantThreads, payload.assistant_thread);
|
|
572
|
+
}
|
|
573
|
+
handleAgentContextChangedEvent({ event, ack }) {
|
|
574
|
+
ack();
|
|
575
|
+
const payload = event;
|
|
576
|
+
if (payload.assistant_thread) {
|
|
577
|
+
handleAgentContextChanged(this.assistantThreads, payload.assistant_thread);
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
const channelId = payload.channel_id ?? payload.channel;
|
|
581
|
+
if (channelId) {
|
|
582
|
+
handleAgentContextChanged(this.assistantThreads, {
|
|
583
|
+
channel_id: channelId,
|
|
584
|
+
context: payload.context,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
async postInThread(channel, threadTs, text, identity) {
|
|
589
|
+
return identity
|
|
590
|
+
? this.postMessage(channel, text, threadTs, identity)
|
|
591
|
+
: this.postMessage(channel, text, threadTs);
|
|
592
|
+
}
|
|
593
|
+
async postInThreadBlocks(channel, threadTs, text, blocks) {
|
|
594
|
+
return this.postMessageBlocks(channel, text, blocks, threadTs);
|
|
595
|
+
}
|
|
290
596
|
async uploadFile(channel, filePath, title, threadTs) {
|
|
291
597
|
return slackRetry(async () => {
|
|
292
598
|
const fileName = title || basename(filePath);
|
|
@@ -301,18 +607,41 @@ export class SlackMessagingBot {
|
|
|
301
607
|
});
|
|
302
608
|
}
|
|
303
609
|
logToFile(channel, entry) {
|
|
304
|
-
appendChannelLog(this.
|
|
610
|
+
appendChannelLog(this.workspace.office(createOfficeAddress("slack", channel)), entry);
|
|
305
611
|
}
|
|
306
|
-
logBotResponse(channel, text, ts, threadTs, slackBlocks) {
|
|
307
|
-
appendBotResponseLog(this.
|
|
612
|
+
logBotResponse(channel, text, ts, threadTs, { slackBlocks, answer } = {}) {
|
|
613
|
+
appendBotResponseLog(this.workspace.office(createOfficeAddress("slack", channel)), text, ts, threadTs, {
|
|
308
614
|
platform: "slack",
|
|
309
|
-
|
|
615
|
+
slackBlocks,
|
|
616
|
+
...answer,
|
|
310
617
|
});
|
|
311
618
|
}
|
|
619
|
+
ownsBlockKitMessage(channel, ts, threadTs) {
|
|
620
|
+
const content = readTextFileNoFollowIfExists(this.office(channel).logPath);
|
|
621
|
+
if (content === undefined)
|
|
622
|
+
return false;
|
|
623
|
+
for (const line of content.trim().split("\n").toReversed()) {
|
|
624
|
+
try {
|
|
625
|
+
const entry = JSON.parse(line);
|
|
626
|
+
if (!isRecord(entry) || entry.ts !== ts)
|
|
627
|
+
continue;
|
|
628
|
+
return (entry.isMessagingBot === true &&
|
|
629
|
+
entry.platform === "slack" &&
|
|
630
|
+
Array.isArray(entry.slackBlocks) &&
|
|
631
|
+
entry.threadTs === threadTs);
|
|
632
|
+
}
|
|
633
|
+
catch {
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
312
639
|
getMessagingInfo() {
|
|
313
640
|
return {
|
|
314
641
|
name: "slack",
|
|
315
|
-
|
|
642
|
+
workspaceId: this.teamId || undefined,
|
|
643
|
+
trustModel: "membership",
|
|
644
|
+
formattingGuide: "## Slack Formatting\nWrite standard Markdown/GFM: **bold**, _italic_, ~~strike~~, `code`, fenced code blocks, [links](url), lists, and pipe tables (rendered as native Slack tables).\nDo NOT use Slack mrkdwn syntax like *single-asterisk bold* or <url|label> links.",
|
|
316
645
|
channels: this.getAllChannels().map((c) => ({ id: c.id, name: c.name })),
|
|
317
646
|
users: this.getAllUsers().map((u) => ({
|
|
318
647
|
id: u.id,
|
|
@@ -324,29 +653,18 @@ export class SlackMessagingBot {
|
|
|
324
653
|
},
|
|
325
654
|
};
|
|
326
655
|
}
|
|
327
|
-
// ==========================================================================
|
|
328
|
-
// Events Integration
|
|
329
|
-
// ==========================================================================
|
|
330
|
-
/**
|
|
331
|
-
* Enqueue an event for processing. Always queues (no "already working" rejection).
|
|
332
|
-
* Returns true if enqueued, false if queue is full (max 5).
|
|
333
|
-
*/
|
|
334
656
|
enqueueEvent(event) {
|
|
335
|
-
|
|
336
|
-
const queue = this.getQueue(conversationId);
|
|
337
|
-
if (queue.size() >= 5) {
|
|
338
|
-
log.logWarning(`Event queue full for ${conversationId}, discarding: ${event.text.substring(0, 50)}`);
|
|
657
|
+
if (this.stopped)
|
|
339
658
|
return false;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
queue.enqueue(async () => {
|
|
659
|
+
const conversationId = event.address.conversationId;
|
|
660
|
+
return this.getQueue(conversationId).offerEvent(event, async () => {
|
|
343
661
|
let anchorTs;
|
|
344
662
|
if (!event.thread_ts) {
|
|
345
663
|
try {
|
|
346
664
|
anchorTs = await this.postMessage(conversationId, SLACK_EVENT_ANCHOR_TEXT);
|
|
347
665
|
}
|
|
348
666
|
catch (err) {
|
|
349
|
-
log.logWarning(`Failed to post Slack event anchor for ${conversationId}`,
|
|
667
|
+
log.logWarning(`Failed to post Slack event anchor for ${conversationId}`, errorMessage(err));
|
|
350
668
|
reportUserFacingError(err, {
|
|
351
669
|
domain: "events",
|
|
352
670
|
surface: "event_delivery",
|
|
@@ -366,16 +684,18 @@ export class SlackMessagingBot {
|
|
|
366
684
|
const eventPlan = planSlackEventAnchorRun(event, anchorTs);
|
|
367
685
|
const eventForRun = eventPlan.event;
|
|
368
686
|
if (eventPlan.initialMessageTs && eventForRun.sessionKey) {
|
|
369
|
-
registerThreadSession({
|
|
370
|
-
|
|
687
|
+
await registerThreadSession({
|
|
688
|
+
office: this.office(conversationId),
|
|
371
689
|
sessionKey: eventForRun.sessionKey,
|
|
372
690
|
});
|
|
373
691
|
}
|
|
374
692
|
const runQueueKey = planSlackAdapterSession(eventForRun, {
|
|
375
693
|
initialMessageTs: eventPlan.initialMessageTs,
|
|
376
694
|
}).sessionKey;
|
|
377
|
-
|
|
378
|
-
const slackEvent = {
|
|
695
|
+
const run = async () => {
|
|
696
|
+
const slackEvent = createConversationEvent({
|
|
697
|
+
platform: "slack",
|
|
698
|
+
address: eventForRun.address,
|
|
379
699
|
type: eventForRun.type,
|
|
380
700
|
conversationId,
|
|
381
701
|
conversationKind: eventForRun.conversationKind,
|
|
@@ -384,78 +704,140 @@ export class SlackMessagingBot {
|
|
|
384
704
|
thread_ts: eventForRun.thread_ts,
|
|
385
705
|
user: eventForRun.user,
|
|
386
706
|
text: eventForRun.text,
|
|
387
|
-
attachments: eventForRun.attachments
|
|
388
|
-
original: attachment.name,
|
|
389
|
-
localPath: attachment.localPath,
|
|
390
|
-
})),
|
|
707
|
+
attachments: eventForRun.attachments,
|
|
391
708
|
sessionKey: eventForRun.sessionKey,
|
|
392
|
-
};
|
|
709
|
+
});
|
|
393
710
|
const context = createSlackAdapters(slackEvent, this, {
|
|
394
711
|
initialMessageTs: eventPlan.initialMessageTs,
|
|
395
|
-
replyMode:
|
|
396
|
-
?.replyMode ?? "top-level",
|
|
712
|
+
replyMode: this.resolveReplyMode(eventForRun.address),
|
|
397
713
|
});
|
|
398
714
|
return this.handler.handleEvent(eventForRun, this, context);
|
|
399
|
-
}
|
|
715
|
+
};
|
|
716
|
+
const runQueue = this.getQueue(runQueueKey);
|
|
717
|
+
if (!runQueue.enqueue(run)) {
|
|
718
|
+
await runQueue.close();
|
|
719
|
+
await run();
|
|
720
|
+
}
|
|
400
721
|
});
|
|
401
|
-
return true;
|
|
402
722
|
}
|
|
403
|
-
// ==========================================================================
|
|
404
|
-
// Private - Event Handlers
|
|
405
|
-
// ==========================================================================
|
|
406
723
|
getQueue(channelId) {
|
|
407
724
|
let queue = this.queues.get(channelId);
|
|
408
725
|
if (!queue) {
|
|
409
726
|
queue = new MessagingEventQueue("Slack");
|
|
727
|
+
if (this.stopped)
|
|
728
|
+
void queue.close();
|
|
410
729
|
this.queues.set(channelId, queue);
|
|
411
730
|
}
|
|
412
731
|
return queue;
|
|
413
732
|
}
|
|
414
|
-
resolveQueueKey(conversationId, sessionKey) {
|
|
733
|
+
async resolveQueueKey(conversationId, sessionKey) {
|
|
415
734
|
if (!isSlackThreadSessionKey(sessionKey))
|
|
416
735
|
return sessionKey;
|
|
417
|
-
if (this.handler.isRunning(sessionKey))
|
|
736
|
+
if (this.handler.isRunning(createOfficeAddress("slack", conversationId), sessionKey)) {
|
|
418
737
|
return sessionKey;
|
|
419
|
-
|
|
738
|
+
}
|
|
739
|
+
return (await this.hasKnownThreadSession(conversationId, sessionKey))
|
|
740
|
+
? sessionKey
|
|
741
|
+
: conversationId;
|
|
742
|
+
}
|
|
743
|
+
isTaskThread(channel, root) {
|
|
744
|
+
return readTaskRoots(this.office(channel)).has(root);
|
|
745
|
+
}
|
|
746
|
+
hasRunningTaskThread(channel) {
|
|
747
|
+
const roots = readTaskRoots(this.office(channel));
|
|
748
|
+
if (!roots.size)
|
|
749
|
+
return false;
|
|
750
|
+
return this.handler
|
|
751
|
+
.getRunningSessions()
|
|
752
|
+
.some((s) => s.address.platform === "slack" &&
|
|
753
|
+
s.address.conversationId === channel &&
|
|
754
|
+
[...roots.keys()].some((root) => resolveSlackSessionKey(channel, root) === s.sessionKey));
|
|
420
755
|
}
|
|
421
756
|
hasKnownThreadSession(conversationId, sessionKey) {
|
|
422
757
|
return hasMaterializedChatSession({
|
|
423
|
-
|
|
758
|
+
office: this.office(conversationId),
|
|
424
759
|
sessionKey,
|
|
425
760
|
});
|
|
426
761
|
}
|
|
762
|
+
channelKindFor(channelId) {
|
|
763
|
+
if (channelId.startsWith("D"))
|
|
764
|
+
return "im";
|
|
765
|
+
const channel = this.channels.get(channelId);
|
|
766
|
+
if (!channel)
|
|
767
|
+
return undefined;
|
|
768
|
+
if (channel.name.startsWith("DM:"))
|
|
769
|
+
return "im";
|
|
770
|
+
if (channel.isExternallyShared)
|
|
771
|
+
return "external";
|
|
772
|
+
if (channel.isPrivate === true)
|
|
773
|
+
return "private_channel";
|
|
774
|
+
if (channel.isPrivate === false)
|
|
775
|
+
return "public_channel";
|
|
776
|
+
return undefined;
|
|
777
|
+
}
|
|
778
|
+
backfillChannelKinds() {
|
|
779
|
+
let recorded = 0;
|
|
780
|
+
for (const record of listRegisteredOffices(this.workspace.stateDir)) {
|
|
781
|
+
if (record.platform !== "slack")
|
|
782
|
+
continue;
|
|
783
|
+
const kind = this.channelKindFor(record.conversationId);
|
|
784
|
+
if (!kind)
|
|
785
|
+
continue;
|
|
786
|
+
try {
|
|
787
|
+
const office = this.workspace.office(record);
|
|
788
|
+
if (readPlatformChannelKind(office) === kind)
|
|
789
|
+
continue;
|
|
790
|
+
recordPlatformChannelKind(office, kind);
|
|
791
|
+
recorded++;
|
|
792
|
+
}
|
|
793
|
+
catch (err) {
|
|
794
|
+
log.logWarning("Failed to backfill Slack channel kind", String(err));
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (recorded > 0)
|
|
798
|
+
log.logInfo(`Recorded channel kind for ${recorded} Slack offices`);
|
|
799
|
+
}
|
|
427
800
|
processSlackMessageIntake(options) {
|
|
428
|
-
|
|
801
|
+
const kind = this.channelKindFor(options.event.address.conversationId);
|
|
802
|
+
if (kind) {
|
|
803
|
+
try {
|
|
804
|
+
recordPlatformChannelKind(this.workspace.office(options.event.address), kind);
|
|
805
|
+
}
|
|
806
|
+
catch (err) {
|
|
807
|
+
log.logWarning("Failed to record Slack channel kind", String(err));
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
const absorbAttachmentFailure = () => {
|
|
811
|
+
void options.attachmentsPromise.catch((err) => {
|
|
812
|
+
log.logWarning("Failed to log Slack message", String(err));
|
|
813
|
+
});
|
|
814
|
+
};
|
|
815
|
+
return processMessageIntake({
|
|
429
816
|
eventBase: options.event,
|
|
430
|
-
|
|
431
|
-
|
|
817
|
+
addressed: options.addressed,
|
|
818
|
+
magicWord: {
|
|
819
|
+
addressed: options.magicWordAddressed ?? options.addressed,
|
|
820
|
+
scopeFallback: "top-level",
|
|
821
|
+
},
|
|
822
|
+
busyPolicy: "queue",
|
|
432
823
|
logEntryBase: {},
|
|
433
824
|
processAttachments: () => options.attachmentsPromise,
|
|
434
825
|
queueKey: options.queueKey,
|
|
435
|
-
enqueue: (queueKey, work) => this.getQueue(queueKey).
|
|
826
|
+
enqueue: (queueKey, work) => this.getQueue(queueKey).admit(work),
|
|
827
|
+
react: (emoji) => this.addReaction(options.event.channel, options.event.ts, emoji),
|
|
436
828
|
handler: this.handler,
|
|
437
829
|
bot: this,
|
|
438
830
|
createContext: (event) => this.createContext(event),
|
|
439
|
-
onNotTriggered: options.onNotTriggered,
|
|
440
831
|
deferAttachmentsUntilRun: true,
|
|
832
|
+
}).then((outcome) => {
|
|
833
|
+
if (outcome !== "enqueued")
|
|
834
|
+
absorbAttachmentFailure();
|
|
835
|
+
}, (err) => {
|
|
836
|
+
log.logWarning("Slack message intake failed", String(err));
|
|
837
|
+
absorbAttachmentFailure();
|
|
441
838
|
});
|
|
442
839
|
}
|
|
443
|
-
|
|
444
|
-
const blocks = [
|
|
445
|
-
{
|
|
446
|
-
type: "section",
|
|
447
|
-
text: {
|
|
448
|
-
type: "mrkdwn",
|
|
449
|
-
text: `*${PRODUCT_NAME}*\nStart a new task or check on running work.`,
|
|
450
|
-
},
|
|
451
|
-
accessory: {
|
|
452
|
-
type: "image",
|
|
453
|
-
image_url: "https://media1.tenor.com/m/lfDATg4Bhc0AAAAC/happy-cat.gif",
|
|
454
|
-
alt_text: PRODUCT_NAME,
|
|
455
|
-
},
|
|
456
|
-
},
|
|
457
|
-
];
|
|
458
|
-
// --- Running tasks ---
|
|
840
|
+
appendRunningTasks(blocks) {
|
|
459
841
|
const runningSessions = this.handler.getRunningSessions();
|
|
460
842
|
blocks.push({ type: "divider" }, {
|
|
461
843
|
type: "header",
|
|
@@ -470,61 +852,51 @@ export class SlackMessagingBot {
|
|
|
470
852
|
type: "context",
|
|
471
853
|
elements: [{ type: "mrkdwn", text: "_No tasks running right now._" }],
|
|
472
854
|
});
|
|
855
|
+
return;
|
|
473
856
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
857
|
+
const stuckThresholdMs = 10 * 60 * 1000;
|
|
858
|
+
for (const session of runningSessions) {
|
|
859
|
+
const channelId = conversationIdOf(session.sessionKey);
|
|
860
|
+
const channelName = this.channels.get(channelId)?.name;
|
|
861
|
+
const elapsed = Math.floor((Date.now() - session.startedAt) / 60000);
|
|
862
|
+
const elapsedStr = elapsed < 1 ? "<1 min" : `${elapsed} min`;
|
|
863
|
+
const lastActivity = session.lastActivityAt ? Date.now() - session.lastActivityAt : 0;
|
|
864
|
+
const isStuck = lastActivity > stuckThresholdMs;
|
|
865
|
+
let statusLine = `${isStuck ? "_stuck_" : "_running_"} · ${elapsedStr}`;
|
|
866
|
+
if (session.currentTool)
|
|
867
|
+
statusLine += ` · ${session.currentTool}`;
|
|
868
|
+
if (isStuck)
|
|
869
|
+
statusLine += ` · idle ${Math.floor(lastActivity / 60000)}m`;
|
|
870
|
+
blocks.push({
|
|
871
|
+
type: "context",
|
|
872
|
+
elements: [
|
|
873
|
+
{
|
|
874
|
+
type: "mrkdwn",
|
|
875
|
+
text: `*${channelName ? `#${channelName}` : channelId}* · ${statusLine}`,
|
|
876
|
+
},
|
|
877
|
+
],
|
|
878
|
+
});
|
|
879
|
+
if (isStuck) {
|
|
497
880
|
blocks.push({
|
|
498
881
|
type: "context",
|
|
499
882
|
elements: [
|
|
883
|
+
{ type: "mrkdwn", text: " " },
|
|
500
884
|
{
|
|
501
|
-
type: "
|
|
502
|
-
text:
|
|
885
|
+
type: "button",
|
|
886
|
+
text: { type: "plain_text", text: "Force Stop", emoji: true },
|
|
887
|
+
action_id: `force_stop_${session.sessionKey.replace(/:/g, "_")}`,
|
|
888
|
+
value: session.sessionKey,
|
|
889
|
+
style: "danger",
|
|
503
890
|
},
|
|
504
891
|
],
|
|
505
892
|
});
|
|
506
|
-
// Add Force Stop button as separate element if stuck
|
|
507
|
-
if (isStuck) {
|
|
508
|
-
blocks.push({
|
|
509
|
-
type: "context",
|
|
510
|
-
elements: [
|
|
511
|
-
{
|
|
512
|
-
type: "mrkdwn",
|
|
513
|
-
text: " ",
|
|
514
|
-
},
|
|
515
|
-
{
|
|
516
|
-
type: "button",
|
|
517
|
-
text: { type: "plain_text", text: "Force Stop", emoji: true },
|
|
518
|
-
action_id: `force_stop_${session.sessionKey.replace(/:/g, "_")}`,
|
|
519
|
-
style: "danger",
|
|
520
|
-
},
|
|
521
|
-
],
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
893
|
}
|
|
525
894
|
}
|
|
526
|
-
|
|
527
|
-
|
|
895
|
+
}
|
|
896
|
+
appendScheduledJobs(blocks, dmChannelId) {
|
|
897
|
+
const periodicEvents = dmChannelId && this.eventScheduler
|
|
898
|
+
? this.eventScheduler.periodicEvents(createOfficeAddress("slack", dmChannelId))
|
|
899
|
+
: [];
|
|
528
900
|
blocks.push({ type: "divider" }, {
|
|
529
901
|
type: "header",
|
|
530
902
|
text: {
|
|
@@ -538,34 +910,46 @@ export class SlackMessagingBot {
|
|
|
538
910
|
type: "context",
|
|
539
911
|
elements: [{ type: "mrkdwn", text: "_No scheduled jobs._" }],
|
|
540
912
|
});
|
|
913
|
+
return;
|
|
541
914
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
:
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
:
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
text: {
|
|
562
|
-
type: "mrkdwn",
|
|
563
|
-
text: `*${ev.text}*\n└ \`${ev.schedule}\` · ${channelLabel} · Next: ${nextStr}`,
|
|
564
|
-
},
|
|
565
|
-
});
|
|
566
|
-
}
|
|
915
|
+
for (const event of periodicEvents) {
|
|
916
|
+
const channel = event.platform === "slack" ? this.channels.get(event.conversationId) : undefined;
|
|
917
|
+
const channelId = channel ? `#${channel.name}` : event.conversationId;
|
|
918
|
+
const channelLabel = `${event.platform}:${channelId}`;
|
|
919
|
+
const nextRun = event.nextRun
|
|
920
|
+
? new Date(event.nextRun).toLocaleString("en-US", {
|
|
921
|
+
month: "short",
|
|
922
|
+
day: "numeric",
|
|
923
|
+
hour: "2-digit",
|
|
924
|
+
minute: "2-digit",
|
|
925
|
+
})
|
|
926
|
+
: "—";
|
|
927
|
+
blocks.push({
|
|
928
|
+
type: "section",
|
|
929
|
+
text: {
|
|
930
|
+
type: "mrkdwn",
|
|
931
|
+
text: `*${event.text}*\n└ \`${event.schedule}\` · ${channelLabel} · Next: ${nextRun}`,
|
|
932
|
+
},
|
|
933
|
+
});
|
|
567
934
|
}
|
|
568
|
-
|
|
935
|
+
}
|
|
936
|
+
buildHomeView(dmChannelId) {
|
|
937
|
+
const blocks = [
|
|
938
|
+
{
|
|
939
|
+
type: "section",
|
|
940
|
+
text: {
|
|
941
|
+
type: "mrkdwn",
|
|
942
|
+
text: `*${PRODUCT_NAME}*\nStart a new task or check on running work.`,
|
|
943
|
+
},
|
|
944
|
+
accessory: {
|
|
945
|
+
type: "image",
|
|
946
|
+
image_url: "https://media1.tenor.com/m/lfDATg4Bhc0AAAAC/happy-cat.gif",
|
|
947
|
+
alt_text: PRODUCT_NAME,
|
|
948
|
+
},
|
|
949
|
+
},
|
|
950
|
+
];
|
|
951
|
+
this.appendRunningTasks(blocks);
|
|
952
|
+
this.appendScheduledJobs(blocks, dmChannelId);
|
|
569
953
|
blocks.push({ type: "divider" }, {
|
|
570
954
|
type: "context",
|
|
571
955
|
elements: [
|
|
@@ -574,58 +958,38 @@ export class SlackMessagingBot {
|
|
|
574
958
|
});
|
|
575
959
|
return { type: "home", blocks: blocks };
|
|
576
960
|
}
|
|
577
|
-
|
|
578
|
-
const
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
});
|
|
583
|
-
if (directTarget)
|
|
584
|
-
return directTarget;
|
|
585
|
-
if (threadTs)
|
|
586
|
-
return null;
|
|
587
|
-
return resolveOnlyScopedStopTarget(this.handler, channelId);
|
|
588
|
-
}
|
|
589
|
-
isStopText(text) {
|
|
590
|
-
const normalized = text.trim().toLowerCase();
|
|
591
|
-
return normalized === "stop" || normalized === "/stop";
|
|
592
|
-
}
|
|
593
|
-
createCommandAdapters(conversationId, userId, userName, text, ts, options = {}) {
|
|
594
|
-
const message = {
|
|
961
|
+
createCommandAdapters(input) {
|
|
962
|
+
const { conversationId, userId, userName, text, ts, ...options } = input;
|
|
963
|
+
const message = createConversationMessage({
|
|
964
|
+
platform: "slack",
|
|
965
|
+
conversationId,
|
|
595
966
|
id: ts,
|
|
596
|
-
sessionKey: conversationId,
|
|
967
|
+
sessionKey: options.sessionKey ?? conversationId,
|
|
597
968
|
conversationKind: options.ephemeralChannelId ? "shared" : "direct",
|
|
598
969
|
userId,
|
|
599
970
|
userName,
|
|
600
971
|
text,
|
|
601
972
|
attachments: [],
|
|
602
|
-
};
|
|
603
|
-
const respond = async (responseText) => {
|
|
604
|
-
if (options.ephemeralChannelId) {
|
|
605
|
-
await this.postEphemeral(options.ephemeralChannelId, userId, responseText, options.threadTs);
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
|
-
const messageTs = await this.postMessage(conversationId, responseText);
|
|
609
|
-
this.logBotResponse(conversationId, responseText, messageTs);
|
|
610
|
-
};
|
|
611
|
-
const respondMuted = async (responseText) => {
|
|
612
|
-
const blocks = [buildMrkdwnContextBlock(responseText)];
|
|
973
|
+
});
|
|
974
|
+
const respond = async (responseText, blocks) => {
|
|
613
975
|
if (options.ephemeralChannelId) {
|
|
614
|
-
await this.
|
|
976
|
+
await this.postEphemeral(options.ephemeralChannelId, userId, responseText, options.threadTs, blocks);
|
|
615
977
|
return;
|
|
616
978
|
}
|
|
617
|
-
const messageTs =
|
|
979
|
+
const messageTs = blocks
|
|
980
|
+
? await this.postMessageBlocks(conversationId, responseText, blocks)
|
|
981
|
+
: await this.postMessage(conversationId, responseText);
|
|
618
982
|
this.logBotResponse(conversationId, responseText, messageTs);
|
|
619
983
|
};
|
|
620
984
|
const responder = {
|
|
621
985
|
respond,
|
|
622
|
-
replaceResponse: respond,
|
|
986
|
+
replaceResponse: (responseText) => respond(responseText),
|
|
623
987
|
respondDiagnostic: async (responseText, responseOptions) => {
|
|
624
988
|
if (responseOptions?.style === "muted") {
|
|
625
|
-
await
|
|
989
|
+
await respond(responseText, [buildMrkdwnContextBlock(responseText)]);
|
|
626
990
|
return;
|
|
627
991
|
}
|
|
628
|
-
await respond(responseOptions?.style === "error" ?
|
|
992
|
+
await respond(responseOptions?.style === "error" ? italicizeLines(responseText) : responseText);
|
|
629
993
|
},
|
|
630
994
|
respondToolResult: async (result) => {
|
|
631
995
|
const duration = (result.durationMs / 1000).toFixed(1);
|
|
@@ -639,6 +1003,7 @@ export class SlackMessagingBot {
|
|
|
639
1003
|
deleteResponse: async () => { },
|
|
640
1004
|
};
|
|
641
1005
|
return {
|
|
1006
|
+
address: message.address,
|
|
642
1007
|
message,
|
|
643
1008
|
responder,
|
|
644
1009
|
platform: this.getMessagingInfo(),
|
|
@@ -664,66 +1029,42 @@ export class SlackMessagingBot {
|
|
|
664
1029
|
text: commandText,
|
|
665
1030
|
attachments: [],
|
|
666
1031
|
isMessagingBot: false,
|
|
667
|
-
|
|
1032
|
+
threadTs: threadTs || undefined,
|
|
668
1033
|
});
|
|
669
|
-
const event = {
|
|
670
|
-
|
|
1034
|
+
const event = createConversationEvent({
|
|
1035
|
+
platform: "slack",
|
|
671
1036
|
conversationId,
|
|
1037
|
+
type: options.type ?? (isDirectMessage ? "dm" : "mention"),
|
|
672
1038
|
conversationKind: isDirectMessage ? "direct" : "shared",
|
|
673
1039
|
ts: eventTs,
|
|
674
1040
|
user: payload.user_id,
|
|
675
1041
|
text: commandText,
|
|
676
1042
|
attachments: [],
|
|
677
|
-
|
|
1043
|
+
thread_ts: threadTs || undefined,
|
|
678
1044
|
sessionKey,
|
|
679
|
-
};
|
|
680
|
-
const context = this.createCommandAdapters(conversationId, payload.user_id, userName, commandText, eventTs, isDirectMessage ? { threadTs } : { ephemeralChannelId: conversationId, threadTs });
|
|
681
|
-
return { event, context };
|
|
682
|
-
}
|
|
683
|
-
async routeSlashLoginCommand(payload) {
|
|
684
|
-
const { event, context } = this.buildSlashCommandEvent(payload, {
|
|
685
|
-
type: payload.channel_id.startsWith("D") ? "dm" : "private_command",
|
|
686
|
-
includeText: true,
|
|
687
1045
|
});
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
const conversationId = payload.channel_id;
|
|
692
|
-
if (!conversationId.startsWith("D")) {
|
|
693
|
-
await this.postEphemeral(conversationId, payload.user_id, `為了避免誤清除共享上下文,${payload.command} 目前只能在與 ${PRODUCT_NAME} 的私訊中使用。`);
|
|
694
|
-
return;
|
|
695
|
-
}
|
|
696
|
-
const createdAt = new Date();
|
|
697
|
-
const eventTs = (createdAt.getTime() / 1000).toFixed(6);
|
|
698
|
-
const userName = payload.user_name ?? this.getUser(payload.user_id)?.userName;
|
|
699
|
-
this.logToFile(conversationId, {
|
|
700
|
-
date: createdAt.toISOString(),
|
|
701
|
-
ts: eventTs,
|
|
702
|
-
user: payload.user_id,
|
|
1046
|
+
const context = this.createCommandAdapters({
|
|
1047
|
+
conversationId,
|
|
1048
|
+
userId: payload.user_id,
|
|
703
1049
|
userName,
|
|
704
|
-
text:
|
|
705
|
-
|
|
706
|
-
|
|
1050
|
+
text: commandText,
|
|
1051
|
+
ts: eventTs,
|
|
1052
|
+
ephemeralChannelId: isDirectMessage ? undefined : conversationId,
|
|
1053
|
+
threadTs,
|
|
1054
|
+
sessionKey,
|
|
707
1055
|
});
|
|
708
|
-
|
|
709
|
-
start: async () => { },
|
|
710
|
-
postMessage: async (_channel, text) => this.postMessage(conversationId, text),
|
|
711
|
-
updateMessage: async (channel, ts, text) => this.updateMessage(channel, ts, text),
|
|
712
|
-
enqueueEvent: (event) => this.enqueueEvent(event),
|
|
713
|
-
getMessagingInfo: () => this.getMessagingInfo(),
|
|
714
|
-
};
|
|
715
|
-
await this.handler.handleNewCommand(conversationId, conversationId, commandMessagingBot);
|
|
716
|
-
}
|
|
717
|
-
async routeSlashModelCommand(payload) {
|
|
718
|
-
const { event, context } = this.buildSlashCommandEvent(payload, { includeText: true });
|
|
719
|
-
await this.handler.handleEvent(event, this, context);
|
|
720
|
-
}
|
|
721
|
-
async routeSlashSessionCommand(payload) {
|
|
722
|
-
const { event, context } = this.buildSlashCommandEvent(payload, { thread: true });
|
|
723
|
-
await this.handler.handleEvent(event, this, context);
|
|
1056
|
+
return { event, context };
|
|
724
1057
|
}
|
|
725
|
-
async
|
|
726
|
-
const { event, context } = this.buildSlashCommandEvent(payload, {
|
|
1058
|
+
async routeSlashCommand(route, payload) {
|
|
1059
|
+
const { event, context } = this.buildSlashCommandEvent(payload, {
|
|
1060
|
+
includeText: route.includeText,
|
|
1061
|
+
thread: route.thread,
|
|
1062
|
+
type: route.privateCommand
|
|
1063
|
+
? payload.channel_id.startsWith("D")
|
|
1064
|
+
? "dm"
|
|
1065
|
+
: "private_command"
|
|
1066
|
+
: undefined,
|
|
1067
|
+
});
|
|
727
1068
|
await this.handler.handleEvent(event, this, context);
|
|
728
1069
|
}
|
|
729
1070
|
setupEventHandlers() {
|
|
@@ -736,38 +1077,57 @@ export class SlackMessagingBot {
|
|
|
736
1077
|
this.socketClient.on("unable_to_socket_mode_start", (err) => {
|
|
737
1078
|
log.logWarning("Slack socket unable_to_start", err ? String(err) : "");
|
|
738
1079
|
});
|
|
739
|
-
this.socketClient.on(
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
1080
|
+
const on = (event, handle) => this.socketClient.on(event, (payload) => this.intake.run(() => handle(withLoggedAck(event, payload))));
|
|
1081
|
+
on("app_mention", (payload) => this.handleAppMention(payload));
|
|
1082
|
+
on("message", (payload) => this.handleMessageEvent(payload));
|
|
1083
|
+
on("slash_commands", (payload) => this.handleSlashCommand(payload));
|
|
1084
|
+
on("app_home_opened", (payload) => this.handleAppHomeOpened(payload));
|
|
1085
|
+
on("assistant_thread_started", (payload) => this.handleAssistantThreadStarted(payload));
|
|
1086
|
+
on("assistant_thread_context_changed", (payload) => this.handleAgentContextChangedEvent(payload));
|
|
1087
|
+
on("app_context_changed", (payload) => this.handleAgentContextChangedEvent(payload));
|
|
1088
|
+
const blockAction = ({ body, ack }) => this.handleBlockAction({ body: body, ack });
|
|
1089
|
+
on("block_actions", blockAction);
|
|
1090
|
+
on("interactive", blockAction);
|
|
1091
|
+
}
|
|
1092
|
+
isRedelivery(kind, channel, ts) {
|
|
1093
|
+
const key = `${kind}:${channel}:${ts}`;
|
|
1094
|
+
if (this.recentMessageKeys.has(key))
|
|
1095
|
+
return true;
|
|
1096
|
+
this.recentMessageKeys.add(key);
|
|
1097
|
+
if (this.recentMessageKeys.size > RECENT_MESSAGE_KEYS) {
|
|
1098
|
+
const oldest = this.recentMessageKeys.values().next().value;
|
|
1099
|
+
if (oldest !== undefined)
|
|
1100
|
+
this.recentMessageKeys.delete(oldest);
|
|
1101
|
+
}
|
|
1102
|
+
return false;
|
|
1103
|
+
}
|
|
1104
|
+
async handleAppMention({ event, ack }) {
|
|
747
1105
|
const e = event;
|
|
748
|
-
// Skip DMs (handled by message event)
|
|
749
1106
|
if (e.channel.startsWith("D")) {
|
|
750
1107
|
ack();
|
|
751
1108
|
return;
|
|
752
1109
|
}
|
|
753
|
-
|
|
754
|
-
|
|
1110
|
+
if (this.isRedelivery("app_mention", e.channel, e.ts)) {
|
|
1111
|
+
log.logInfo(`[${e.channel}] Ignoring redelivered mention ${e.ts}`);
|
|
1112
|
+
await ack();
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
755
1115
|
const sessionKey = resolveSlackSessionKey(e.channel, e.thread_ts);
|
|
756
1116
|
const mentionText = this.stripOwnMention(e.text);
|
|
757
|
-
const slackEvent = {
|
|
758
|
-
|
|
1117
|
+
const slackEvent = createConversationEvent({
|
|
1118
|
+
platform: "slack",
|
|
759
1119
|
conversationId: e.channel,
|
|
1120
|
+
type: "mention",
|
|
760
1121
|
conversationKind: "shared",
|
|
761
|
-
channel: e.channel,
|
|
762
1122
|
ts: e.ts,
|
|
763
1123
|
thread_ts: e.thread_ts,
|
|
764
1124
|
user: e.user,
|
|
765
1125
|
text: mentionText || "Please respond to the recent conversation context.",
|
|
766
|
-
files: e.files,
|
|
767
1126
|
sessionKey,
|
|
768
|
-
|
|
1127
|
+
channel: e.channel,
|
|
1128
|
+
files: e.files,
|
|
1129
|
+
});
|
|
769
1130
|
const attachmentsPromise = this.logUserMessage(slackEvent);
|
|
770
|
-
// Only trigger processing for messages AFTER startup (not replayed old messages)
|
|
771
1131
|
if (this.startupTs && e.ts < this.startupTs) {
|
|
772
1132
|
log.logInfo(`[${e.channel}] Logged old message (pre-startup), not triggering: ${slackEvent.text.substring(0, 30)}`);
|
|
773
1133
|
void attachmentsPromise.catch((err) => {
|
|
@@ -776,90 +1136,161 @@ export class SlackMessagingBot {
|
|
|
776
1136
|
ack();
|
|
777
1137
|
return;
|
|
778
1138
|
}
|
|
779
|
-
|
|
780
|
-
if (this.isStopText(slackEvent.text)) {
|
|
781
|
-
const stopTarget = this.resolveStopTarget(e.channel, e.thread_ts);
|
|
782
|
-
if (stopTarget) {
|
|
783
|
-
this.handler.handleStop(stopTarget, e.channel, this);
|
|
784
|
-
}
|
|
785
|
-
else {
|
|
786
|
-
this.postMessage(e.channel, formatNothingRunning("slack"));
|
|
787
|
-
}
|
|
788
|
-
void attachmentsPromise.catch((err) => {
|
|
789
|
-
log.logWarning("Failed to log Slack message", String(err));
|
|
790
|
-
});
|
|
791
|
-
ack();
|
|
792
|
-
return;
|
|
793
|
-
}
|
|
794
|
-
this.processSlackMessageIntake({
|
|
1139
|
+
const intake = this.processSlackMessageIntake({
|
|
795
1140
|
event: slackEvent,
|
|
796
1141
|
attachmentsPromise,
|
|
797
|
-
queueKey: this.resolveQueueKey(e.channel, sessionKey),
|
|
798
|
-
|
|
1142
|
+
queueKey: await this.resolveQueueKey(e.channel, sessionKey),
|
|
1143
|
+
addressed: true,
|
|
799
1144
|
});
|
|
800
1145
|
ack();
|
|
1146
|
+
await intake;
|
|
801
1147
|
}
|
|
802
|
-
|
|
803
|
-
const
|
|
804
|
-
const
|
|
805
|
-
|
|
806
|
-
const isOwnMessagingBotMessage = (!!e.user && e.user === this.botUserId) || (!!this.botId && e.bot_id === this.botId);
|
|
807
|
-
if (isOwnMessagingBotMessage) {
|
|
1148
|
+
admitHumanMessage(event, ack) {
|
|
1149
|
+
const hasSlackContent = hasMessageContent(event);
|
|
1150
|
+
const isOwnMessage = event.user === this.botUserId || (!!this.botId && event.bot_id === this.botId);
|
|
1151
|
+
if (isOwnMessage) {
|
|
808
1152
|
ack();
|
|
809
|
-
return;
|
|
1153
|
+
return false;
|
|
810
1154
|
}
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
1155
|
+
const authorIsKnownHuman = !!event.user && this.users.get(event.user)?.isBot === false;
|
|
1156
|
+
const isExternalMessage = event.subtype === "bot_message" || (!!event.bot_id && !authorIsKnownHuman);
|
|
1157
|
+
if (isExternalMessage) {
|
|
1158
|
+
const supportedSubtype = BOT_MESSAGE_SUBTYPES.has(event.subtype);
|
|
1159
|
+
if (supportedSubtype && hasSlackContent) {
|
|
1160
|
+
void this.logExternalMessagingBotMessage(event).catch((err) => {
|
|
1161
|
+
log.logWarning("Failed to log Slack bot message", String(err));
|
|
1162
|
+
});
|
|
816
1163
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1164
|
+
ack();
|
|
1165
|
+
return false;
|
|
1166
|
+
}
|
|
1167
|
+
const isSupportedUserMessage = !!event.user && USER_MESSAGE_SUBTYPES.has(event.subtype) && hasSlackContent;
|
|
1168
|
+
if (isSupportedUserMessage)
|
|
1169
|
+
return true;
|
|
1170
|
+
ack();
|
|
1171
|
+
return false;
|
|
1172
|
+
}
|
|
1173
|
+
async deliverTaskUpdate(event, attachmentsPromise) {
|
|
1174
|
+
if (matchMagicWord(event.text) === "stop" || event.text.startsWith("/"))
|
|
1175
|
+
return false;
|
|
1176
|
+
event.attachments = await attachmentsPromise;
|
|
1177
|
+
const context = this.createContext(event);
|
|
1178
|
+
const post = (text) => event.thread_ts
|
|
1179
|
+
? this.postInThread(event.channel, event.thread_ts, text)
|
|
1180
|
+
: this.postMessage(event.channel, text);
|
|
1181
|
+
try {
|
|
1182
|
+
const tasks = await context.responder.getTaskStatus(event.thread_ts ? event.sessionKey : undefined);
|
|
1183
|
+
if (!tasks.length)
|
|
1184
|
+
return false;
|
|
1185
|
+
const active = tasks.filter((t) => t.status === "running" || t.status === "stopping");
|
|
1186
|
+
const user = this.users.get(event.user);
|
|
1187
|
+
const intent = event.attachments?.length
|
|
1188
|
+
? event.thread_ts
|
|
1189
|
+
? "steer"
|
|
1190
|
+
: "request"
|
|
1191
|
+
: await classifyTaskIntent(buildTaskIntentState(this.conversationDir(event.channel), event, {
|
|
1192
|
+
speaker: user?.displayName ?? user?.userName,
|
|
1193
|
+
tasks,
|
|
1194
|
+
resolveName: (id) => this.users.get(id)?.displayName ?? this.users.get(id)?.userName,
|
|
1195
|
+
botUserId: this.botUserId,
|
|
1196
|
+
}), event.text, { conversationId: event.channel, inTaskThread: !!event.thread_ts });
|
|
1197
|
+
if (intent === "status") {
|
|
1198
|
+
const selection = event.thread_ts ? tasks : active.length ? active : tasks.slice(0, 1);
|
|
1199
|
+
const text = selection.length > 1
|
|
1200
|
+
? "目前有多個任務在處理,請到你想查詢的任務對話串詢問,避免弄錯。"
|
|
1201
|
+
: formatTaskStatus(selection);
|
|
1202
|
+
const ts = await post(text);
|
|
1203
|
+
this.logBotResponse(event.channel, text, ts, event.thread_ts);
|
|
1204
|
+
return true;
|
|
1205
|
+
}
|
|
1206
|
+
if (intent === "steer") {
|
|
1207
|
+
const target = event.thread_ts
|
|
1208
|
+
? context.message
|
|
1209
|
+
: active.length === 1
|
|
1210
|
+
? {
|
|
1211
|
+
...context.message,
|
|
1212
|
+
sessionKey: active[0].sessionKey,
|
|
1213
|
+
threadTs: active[0].threadTs,
|
|
1214
|
+
}
|
|
1215
|
+
: undefined;
|
|
1216
|
+
if (target && (await this.handler.steer?.(target))) {
|
|
1217
|
+
const text = "收到補充,會在下一個處理步驟納入;目前的操作不會立即中斷。";
|
|
1218
|
+
const ts = await post(text);
|
|
1219
|
+
this.logBotResponse(event.channel, text, ts, event.thread_ts);
|
|
1220
|
+
return true;
|
|
1221
|
+
}
|
|
820
1222
|
}
|
|
821
|
-
|
|
822
|
-
|
|
1223
|
+
}
|
|
1224
|
+
catch (error) {
|
|
1225
|
+
await post(error instanceof Error ? error.message : "Could not deliver task update.");
|
|
1226
|
+
return true;
|
|
1227
|
+
}
|
|
1228
|
+
return false;
|
|
1229
|
+
}
|
|
1230
|
+
async evaluateJevAddressed(event) {
|
|
1231
|
+
if (!event.text.trim() || matchMagicWord(event.text) === "stop")
|
|
1232
|
+
return false;
|
|
1233
|
+
try {
|
|
1234
|
+
const user = this.users.get(event.user);
|
|
1235
|
+
const state = buildAutoReplyState(this.conversationDir(event.channel), event, {
|
|
1236
|
+
speaker: user?.displayName ?? user?.userName,
|
|
1237
|
+
resolveName: (id) => this.users.get(id)?.displayName ?? this.users.get(id)?.userName,
|
|
1238
|
+
botUserId: this.botUserId,
|
|
823
1239
|
});
|
|
824
|
-
|
|
825
|
-
|
|
1240
|
+
const result = await evaluateWithJev(state, { addressed: { type: "boolean", instructions: JEV_ADDRESSED_INSTRUCTIONS } }, { caller: "slack_auto_reply" });
|
|
1241
|
+
const probability = result.answers.addressed.probability;
|
|
1242
|
+
log.logInfo(`[${event.channel}] jev auto-reply: probability=${probability.toFixed(2)} addressed=${probability > 0.5} text="${event.text.slice(0, 80)}"`);
|
|
1243
|
+
return probability > 0.5;
|
|
826
1244
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
1245
|
+
catch (err) {
|
|
1246
|
+
if (err instanceof JevNotConfiguredError) {
|
|
1247
|
+
log.logWarning("Slack auto-reply jev mode requires OPENROUTER_API_KEY; treating message as unaddressed", String(err));
|
|
1248
|
+
return false;
|
|
1249
|
+
}
|
|
1250
|
+
reportUserFacingError(err, {
|
|
1251
|
+
domain: "chat_platform",
|
|
1252
|
+
surface: "slack_auto_reply_jev",
|
|
1253
|
+
operation: "evaluate",
|
|
1254
|
+
severity: "warning",
|
|
1255
|
+
context: { conversationId: event.address.conversationId },
|
|
1256
|
+
});
|
|
1257
|
+
return false;
|
|
830
1258
|
}
|
|
831
|
-
|
|
832
|
-
|
|
1259
|
+
}
|
|
1260
|
+
async handleMessageEvent({ event, ack }) {
|
|
1261
|
+
const e = event;
|
|
1262
|
+
if (!this.admitHumanMessage(e, ack))
|
|
833
1263
|
return;
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
ack();
|
|
1264
|
+
if (this.isRedelivery("message", e.channel, e.ts)) {
|
|
1265
|
+
log.logInfo(`[${e.channel}] Ignoring redelivered message ${e.ts}`);
|
|
1266
|
+
await ack();
|
|
837
1267
|
return;
|
|
838
1268
|
}
|
|
839
|
-
const isDM = e.channel_type === "im";
|
|
1269
|
+
const isDM = e.channel_type === "im" || e.channel.startsWith("D");
|
|
840
1270
|
const conversationKind = isDM ? "direct" : "shared";
|
|
841
1271
|
const isMessagingBotMention = e.text?.includes(`<@${this.botUserId}>`);
|
|
842
|
-
// Skip channel @mentions - already handled by app_mention event
|
|
843
1272
|
if (!isDM && isMessagingBotMention) {
|
|
844
1273
|
ack();
|
|
845
1274
|
return;
|
|
846
1275
|
}
|
|
847
|
-
const isThreadReply = !!e.thread_ts;
|
|
848
1276
|
const sessionKey = isDM ? resolveSlackSessionKey(e.channel, e.thread_ts) : undefined;
|
|
849
|
-
|
|
850
|
-
|
|
1277
|
+
if (isDM && e.thread_ts && e.text) {
|
|
1278
|
+
void titleAssistantThread(this.assistantOps(), this.assistantThreads, e.channel, e.thread_ts, this.stripOwnMention(e.text));
|
|
1279
|
+
}
|
|
1280
|
+
const slackEvent = createConversationEvent({
|
|
1281
|
+
platform: "slack",
|
|
851
1282
|
conversationId: e.channel,
|
|
1283
|
+
type: (isDM ? "dm" : "mention"),
|
|
852
1284
|
conversationKind,
|
|
853
|
-
channel: e.channel,
|
|
854
1285
|
ts: e.ts,
|
|
855
1286
|
thread_ts: e.thread_ts,
|
|
856
1287
|
user: e.user,
|
|
857
1288
|
text: this.stripOwnMention(e.text),
|
|
858
|
-
files: e.files,
|
|
859
1289
|
sessionKey,
|
|
860
|
-
|
|
1290
|
+
channel: e.channel,
|
|
1291
|
+
files: e.files,
|
|
1292
|
+
});
|
|
861
1293
|
const attachmentsPromise = this.logUserMessage(slackEvent);
|
|
862
|
-
// Only trigger processing for messages AFTER startup (not replayed old messages)
|
|
863
1294
|
if (this.startupTs && e.ts < this.startupTs) {
|
|
864
1295
|
log.logInfo(`[${e.channel}] Skipping old message (pre-startup): ${slackEvent.text.substring(0, 30)}`);
|
|
865
1296
|
void attachmentsPromise.catch((err) => {
|
|
@@ -868,136 +1299,77 @@ export class SlackMessagingBot {
|
|
|
868
1299
|
ack();
|
|
869
1300
|
return;
|
|
870
1301
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
this.
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
this.postMessage(e.channel, formatNothingRunning("slack"));
|
|
879
|
-
}
|
|
880
|
-
void attachmentsPromise.catch((err) => {
|
|
881
|
-
log.logWarning("Failed to log Slack message", String(err));
|
|
882
|
-
});
|
|
883
|
-
ack();
|
|
884
|
-
return;
|
|
885
|
-
}
|
|
886
|
-
const enqueueTriggered = () => {
|
|
887
|
-
const activeSessionKey = slackEvent.sessionKey ?? resolveSlackSessionKey(e.channel, e.thread_ts);
|
|
888
|
-
// Auto-reply top-level channel messages start with no sessionKey because
|
|
889
|
-
// they are only candidates until the policy allows them. Once triggered,
|
|
890
|
-
// persist the resolved key on the event; otherwise the runtime fallback
|
|
891
|
-
// treats the message ts as a thread session (`channel:ts`) instead of the
|
|
892
|
-
// persistent top-level channel session.
|
|
893
|
-
slackEvent.sessionKey = activeSessionKey;
|
|
894
|
-
this.processSlackMessageIntake({
|
|
895
|
-
event: slackEvent,
|
|
896
|
-
attachmentsPromise,
|
|
897
|
-
queueKey: this.resolveQueueKey(e.channel, activeSessionKey),
|
|
898
|
-
isAutoReplyCandidate: false,
|
|
899
|
-
});
|
|
900
|
-
};
|
|
901
|
-
const logOnly = () => {
|
|
902
|
-
void attachmentsPromise.catch((err) => {
|
|
903
|
-
log.logWarning("Failed to log Slack message", String(err));
|
|
904
|
-
});
|
|
905
|
-
};
|
|
906
|
-
if (isDM) {
|
|
907
|
-
enqueueTriggered();
|
|
908
|
-
ack();
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
if (isThreadReply) {
|
|
912
|
-
logOnly();
|
|
1302
|
+
const activeSessionKey = slackEvent.sessionKey ?? resolveSlackSessionKey(e.channel, e.thread_ts);
|
|
1303
|
+
slackEvent.sessionKey = activeSessionKey;
|
|
1304
|
+
const taskControl = isDM &&
|
|
1305
|
+
(e.thread_ts
|
|
1306
|
+
? this.isTaskThread(e.channel, e.thread_ts)
|
|
1307
|
+
: this.hasRunningTaskThread(e.channel) || isTaskStatusQuestion(slackEvent.text));
|
|
1308
|
+
if (taskControl) {
|
|
913
1309
|
ack();
|
|
914
|
-
|
|
1310
|
+
if (await this.deliverTaskUpdate(slackEvent, attachmentsPromise))
|
|
1311
|
+
return;
|
|
915
1312
|
}
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
|
|
1313
|
+
const autoReplyMode = isDM
|
|
1314
|
+
? "off"
|
|
1315
|
+
: slackConversationAutoReplyMode(this.workspace.office(slackEvent.address));
|
|
1316
|
+
const autoReply = autoReplyMode === "on"
|
|
1317
|
+
? true
|
|
1318
|
+
: autoReplyMode === "jev"
|
|
1319
|
+
? await this.evaluateJevAddressed(slackEvent)
|
|
1320
|
+
: false;
|
|
1321
|
+
const intake = this.processSlackMessageIntake({
|
|
919
1322
|
event: slackEvent,
|
|
920
1323
|
attachmentsPromise,
|
|
921
|
-
queueKey: this.resolveQueueKey(e.channel, activeSessionKey),
|
|
922
|
-
|
|
923
|
-
|
|
1324
|
+
queueKey: await this.resolveQueueKey(e.channel, activeSessionKey),
|
|
1325
|
+
addressed: isDM || autoReply,
|
|
1326
|
+
magicWordAddressed: isDM,
|
|
924
1327
|
});
|
|
925
|
-
|
|
1328
|
+
if (!taskControl)
|
|
1329
|
+
ack();
|
|
1330
|
+
await intake;
|
|
926
1331
|
}
|
|
927
|
-
async handleSlashCommand({ body, ack
|
|
1332
|
+
async handleSlashCommand({ body, ack }) {
|
|
928
1333
|
const payload = body;
|
|
929
1334
|
await ack();
|
|
930
1335
|
if (!payload.command || !payload.channel_id || !payload.user_id) {
|
|
931
1336
|
return;
|
|
932
1337
|
}
|
|
933
1338
|
const { command, text, channel_id, user_id, user_name, thread_ts } = payload;
|
|
934
|
-
|
|
935
|
-
if (
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
user_name,
|
|
942
|
-
});
|
|
943
|
-
}
|
|
944
|
-
else if (command === "/pi-new") {
|
|
945
|
-
handlerPromise = this.routeSlashNewCommand({ command, channel_id, user_id, user_name });
|
|
946
|
-
}
|
|
947
|
-
else if (command === "/pi-session") {
|
|
948
|
-
handlerPromise = this.routeSlashSessionCommand({
|
|
949
|
-
command,
|
|
950
|
-
channel_id,
|
|
951
|
-
user_id,
|
|
952
|
-
user_name,
|
|
953
|
-
thread_ts,
|
|
954
|
-
});
|
|
955
|
-
}
|
|
956
|
-
else if (command === "/pi-model") {
|
|
957
|
-
handlerPromise = this.routeSlashModelCommand({
|
|
958
|
-
command,
|
|
959
|
-
text,
|
|
960
|
-
channel_id,
|
|
961
|
-
user_id,
|
|
962
|
-
user_name,
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
else if (command === "/pi-sandbox" ||
|
|
966
|
-
command === "/pi-auto-reply" ||
|
|
967
|
-
command === "/pi-extensions") {
|
|
968
|
-
handlerPromise = this.routeSlashModelCommand({
|
|
1339
|
+
const entry = COMMAND_MANIFEST.find((candidate) => candidate.slackCommand === command);
|
|
1340
|
+
if (!entry)
|
|
1341
|
+
return;
|
|
1342
|
+
if (!entry.slackRoute)
|
|
1343
|
+
return;
|
|
1344
|
+
try {
|
|
1345
|
+
await this.routeSlashCommand(entry.slackRoute, {
|
|
969
1346
|
command,
|
|
970
1347
|
text,
|
|
971
1348
|
channel_id,
|
|
972
1349
|
user_id,
|
|
973
1350
|
user_name,
|
|
974
|
-
});
|
|
975
|
-
}
|
|
976
|
-
else if (command === "/pi-admin") {
|
|
977
|
-
handlerPromise = this.routeSlashAdminCommand({
|
|
978
|
-
command,
|
|
979
|
-
channel_id,
|
|
980
|
-
user_id,
|
|
981
|
-
user_name,
|
|
982
1351
|
thread_ts,
|
|
983
1352
|
});
|
|
984
1353
|
}
|
|
985
|
-
|
|
986
|
-
|
|
1354
|
+
catch (err) {
|
|
1355
|
+
log.logWarning("Slack slash command error", errorMessage(err));
|
|
987
1356
|
}
|
|
988
|
-
handlerPromise.catch((err) => {
|
|
989
|
-
log.logWarning("Slack slash command error", err instanceof Error ? err.message : String(err));
|
|
990
|
-
});
|
|
991
1357
|
}
|
|
992
1358
|
handleAppHomeOpened({ event, ack }) {
|
|
993
1359
|
const e = event;
|
|
994
1360
|
ack();
|
|
1361
|
+
if (e.tab === "messages") {
|
|
1362
|
+
if (e.channel) {
|
|
1363
|
+
void handleAgentDmOpened(this.assistantOps(), this.assistantThreads, e.channel, e.context);
|
|
1364
|
+
}
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
995
1367
|
if (e.tab !== "home")
|
|
996
1368
|
return;
|
|
997
1369
|
this.webClient.views
|
|
998
1370
|
.publish({
|
|
999
1371
|
user_id: e.user,
|
|
1000
|
-
view: this.buildHomeView(),
|
|
1372
|
+
view: this.buildHomeView(e.channel),
|
|
1001
1373
|
})
|
|
1002
1374
|
.catch((err) => {
|
|
1003
1375
|
log.logWarning(`Failed to publish App Home view`, String(err));
|
|
@@ -1011,31 +1383,28 @@ export class SlackMessagingBot {
|
|
|
1011
1383
|
}
|
|
1012
1384
|
if (!action.action_id?.startsWith("force_stop_")) {
|
|
1013
1385
|
ack();
|
|
1014
|
-
this.handleSlackInteraction(body, action);
|
|
1386
|
+
await this.handleSlackInteraction(body, action);
|
|
1015
1387
|
return;
|
|
1016
1388
|
}
|
|
1017
1389
|
ack();
|
|
1018
|
-
const sessionKey = action.action_id.replace("force_stop_", "").replace(/_/g, ":");
|
|
1390
|
+
const sessionKey = action.value ?? action.action_id.replace("force_stop_", "").replace(/_/g, ":");
|
|
1019
1391
|
const userId = body.user?.id;
|
|
1020
|
-
const channelId = body.container?.channel_id || sessionKey
|
|
1392
|
+
const channelId = body.container?.channel_id || conversationIdOf(sessionKey);
|
|
1021
1393
|
log.logInfo(`[Force Stop] User ${userId} requested force stop for ${sessionKey}`);
|
|
1022
|
-
|
|
1023
|
-
this.handler.forceStop(sessionKey);
|
|
1024
|
-
// Notify in channel
|
|
1394
|
+
this.handler.forceStop(createOfficeAddress("slack", channelId), sessionKey);
|
|
1025
1395
|
await this.postMessage(channelId, formatForceStopped("slack", userId ?? "unknown"));
|
|
1026
|
-
// Refresh home tab
|
|
1027
1396
|
if (userId) {
|
|
1028
|
-
this.
|
|
1029
|
-
.publish({
|
|
1397
|
+
this.openDirectConversation(userId)
|
|
1398
|
+
.then((dmChannelId) => this.webClient.views.publish({
|
|
1030
1399
|
user_id: userId,
|
|
1031
|
-
view: this.buildHomeView(),
|
|
1032
|
-
})
|
|
1400
|
+
view: this.buildHomeView(dmChannelId),
|
|
1401
|
+
}))
|
|
1033
1402
|
.catch((err) => {
|
|
1034
1403
|
log.logWarning(`Failed to refresh App Home view`, String(err));
|
|
1035
1404
|
});
|
|
1036
1405
|
}
|
|
1037
1406
|
}
|
|
1038
|
-
handleSlackInteraction(body, action) {
|
|
1407
|
+
async handleSlackInteraction(body, action) {
|
|
1039
1408
|
const container = body.container ?? {};
|
|
1040
1409
|
const channelId = container.channel_id;
|
|
1041
1410
|
const userId = body.user?.id;
|
|
@@ -1045,6 +1414,8 @@ export class SlackMessagingBot {
|
|
|
1045
1414
|
const selectedOptions = Array.isArray(action.selected_options)
|
|
1046
1415
|
? action.selected_options
|
|
1047
1416
|
: undefined;
|
|
1417
|
+
const threadTs = container.thread_ts;
|
|
1418
|
+
const sessionKey = resolveSlackSessionKey(channelId, threadTs);
|
|
1048
1419
|
const selectedText = selectedOption?.text?.text ?? selectedOption?.value;
|
|
1049
1420
|
const selectedTexts = selectedOptions?.map((option) => option.text?.text ?? option.value);
|
|
1050
1421
|
const valueText = selectedTexts?.length
|
|
@@ -1052,12 +1423,10 @@ export class SlackMessagingBot {
|
|
|
1052
1423
|
: (selectedText ?? action.value ?? action.action_id);
|
|
1053
1424
|
const text = `[Slack action] ${action.action_id}: ${valueText}`;
|
|
1054
1425
|
const ts = `action:${Date.now()}`;
|
|
1055
|
-
const threadTs = container.thread_ts;
|
|
1056
|
-
const sessionKey = resolveSlackSessionKey(channelId, threadTs);
|
|
1057
1426
|
this.logToFile(channelId, {
|
|
1058
1427
|
date: new Date().toISOString(),
|
|
1059
1428
|
ts,
|
|
1060
|
-
|
|
1429
|
+
threadTs: threadTs || undefined,
|
|
1061
1430
|
user: userId,
|
|
1062
1431
|
userName: body.user?.username ?? body.user?.name,
|
|
1063
1432
|
text,
|
|
@@ -1080,50 +1449,87 @@ export class SlackMessagingBot {
|
|
|
1080
1449
|
messageTs: container.message_ts,
|
|
1081
1450
|
},
|
|
1082
1451
|
});
|
|
1083
|
-
const event = {
|
|
1084
|
-
|
|
1452
|
+
const event = createConversationEvent({
|
|
1453
|
+
platform: "slack",
|
|
1085
1454
|
conversationId: channelId,
|
|
1455
|
+
type: "slack_action",
|
|
1086
1456
|
conversationKind: channelId.startsWith("D") ? "direct" : "shared",
|
|
1087
1457
|
ts,
|
|
1088
1458
|
user: userId,
|
|
1089
1459
|
text,
|
|
1090
1460
|
attachments: [],
|
|
1091
|
-
|
|
1461
|
+
thread_ts: threadTs || undefined,
|
|
1092
1462
|
sessionKey,
|
|
1093
|
-
};
|
|
1094
|
-
this.getQueue(this.resolveQueueKey(channelId, sessionKey)).enqueue(async () => {
|
|
1463
|
+
});
|
|
1464
|
+
this.getQueue(await this.resolveQueueKey(channelId, sessionKey)).enqueue(async () => {
|
|
1095
1465
|
const slackEvent = {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1466
|
+
...createConversationEvent({
|
|
1467
|
+
platform: "slack",
|
|
1468
|
+
conversationId: channelId,
|
|
1469
|
+
type: (event.conversationKind === "direct" ? "dm" : "mention"),
|
|
1470
|
+
conversationKind: event.conversationKind,
|
|
1471
|
+
ts,
|
|
1472
|
+
thread_ts: threadTs,
|
|
1473
|
+
user: userId,
|
|
1474
|
+
text,
|
|
1475
|
+
attachments: [],
|
|
1476
|
+
sessionKey,
|
|
1477
|
+
}),
|
|
1099
1478
|
channel: channelId,
|
|
1100
|
-
ts,
|
|
1101
|
-
thread_ts: threadTs,
|
|
1102
|
-
user: userId,
|
|
1103
|
-
text,
|
|
1104
1479
|
attachments: [],
|
|
1105
|
-
sessionKey,
|
|
1106
1480
|
};
|
|
1107
1481
|
return this.handler.handleEvent(event, this, this.createContext(slackEvent));
|
|
1108
1482
|
});
|
|
1109
1483
|
}
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1484
|
+
async processAttachments(channelId, files, timestamp) {
|
|
1485
|
+
const items = [];
|
|
1486
|
+
for (const file of files) {
|
|
1487
|
+
const url = file.url_private_download || file.url_private;
|
|
1488
|
+
if (!url)
|
|
1489
|
+
continue;
|
|
1490
|
+
if (!file.name) {
|
|
1491
|
+
throw new Error(`Attachment missing name for URL: ${url}`);
|
|
1492
|
+
}
|
|
1493
|
+
items.push({
|
|
1494
|
+
name: file.name,
|
|
1495
|
+
timestampMs: Math.floor(parseFloat(timestamp) * 1000),
|
|
1496
|
+
download: (destPath) => this.downloadSlackFile(url, destPath),
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
const office = this.workspace.office(createOfficeAddress("slack", channelId));
|
|
1500
|
+
const { saved, failed } = await saveIncomingAttachments(office, items);
|
|
1501
|
+
const firstFailure = failed[0];
|
|
1502
|
+
if (firstFailure) {
|
|
1503
|
+
const { name, error } = firstFailure;
|
|
1504
|
+
const errorMsg = errorMessage(error);
|
|
1505
|
+
throw new Error(`Failed to download attachment ${name}: ${errorMsg}`, { cause: error });
|
|
1506
|
+
}
|
|
1507
|
+
return saved;
|
|
1508
|
+
}
|
|
1509
|
+
async downloadSlackFile(url, destPath) {
|
|
1510
|
+
assertSlackFileUrl(url);
|
|
1511
|
+
await withRetry(async () => {
|
|
1512
|
+
const response = await this.fetchFn(url, {
|
|
1513
|
+
headers: { Authorization: `Bearer ${this.botToken}` },
|
|
1514
|
+
});
|
|
1515
|
+
if (!response.ok) {
|
|
1516
|
+
throw new AttachmentDownloadHttpError(`HTTP ${response.status}: ${response.statusText}`, response.status);
|
|
1517
|
+
}
|
|
1518
|
+
await writeResponseToFile(response, destPath);
|
|
1519
|
+
}, { maxAttempts: 3, baseDelayMs: 250, isRateLimited: isRetryableAttachmentDownloadError });
|
|
1520
|
+
}
|
|
1113
1521
|
async logUserMessage(event) {
|
|
1114
1522
|
const user = this.users.get(event.user);
|
|
1115
1523
|
let attachments = [];
|
|
1116
1524
|
let attachmentError;
|
|
1117
1525
|
if (event.files) {
|
|
1118
1526
|
try {
|
|
1119
|
-
attachments = await this.
|
|
1527
|
+
attachments = await this.processAttachments(event.channel, event.files, event.ts);
|
|
1120
1528
|
}
|
|
1121
1529
|
catch (err) {
|
|
1122
1530
|
attachmentError = err;
|
|
1123
1531
|
}
|
|
1124
1532
|
}
|
|
1125
|
-
// Always write the text log, even if attachment processing failed — we want
|
|
1126
|
-
// a record of the user message regardless of file-handling errors.
|
|
1127
1533
|
this.logToFile(event.channel, {
|
|
1128
1534
|
date: new Date(parseFloat(event.ts) * 1000).toISOString(),
|
|
1129
1535
|
ts: event.ts,
|
|
@@ -1141,7 +1547,7 @@ export class SlackMessagingBot {
|
|
|
1141
1547
|
}
|
|
1142
1548
|
async logExternalMessagingBotMessage(event) {
|
|
1143
1549
|
const attachments = event.files
|
|
1144
|
-
? await this.
|
|
1550
|
+
? await this.processAttachments(event.channel, event.files, event.ts)
|
|
1145
1551
|
: [];
|
|
1146
1552
|
const botName = event.username ?? event.bot_profile?.name ?? event.bot_profile?.real_name ?? event.bot_id;
|
|
1147
1553
|
this.logToFile(event.channel, {
|
|
@@ -1160,31 +1566,39 @@ export class SlackMessagingBot {
|
|
|
1160
1566
|
});
|
|
1161
1567
|
return attachments;
|
|
1162
1568
|
}
|
|
1163
|
-
// ==========================================================================
|
|
1164
|
-
// Private - Backfill
|
|
1165
|
-
// ==========================================================================
|
|
1166
1569
|
async getExistingTimestamps(channelId) {
|
|
1167
|
-
const logPath =
|
|
1570
|
+
const { logPath } = this.office(channelId);
|
|
1168
1571
|
const timestamps = new Set();
|
|
1169
|
-
|
|
1572
|
+
const content = readTextFileNoFollowIfExists(logPath);
|
|
1573
|
+
if (content === undefined)
|
|
1170
1574
|
return timestamps;
|
|
1171
|
-
const content = await readFile(logPath, "utf-8");
|
|
1172
1575
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
1173
1576
|
for (let i = 0; i < lines.length; i++) {
|
|
1174
1577
|
try {
|
|
1175
|
-
const entry = JSON.parse(lines[i]);
|
|
1578
|
+
const entry = JSON.parse(lines[i] ?? "");
|
|
1176
1579
|
if (entry.ts)
|
|
1177
1580
|
timestamps.add(entry.ts);
|
|
1178
1581
|
}
|
|
1179
1582
|
catch (err) {
|
|
1180
|
-
log.logWarning(`Skipping malformed log entry at ${logPath}:${i + 1}`,
|
|
1583
|
+
log.logWarning(`Skipping malformed log entry at ${logPath}:${i + 1}`, errorMessage(err));
|
|
1181
1584
|
}
|
|
1182
1585
|
}
|
|
1183
1586
|
return timestamps;
|
|
1184
1587
|
}
|
|
1588
|
+
isBackfillableMessage(message) {
|
|
1589
|
+
if (message.user === this.botUserId)
|
|
1590
|
+
return true;
|
|
1591
|
+
if (hasBotIdentity(message)) {
|
|
1592
|
+
if (this.botId && message.bot_id === this.botId)
|
|
1593
|
+
return false;
|
|
1594
|
+
return BOT_MESSAGE_SUBTYPES.has(message.subtype) && hasMessageContent(message);
|
|
1595
|
+
}
|
|
1596
|
+
return (!!message.user &&
|
|
1597
|
+
USER_MESSAGE_SUBTYPES.has(message.subtype) &&
|
|
1598
|
+
hasMessageContent(message, false));
|
|
1599
|
+
}
|
|
1185
1600
|
async backfillChannel(channelId, upperBoundTs) {
|
|
1186
1601
|
const existingTs = await this.getExistingTimestamps(channelId);
|
|
1187
|
-
// Find the biggest ts in log.jsonl
|
|
1188
1602
|
let lastLoggedTs;
|
|
1189
1603
|
for (const ts of existingTs) {
|
|
1190
1604
|
if (!lastLoggedTs || parseFloat(ts) > parseFloat(lastLoggedTs))
|
|
@@ -1197,8 +1611,8 @@ export class SlackMessagingBot {
|
|
|
1197
1611
|
do {
|
|
1198
1612
|
const result = await this.webClient.conversations.history({
|
|
1199
1613
|
channel: channelId,
|
|
1200
|
-
oldest: lastLoggedTs,
|
|
1201
|
-
latest: upperBoundTs,
|
|
1614
|
+
oldest: lastLoggedTs,
|
|
1615
|
+
latest: upperBoundTs,
|
|
1202
1616
|
inclusive: false,
|
|
1203
1617
|
limit: 1000,
|
|
1204
1618
|
cursor,
|
|
@@ -1209,40 +1623,15 @@ export class SlackMessagingBot {
|
|
|
1209
1623
|
cursor = result.response_metadata?.next_cursor;
|
|
1210
1624
|
pageCount++;
|
|
1211
1625
|
} while (cursor && pageCount < maxPages);
|
|
1212
|
-
// Filter: include mikan's messages, external app/bot messages, and user messages.
|
|
1213
1626
|
const relevantMessages = allMessages.filter((msg) => {
|
|
1214
1627
|
if (!msg.ts || existingTs.has(msg.ts))
|
|
1215
|
-
return false; // Skip duplicates
|
|
1216
|
-
if (msg.user === this.botUserId)
|
|
1217
|
-
return true;
|
|
1218
|
-
const isExternalMessagingBotMessage = !!msg.bot_id || msg.subtype === "bot_message";
|
|
1219
|
-
if (isExternalMessagingBotMessage) {
|
|
1220
|
-
if (this.botId && msg.bot_id === this.botId)
|
|
1221
|
-
return false;
|
|
1222
|
-
if (msg.subtype !== undefined &&
|
|
1223
|
-
msg.subtype !== "bot_message" &&
|
|
1224
|
-
msg.subtype !== "file_share") {
|
|
1225
|
-
return false;
|
|
1226
|
-
}
|
|
1227
|
-
return (!!msg.text ||
|
|
1228
|
-
!!(msg.files && msg.files.length > 0) ||
|
|
1229
|
-
!!msg.blocks?.length ||
|
|
1230
|
-
!!msg.attachments?.length);
|
|
1231
|
-
}
|
|
1232
|
-
if (msg.subtype !== undefined && msg.subtype !== "file_share")
|
|
1233
|
-
return false;
|
|
1234
|
-
if (!msg.user)
|
|
1235
|
-
return false;
|
|
1236
|
-
if (!msg.text && (!msg.files || msg.files.length === 0))
|
|
1237
1628
|
return false;
|
|
1238
|
-
return
|
|
1629
|
+
return this.isBackfillableMessage(msg);
|
|
1239
1630
|
});
|
|
1240
|
-
// Reverse to chronological order
|
|
1241
1631
|
relevantMessages.reverse();
|
|
1242
|
-
// Log each message to log.jsonl
|
|
1243
1632
|
for (const msg of relevantMessages) {
|
|
1244
1633
|
const isMikanMessage = msg.user === this.botUserId;
|
|
1245
|
-
const isExternalMessagingBotMessage = !isMikanMessage && (
|
|
1634
|
+
const isExternalMessagingBotMessage = !isMikanMessage && hasBotIdentity(msg);
|
|
1246
1635
|
if (isExternalMessagingBotMessage) {
|
|
1247
1636
|
await this.logExternalMessagingBotMessage({ ...msg, channel: channelId, ts: msg.ts });
|
|
1248
1637
|
continue;
|
|
@@ -1250,7 +1639,7 @@ export class SlackMessagingBot {
|
|
|
1250
1639
|
const user = this.users.get(msg.user);
|
|
1251
1640
|
const text = this.stripOwnMention(msg.text);
|
|
1252
1641
|
const attachments = msg.files
|
|
1253
|
-
? await this.
|
|
1642
|
+
? await this.processAttachments(channelId, msg.files, msg.ts)
|
|
1254
1643
|
: [];
|
|
1255
1644
|
this.logToFile(channelId, {
|
|
1256
1645
|
date: new Date(parseFloat(msg.ts) * 1000).toISOString(),
|
|
@@ -1268,11 +1657,9 @@ export class SlackMessagingBot {
|
|
|
1268
1657
|
}
|
|
1269
1658
|
async backfillAllChannels(upperBoundTs) {
|
|
1270
1659
|
const startTime = Date.now();
|
|
1271
|
-
// Only backfill channels that already have a log.jsonl (mikan has interacted with them before)
|
|
1272
1660
|
const channelsToBackfill = [];
|
|
1273
1661
|
for (const [channelId, channel] of this.channels) {
|
|
1274
|
-
|
|
1275
|
-
if (existsSync(logPath)) {
|
|
1662
|
+
if (existsSync(this.office(channelId).logPath)) {
|
|
1276
1663
|
channelsToBackfill.push([channelId, channel]);
|
|
1277
1664
|
}
|
|
1278
1665
|
}
|
|
@@ -1288,38 +1675,32 @@ export class SlackMessagingBot {
|
|
|
1288
1675
|
catch (error) {
|
|
1289
1676
|
log.logWarning(`Failed to backfill #${channel.name}`, String(error));
|
|
1290
1677
|
}
|
|
1291
|
-
|
|
1292
|
-
if (channelId !== channelsToBackfill[channelsToBackfill.length - 1][0]) {
|
|
1678
|
+
if (channelId !== channelsToBackfill[channelsToBackfill.length - 1]?.[0]) {
|
|
1293
1679
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1294
1680
|
}
|
|
1295
1681
|
}
|
|
1296
1682
|
const durationMs = Date.now() - startTime;
|
|
1297
1683
|
log.logBackfillComplete(totalMessages, durationMs);
|
|
1298
1684
|
}
|
|
1299
|
-
// ==========================================================================
|
|
1300
|
-
// Private - Fetch Users/Channels
|
|
1301
|
-
// ==========================================================================
|
|
1302
1685
|
async fetchUsers() {
|
|
1303
1686
|
let cursor;
|
|
1304
1687
|
do {
|
|
1305
1688
|
const result = await this.webClient.users.list({ limit: 200, cursor });
|
|
1306
1689
|
const members = result.members;
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
}
|
|
1690
|
+
for (const u of members ?? []) {
|
|
1691
|
+
if (!u.id || !u.name || u.deleted)
|
|
1692
|
+
continue;
|
|
1693
|
+
this.users.set(u.id, {
|
|
1694
|
+
id: u.id,
|
|
1695
|
+
userName: u.name,
|
|
1696
|
+
displayName: u.real_name || u.name,
|
|
1697
|
+
isBot: !!u.is_bot,
|
|
1698
|
+
});
|
|
1317
1699
|
}
|
|
1318
1700
|
cursor = result.response_metadata?.next_cursor;
|
|
1319
1701
|
} while (cursor);
|
|
1320
1702
|
}
|
|
1321
1703
|
async fetchChannels() {
|
|
1322
|
-
// Fetch public/private channels
|
|
1323
1704
|
let cursor;
|
|
1324
1705
|
do {
|
|
1325
1706
|
const result = await this.webClient.conversations.list({
|
|
@@ -1329,16 +1710,18 @@ export class SlackMessagingBot {
|
|
|
1329
1710
|
cursor,
|
|
1330
1711
|
});
|
|
1331
1712
|
const channels = result.channels;
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1713
|
+
for (const c of channels ?? []) {
|
|
1714
|
+
if (!c.id || !c.name || !c.is_member)
|
|
1715
|
+
continue;
|
|
1716
|
+
this.channels.set(c.id, {
|
|
1717
|
+
id: c.id,
|
|
1718
|
+
name: c.name,
|
|
1719
|
+
isPrivate: typeof c.is_private === "boolean" ? c.is_private : undefined,
|
|
1720
|
+
isExternallyShared: c.is_shared || c.is_ext_shared ? true : undefined,
|
|
1721
|
+
});
|
|
1338
1722
|
}
|
|
1339
1723
|
cursor = result.response_metadata?.next_cursor;
|
|
1340
1724
|
} while (cursor);
|
|
1341
|
-
// Also fetch DM channels (IMs)
|
|
1342
1725
|
cursor = undefined;
|
|
1343
1726
|
do {
|
|
1344
1727
|
const result = await this.webClient.conversations.list({
|
|
@@ -1347,15 +1730,12 @@ export class SlackMessagingBot {
|
|
|
1347
1730
|
cursor,
|
|
1348
1731
|
});
|
|
1349
1732
|
const ims = result.channels;
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
this.channels.set(im.id, { id: im.id, name });
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1733
|
+
for (const im of ims ?? []) {
|
|
1734
|
+
if (!im.id)
|
|
1735
|
+
continue;
|
|
1736
|
+
const user = im.user ? this.users.get(im.user) : undefined;
|
|
1737
|
+
const name = user ? `DM:${user.userName}` : `DM:${im.id}`;
|
|
1738
|
+
this.channels.set(im.id, { id: im.id, name });
|
|
1359
1739
|
}
|
|
1360
1740
|
cursor = result.response_metadata?.next_cursor;
|
|
1361
1741
|
} while (cursor);
|