agor-live 0.21.2 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/branch/env/restart.js +1 -1
- package/dist/cli/commands/branch/env/start.js +1 -1
- package/dist/cli/commands/branch/env/stop.js +1 -1
- package/dist/core/api/index.cjs +5 -1
- package/dist/core/api/index.d.ts +6 -1
- package/dist/core/api/index.d.ts.map +1 -1
- package/dist/core/api/index.js +5 -1
- package/dist/core/claude/index.cjs +162 -14
- package/dist/core/claude/index.js +166 -18
- package/dist/core/client/index.cjs +43 -3
- package/dist/core/client/index.js +37 -3
- package/dist/core/config/browser.cjs +2 -2
- package/dist/core/config/browser.js +2 -2
- package/dist/core/config/config-manager.d.ts.map +1 -1
- package/dist/core/config/constants.d.ts +1 -1
- package/dist/core/config/index.cjs +174 -20
- package/dist/core/config/index.js +178 -24
- package/dist/core/config/types.d.ts +8 -0
- package/dist/core/config/types.d.ts.map +1 -1
- package/dist/core/db/index.cjs +495 -86
- package/dist/core/db/index.js +482 -75
- package/dist/core/db/repositories/artifacts.d.ts.map +1 -1
- package/dist/core/db/repositories/branches.d.ts +13 -1
- package/dist/core/db/repositories/branches.d.ts.map +1 -1
- package/dist/core/db/repositories/gateway-channels.d.ts.map +1 -1
- package/dist/core/db/repositories/index.d.ts +1 -0
- package/dist/core/db/repositories/index.d.ts.map +1 -1
- package/dist/core/db/repositories/session-relationships.d.ts +30 -0
- package/dist/core/db/repositories/session-relationships.d.ts.map +1 -0
- package/dist/core/db/repositories/users.d.ts +11 -0
- package/dist/core/db/repositories/users.d.ts.map +1 -1
- package/dist/core/db/schema.d.ts +421 -4
- package/dist/core/db/schema.d.ts.map +1 -1
- package/dist/core/db/schema.postgres.d.ts +226 -2
- package/dist/core/db/schema.postgres.d.ts.map +1 -1
- package/dist/core/db/schema.sqlite.d.ts +228 -2
- package/dist/core/db/schema.sqlite.d.ts.map +1 -1
- package/dist/core/drizzle/postgres/0050_artifact_source_session.sql +3 -0
- package/dist/core/drizzle/postgres/0051_session_relationships.sql +26 -0
- package/dist/core/drizzle/postgres/meta/_journal.json +14 -0
- package/dist/core/drizzle/sqlite/0059_artifact_source_session.sql +2 -0
- package/dist/core/drizzle/sqlite/0060_session_relationships.sql +23 -0
- package/dist/core/drizzle/sqlite/meta/_journal.json +14 -0
- package/dist/core/gateway/connector-registry.d.ts.map +1 -1
- package/dist/core/gateway/connector.d.ts +22 -0
- package/dist/core/gateway/connector.d.ts.map +1 -1
- package/dist/core/gateway/connectors/slack.d.ts +35 -3
- package/dist/core/gateway/connectors/slack.d.ts.map +1 -1
- package/dist/core/gateway/connectors/teams.d.ts +107 -0
- package/dist/core/gateway/connectors/teams.d.ts.map +1 -0
- package/dist/core/gateway/context.d.ts.map +1 -1
- package/dist/core/gateway/index.cjs +552 -52
- package/dist/core/gateway/index.d.ts +3 -2
- package/dist/core/gateway/index.d.ts.map +1 -1
- package/dist/core/gateway/index.js +544 -52
- package/dist/core/gateway/system-message.d.ts +21 -0
- package/dist/core/gateway/system-message.d.ts.map +1 -1
- package/dist/core/index.cjs +616 -127
- package/dist/core/index.js +597 -116
- package/dist/core/mcp/index.cjs +146 -9
- package/dist/core/mcp/index.js +146 -9
- package/dist/core/mcp/template-resolver.d.ts.map +1 -1
- package/dist/core/seed/index.cjs +375 -59
- package/dist/core/seed/index.js +378 -62
- package/dist/core/sessions/index.cjs +19 -0
- package/dist/core/sessions/index.js +19 -0
- package/dist/core/templates/agor-system-prompt.md +21 -53
- package/dist/core/templates/session-context.cjs +2 -73
- package/dist/core/templates/session-context.d.ts +9 -53
- package/dist/core/templates/session-context.d.ts.map +1 -1
- package/dist/core/templates/session-context.js +2 -72
- package/dist/core/tools/mcp/jwt-auth.cjs +2 -20
- package/dist/core/tools/mcp/jwt-auth.d.ts.map +1 -1
- package/dist/core/tools/mcp/jwt-auth.js +2 -20
- package/dist/core/tools/mcp/oauth-mcp-transport.cjs +2 -15
- package/dist/core/tools/mcp/oauth-mcp-transport.d.ts.map +1 -1
- package/dist/core/tools/mcp/oauth-mcp-transport.js +2 -15
- package/dist/core/tools/mcp/oauth-refresh.cjs +167 -19
- package/dist/core/tools/mcp/oauth-refresh.js +167 -19
- package/dist/core/types/artifact.d.ts +5 -1
- package/dist/core/types/artifact.d.ts.map +1 -1
- package/dist/core/types/branch.d.ts +14 -0
- package/dist/core/types/branch.d.ts.map +1 -1
- package/dist/core/types/gateway.d.ts +1 -1
- package/dist/core/types/gateway.d.ts.map +1 -1
- package/dist/core/types/id.d.ts +1 -0
- package/dist/core/types/id.d.ts.map +1 -1
- package/dist/core/types/index.cjs +36 -0
- package/dist/core/types/index.js +30 -0
- package/dist/core/types/session.d.ts +65 -1
- package/dist/core/types/session.d.ts.map +1 -1
- package/dist/core/types/task.d.ts +9 -0
- package/dist/core/types/task.d.ts.map +1 -1
- package/dist/core/unix/index.cjs +371 -55
- package/dist/core/unix/index.js +374 -58
- package/dist/core/unix/unix-integration-service.d.ts +10 -7
- package/dist/core/unix/unix-integration-service.d.ts.map +1 -1
- package/dist/core/utils/permission-mode-mapper.cjs +19 -0
- package/dist/core/utils/permission-mode-mapper.js +19 -0
- package/dist/daemon/declarations.d.ts +7 -0
- package/dist/daemon/declarations.d.ts.map +1 -1
- package/dist/daemon/hooks/gateway-route.d.ts.map +1 -1
- package/dist/daemon/hooks/gateway-route.js +53 -1
- package/dist/daemon/index.js +3760 -2107
- package/dist/daemon/main.js +3760 -2107
- package/dist/daemon/mcp/server.js +155 -17
- package/dist/daemon/mcp/tools/analytics.js +6 -7
- package/dist/daemon/mcp/tools/artifacts.d.ts.map +1 -1
- package/dist/daemon/mcp/tools/artifacts.js +7 -7
- package/dist/daemon/mcp/tools/boards.js +6 -7
- package/dist/daemon/mcp/tools/branches.js +6 -7
- package/dist/daemon/mcp/tools/card-types.js +6 -7
- package/dist/daemon/mcp/tools/cards.js +6 -7
- package/dist/daemon/mcp/tools/environment.d.ts.map +1 -1
- package/dist/daemon/mcp/tools/environment.js +19 -12
- package/dist/daemon/mcp/tools/knowledge.js +6 -7
- package/dist/daemon/mcp/tools/mcp-servers.js +6 -7
- package/dist/daemon/mcp/tools/messages.js +6 -7
- package/dist/daemon/mcp/tools/proxies.js +6 -7
- package/dist/daemon/mcp/tools/repos.js +6 -7
- package/dist/daemon/mcp/tools/schedules.js +6 -7
- package/dist/daemon/mcp/tools/search.js +6 -7
- package/dist/daemon/mcp/tools/sessions.d.ts.map +1 -1
- package/dist/daemon/mcp/tools/sessions.js +140 -11
- package/dist/daemon/mcp/tools/tasks.js +6 -7
- package/dist/daemon/mcp/tools/users.js +6 -7
- package/dist/daemon/mcp/tools/widgets.js +7 -8
- package/dist/daemon/register-hooks.d.ts +5 -1
- package/dist/daemon/register-hooks.d.ts.map +1 -1
- package/dist/daemon/register-hooks.js +404 -179
- package/dist/daemon/register-routes.d.ts.map +1 -1
- package/dist/daemon/register-routes.js +360 -162
- package/dist/daemon/register-services.d.ts.map +1 -1
- package/dist/daemon/register-services.js +1928 -671
- package/dist/daemon/services/artifacts.d.ts +10 -1
- package/dist/daemon/services/artifacts.d.ts.map +1 -1
- package/dist/daemon/services/artifacts.js +25 -1
- package/dist/daemon/services/branches.d.ts +11 -1
- package/dist/daemon/services/branches.d.ts.map +1 -1
- package/dist/daemon/services/branches.js +470 -184
- package/dist/daemon/services/claude-models.js +879 -339
- package/dist/daemon/services/gateway.d.ts +58 -2
- package/dist/daemon/services/gateway.d.ts.map +1 -1
- package/dist/daemon/services/gateway.js +590 -47
- package/dist/daemon/services/groups.d.ts +2 -0
- package/dist/daemon/services/groups.d.ts.map +1 -1
- package/dist/daemon/services/groups.js +56 -0
- package/dist/daemon/services/mcp-servers.d.ts.map +1 -1
- package/dist/daemon/services/mcp-servers.js +0 -6
- package/dist/daemon/services/scheduler.d.ts.map +1 -1
- package/dist/daemon/services/scheduler.js +6 -2
- package/dist/daemon/services/sessions.d.ts +11 -3
- package/dist/daemon/services/sessions.d.ts.map +1 -1
- package/dist/daemon/services/sessions.js +68 -7
- package/dist/daemon/services/tasks.d.ts +13 -2
- package/dist/daemon/services/tasks.d.ts.map +1 -1
- package/dist/daemon/services/tasks.js +42 -26
- package/dist/daemon/startup.js +6 -2
- package/dist/daemon/utils/session-stop.d.ts +43 -0
- package/dist/daemon/utils/session-stop.d.ts.map +1 -0
- package/dist/daemon/utils/session-stop.js +102 -0
- package/dist/daemon/utils/session-task-state.d.ts +7 -2
- package/dist/daemon/utils/session-task-state.d.ts.map +1 -1
- package/dist/daemon/utils/session-task-state.js +9 -3
- package/dist/daemon/utils/session-tasks.d.ts +4 -4
- package/dist/daemon/utils/session-tasks.d.ts.map +1 -1
- package/dist/daemon/utils/session-tasks.js +4 -8
- package/dist/executor/commands/environment.d.ts +13 -0
- package/dist/executor/commands/environment.d.ts.map +1 -0
- package/dist/executor/commands/environment.js +287 -0
- package/dist/executor/commands/index.d.ts.map +1 -1
- package/dist/executor/commands/index.js +5 -1
- package/dist/executor/commands/unix.d.ts +8 -1
- package/dist/executor/commands/unix.d.ts.map +1 -1
- package/dist/executor/commands/unix.js +157 -2
- package/dist/executor/payload-types.d.ts +201 -5
- package/dist/executor/payload-types.d.ts.map +1 -1
- package/dist/executor/payload-types.js +98 -0
- package/dist/executor/sdk-handlers/base/mcp-scoping.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/base/mcp-scoping.js +14 -0
- package/dist/executor/sdk-handlers/claude/message-processor.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/claude/message-processor.js +0 -1
- package/dist/executor/sdk-handlers/claude/query-builder.d.ts +1 -0
- package/dist/executor/sdk-handlers/claude/query-builder.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/claude/query-builder.js +91 -39
- package/dist/executor/sdk-handlers/codex/prompt-service.d.ts +12 -5
- package/dist/executor/sdk-handlers/codex/prompt-service.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/codex/prompt-service.js +72 -30
- package/dist/executor/sdk-handlers/copilot/prompt-service.d.ts +2 -4
- package/dist/executor/sdk-handlers/copilot/prompt-service.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/copilot/prompt-service.js +4 -13
- package/dist/executor/sdk-handlers/gemini/prompt-service.d.ts +2 -4
- package/dist/executor/sdk-handlers/gemini/prompt-service.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/gemini/prompt-service.js +4 -13
- package/dist/ui/assets/App-DcEY8Ota.js +3 -0
- package/dist/ui/assets/App-DcEY8Ota.js.gz +0 -0
- package/dist/ui/assets/{ArtifactConsentModal-ParNk5kW.js → ArtifactConsentModal-CiCbK9iv.js} +1 -1
- package/dist/ui/assets/ArtifactConsentModal-CiCbK9iv.js.gz +0 -0
- package/dist/ui/assets/ArtifactFullscreenPage-CfsTEGKd.js +9 -0
- package/dist/ui/assets/ArtifactFullscreenPage-CfsTEGKd.js.gz +0 -0
- package/dist/ui/assets/AutocompleteTextarea-BAFFH_5e.js +18 -0
- package/dist/ui/assets/AutocompleteTextarea-BAFFH_5e.js.gz +0 -0
- package/dist/ui/assets/BoardObjectNodes-D-O6bZIG.js +34 -0
- package/dist/ui/assets/BoardObjectNodes-D-O6bZIG.js.gz +0 -0
- package/dist/ui/assets/{CodeEditor.inner-D51Z_CLQ.js → CodeEditor.inner-DBgsP4tn.js} +2 -2
- package/dist/ui/assets/CodeEditor.inner-DBgsP4tn.js.gz +0 -0
- package/dist/ui/assets/ConversationView-CUWR0gR6.js +1 -0
- package/dist/ui/assets/ConversationView-CUWR0gR6.js.gz +0 -0
- package/dist/ui/assets/KnowledgePage-B2bzlXfn.js +24 -0
- package/dist/ui/assets/KnowledgePage-B2bzlXfn.js.gz +0 -0
- package/dist/ui/assets/MarketingScreenshotPage-9Qd7eZsm.css +1 -0
- package/dist/ui/assets/MarketingScreenshotPage-zv5RUCuV.js +143 -0
- package/dist/ui/assets/MarketingScreenshotPage-zv5RUCuV.js.gz +0 -0
- package/dist/ui/assets/MobileApp-VgVnsnsN.js +1 -0
- package/dist/ui/assets/MobileApp-VgVnsnsN.js.gz +0 -0
- package/dist/ui/assets/SessionCanvas-f1-1Gbcw.js +20 -0
- package/dist/ui/assets/SessionCanvas-f1-1Gbcw.js.gz +0 -0
- package/dist/ui/assets/{App-BAdBsEnV.css → SessionCanvas-mEmYGZhC.css} +1 -1
- package/dist/ui/assets/SessionCanvas-mEmYGZhC.css.gz +0 -0
- package/dist/ui/assets/{StreamdownDemoPage-B9wbgp2s.js → StreamdownDemoPage-wzWaqWwr.js} +1 -1
- package/dist/ui/assets/StreamdownDemoPage-wzWaqWwr.js.gz +0 -0
- package/dist/ui/assets/{ThemeSwitcher-ubn6IOz9.js → ThemeSwitcher-Dly2y9pi.js} +1 -1
- package/dist/ui/assets/ThemeSwitcher-Dly2y9pi.js.gz +0 -0
- package/dist/ui/assets/antd-CfbbHJOz.js +401 -0
- package/dist/ui/assets/antd-CfbbHJOz.js.gz +0 -0
- package/dist/ui/assets/architecture-U656AL7Q-CykGFbQU.js +1 -0
- package/dist/ui/assets/{architectureDiagram-VXUJARFQ-ChmZt3zk.js → architectureDiagram-VXUJARFQ-C8HXAenz.js} +1 -1
- package/dist/ui/assets/architectureDiagram-VXUJARFQ-C8HXAenz.js.gz +0 -0
- package/dist/ui/assets/{blockDiagram-VD42YOAC-CzGHAHao.js → blockDiagram-VD42YOAC-BhZaEN19.js} +1 -1
- package/dist/ui/assets/blockDiagram-VD42YOAC-BhZaEN19.js.gz +0 -0
- package/dist/ui/assets/{c4Diagram-YG6GDRKO-DscJyaWN.js → c4Diagram-YG6GDRKO-Dk_UH-sY.js} +1 -1
- package/dist/ui/assets/c4Diagram-YG6GDRKO-Dk_UH-sY.js.gz +0 -0
- package/dist/ui/assets/channel-D6_nUWlW.js +1 -0
- package/dist/ui/assets/{chunk-4BX2VUAB-DoWpTvP8.js → chunk-4BX2VUAB-XprbG2TG.js} +1 -1
- package/dist/ui/assets/chunk-55IACEB6-ByzqIgSb.js +1 -0
- package/dist/ui/assets/{chunk-ABZYJK2D-RzDCrjE6.js → chunk-ABZYJK2D-BJcrryHK.js} +1 -1
- package/dist/ui/assets/chunk-ABZYJK2D-BJcrryHK.js.gz +0 -0
- package/dist/ui/assets/{chunk-AGHRB4JF-jidCS5Of.js → chunk-AGHRB4JF-DvxmfbM0.js} +1 -1
- package/dist/ui/assets/chunk-AGHRB4JF-DvxmfbM0.js.gz +0 -0
- package/dist/ui/assets/{chunk-ATLVNIR6-BEIIfJtC.js → chunk-ATLVNIR6-DbeJ0OrR.js} +1 -1
- package/dist/ui/assets/chunk-ATLVNIR6-DbeJ0OrR.js.gz +0 -0
- package/dist/ui/assets/{chunk-B4BG7PRW-B8b6dQQ2.js → chunk-B4BG7PRW-C53q2ggf.js} +1 -1
- package/dist/ui/assets/chunk-B4BG7PRW-C53q2ggf.js.gz +0 -0
- package/dist/ui/assets/{chunk-CVBHYZKI-D-mQAfrk.js → chunk-CVBHYZKI-B3EBSlb3.js} +1 -1
- package/dist/ui/assets/{chunk-DI55MBZ5-BfATX3V8.js → chunk-DI55MBZ5-vIyNEQN-.js} +1 -1
- package/dist/ui/assets/chunk-DI55MBZ5-vIyNEQN-.js.gz +0 -0
- package/dist/ui/assets/chunk-EXTU4WIE-B3ObkuOm.js +1 -0
- package/dist/ui/assets/{chunk-FMBD7UC4-fqXscNvc.js → chunk-FMBD7UC4-rddmfK-Z.js} +1 -1
- package/dist/ui/assets/{chunk-HN2XXSSU-BCHvD80g.js → chunk-HN2XXSSU-Dttqcg3b.js} +1 -1
- package/dist/ui/assets/chunk-HN2XXSSU-Dttqcg3b.js.gz +0 -0
- package/dist/ui/assets/{chunk-JA3XYJ7Z-Cp6dqHnY.js → chunk-JA3XYJ7Z-DbNDev3D.js} +1 -1
- package/dist/ui/assets/chunk-JA3XYJ7Z-DbNDev3D.js.gz +0 -0
- package/dist/ui/assets/{chunk-JZLCHNYA-cKMooY3y.js → chunk-JZLCHNYA-EUmx2y4H.js} +1 -1
- package/dist/ui/assets/chunk-JZLCHNYA-EUmx2y4H.js.gz +0 -0
- package/dist/ui/assets/{chunk-MI3HLSF2-BlzO5wOE.js → chunk-MI3HLSF2-65n9Mkyc.js} +1 -1
- package/dist/ui/assets/chunk-MI3HLSF2-65n9Mkyc.js.gz +0 -0
- package/dist/ui/assets/chunk-N4CR4FBY-mv5koXqW.js +2 -0
- package/dist/ui/assets/chunk-N4CR4FBY-mv5koXqW.js.gz +0 -0
- package/dist/ui/assets/{chunk-QN33PNHL-DIHGQ_pd.js → chunk-QN33PNHL-DsRKK6NR.js} +1 -1
- package/dist/ui/assets/{chunk-QXUST7PY-C9l0muI0.js → chunk-QXUST7PY-BCYnMiS3.js} +1 -1
- package/dist/ui/assets/chunk-QXUST7PY-BCYnMiS3.js.gz +0 -0
- package/dist/ui/assets/chunk-QZHKN3VN-HucAw4xW.js +1 -0
- package/dist/ui/assets/{chunk-S3R3BYOJ-VJiLzt2o.js → chunk-S3R3BYOJ-CWMEa9Dc.js} +1 -1
- package/dist/ui/assets/chunk-S3R3BYOJ-CWMEa9Dc.js.gz +0 -0
- package/dist/ui/assets/{chunk-TZMSLE5B-DZwI0C_2.js → chunk-TZMSLE5B-BuQUQcTr.js} +1 -1
- package/dist/ui/assets/chunk-TZMSLE5B-BuQUQcTr.js.gz +0 -0
- package/dist/ui/assets/classDiagram-2ON5EDUG-CUT3rPTB.js +1 -0
- package/dist/ui/assets/classDiagram-v2-WZHVMYZB-CUT3rPTB.js +1 -0
- package/dist/ui/assets/{cose-bilkent-S5V4N54A-Ipik-oSD.js → cose-bilkent-S5V4N54A-CnPB3ARO.js} +1 -1
- package/dist/ui/assets/cose-bilkent-S5V4N54A-CnPB3ARO.js.gz +0 -0
- package/dist/ui/assets/cursor-QEb7m-rN.png +0 -0
- package/dist/ui/assets/{dagre-6UL2VRFP-BDpyWQnh.js → dagre-6UL2VRFP-DhS-k_Se.js} +1 -1
- package/dist/ui/assets/dagre-6UL2VRFP-DhS-k_Se.js.gz +0 -0
- package/dist/ui/assets/{dagre-CgA4KhUX.js → dagre-KgLoHEuy.js} +1 -1
- package/dist/ui/assets/dagre-KgLoHEuy.js.gz +0 -0
- package/dist/ui/assets/{diagram-PSM6KHXK-B4GRzxLJ.js → diagram-PSM6KHXK-BZg3MJmb.js} +1 -1
- package/dist/ui/assets/diagram-PSM6KHXK-BZg3MJmb.js.gz +0 -0
- package/dist/ui/assets/{diagram-QEK2KX5R-BWPW28XI.js → diagram-QEK2KX5R-BPCitvbo.js} +1 -1
- package/dist/ui/assets/diagram-QEK2KX5R-BPCitvbo.js.gz +0 -0
- package/dist/ui/assets/{diagram-S2PKOQOG-BIHhcGoV.js → diagram-S2PKOQOG-NW4uK6sx.js} +1 -1
- package/dist/ui/assets/diagram-S2PKOQOG-NW4uK6sx.js.gz +0 -0
- package/dist/ui/assets/{editor-C-HJ7Yw0.js → editor-CzFWIUw2.js} +1 -1
- package/dist/ui/assets/editor-CzFWIUw2.js.gz +0 -0
- package/dist/ui/assets/{emoji-D8F6B62m.js → emoji-Dkz4Zzv_.js} +1 -1
- package/dist/ui/assets/emoji-Dkz4Zzv_.js.gz +0 -0
- package/dist/ui/assets/{erDiagram-Q2GNP2WA-ubTaAFcK.js → erDiagram-Q2GNP2WA-me1fboaf.js} +1 -1
- package/dist/ui/assets/erDiagram-Q2GNP2WA-me1fboaf.js.gz +0 -0
- package/dist/ui/assets/{flowDiagram-NV44I4VS-BHLCTYjI.js → flowDiagram-NV44I4VS-DzE8dGsh.js} +1 -1
- package/dist/ui/assets/flowDiagram-NV44I4VS-DzE8dGsh.js.gz +0 -0
- package/dist/ui/assets/{ganttDiagram-LVOFAZNH-ClC3pay1.js → ganttDiagram-LVOFAZNH-CFQD09Mi.js} +2 -2
- package/dist/ui/assets/ganttDiagram-LVOFAZNH-CFQD09Mi.js.gz +0 -0
- package/dist/ui/assets/{gitGraph-F6HP7TQM-DghoobE6.js → gitGraph-F6HP7TQM-CfFE_uAC.js} +1 -1
- package/dist/ui/assets/{gitGraphDiagram-NY62KEGX-Clqpiswu.js → gitGraphDiagram-NY62KEGX-CaVoxU4C.js} +1 -1
- package/dist/ui/assets/gitGraphDiagram-NY62KEGX-CaVoxU4C.js.gz +0 -0
- package/dist/ui/assets/index-BN5_Qq7R.js +324 -0
- package/dist/ui/assets/index-BN5_Qq7R.js.gz +0 -0
- package/dist/ui/assets/index-DxuPq13l.css +1 -0
- package/dist/ui/assets/index-DxuPq13l.css.gz +0 -0
- package/dist/ui/assets/{info-NVLQJR56-0ZldEXPQ.js → info-NVLQJR56-i_xHYg3f.js} +1 -1
- package/dist/ui/assets/{infoDiagram-ER5ION4S-DMmPX-il.js → infoDiagram-ER5ION4S-2NL93b78.js} +1 -1
- package/dist/ui/assets/{journeyDiagram-XKPGCS4Q-CrVickA2.js → journeyDiagram-XKPGCS4Q-CZF-2DHU.js} +1 -1
- package/dist/ui/assets/journeyDiagram-XKPGCS4Q-CZF-2DHU.js.gz +0 -0
- package/dist/ui/assets/{kanban-definition-3W4ZIXB7-C_BsdHYL.js → kanban-definition-3W4ZIXB7-CfvJIOny.js} +1 -1
- package/dist/ui/assets/kanban-definition-3W4ZIXB7-CfvJIOny.js.gz +0 -0
- package/dist/ui/assets/{line-CfBP7Yic.js → line-DDv8kOJk.js} +1 -1
- package/dist/ui/assets/{linear-BsjageUB.js → linear-Daef-l29.js} +1 -1
- package/dist/ui/assets/linear-Daef-l29.js.gz +0 -0
- package/dist/ui/assets/{mermaid-parser.core-DZdP-NFq.js → mermaid-parser.core-CdK9QgYV.js} +2 -2
- package/dist/ui/assets/mermaid-parser.core-CdK9QgYV.js.gz +0 -0
- package/dist/ui/assets/{mermaid.core-TNrI0pHG.js → mermaid.core-D6GS9mU-.js} +3 -3
- package/dist/ui/assets/mermaid.core-D6GS9mU-.js.gz +0 -0
- package/dist/ui/assets/message-SqLqNYcv.js +36 -0
- package/dist/ui/assets/message-SqLqNYcv.js.gz +0 -0
- package/dist/ui/assets/{mindmap-definition-VGOIOE7T-BbmaUjMY.js → mindmap-definition-VGOIOE7T-Cb3QMflX.js} +1 -1
- package/dist/ui/assets/mindmap-definition-VGOIOE7T-Cb3QMflX.js.gz +0 -0
- package/dist/ui/assets/{packet-BFZMPI3H-D_ZhkXuT.js → packet-BFZMPI3H-Bm2uwz4i.js} +1 -1
- package/dist/ui/assets/{particles-Dv28pjOd.js → particles-DsJFOarW.js} +1 -1
- package/dist/ui/assets/particles-DsJFOarW.js.gz +0 -0
- package/dist/ui/assets/{pie-7BOR55EZ-Dn0Q3qNx.js → pie-7BOR55EZ-5i17tVnF.js} +1 -1
- package/dist/ui/assets/{pieDiagram-ADFJNKIX-BbyjfYu8.js → pieDiagram-ADFJNKIX-BxIwQWvw.js} +1 -1
- package/dist/ui/assets/pieDiagram-ADFJNKIX-BxIwQWvw.js.gz +0 -0
- package/dist/ui/assets/{quadrantDiagram-AYHSOK5B-Bc3GqMKz.js → quadrantDiagram-AYHSOK5B-B5HPe4ga.js} +1 -1
- package/dist/ui/assets/quadrantDiagram-AYHSOK5B-B5HPe4ga.js.gz +0 -0
- package/dist/ui/assets/{radar-NHE76QYJ-BHwoAy1q.js → radar-NHE76QYJ-BTn-tq0k.js} +1 -1
- package/dist/ui/assets/{reactflow-BVYPxNhc.js → reactflow-Bf74ngoo.js} +2 -2
- package/dist/ui/assets/reactflow-Bf74ngoo.js.gz +0 -0
- package/dist/ui/assets/{requirementDiagram-UZGBJVZJ-BQaKKL09.js → requirementDiagram-UZGBJVZJ-YfI6llkX.js} +1 -1
- package/dist/ui/assets/requirementDiagram-UZGBJVZJ-YfI6llkX.js.gz +0 -0
- package/dist/ui/assets/{sandpack-BQW_FQ7G.js → sandpack-D7koO5op.js} +1 -1
- package/dist/ui/assets/sandpack-D7koO5op.js.gz +0 -0
- package/dist/ui/assets/{sankeyDiagram-TZEHDZUN-DiJvDvhb.js → sankeyDiagram-TZEHDZUN-DOk_B10B.js} +1 -1
- package/dist/ui/assets/sankeyDiagram-TZEHDZUN-DOk_B10B.js.gz +0 -0
- package/dist/ui/assets/{sequenceDiagram-WL72ISMW-Dw32824o.js → sequenceDiagram-WL72ISMW-DAe4Um17.js} +1 -1
- package/dist/ui/assets/sequenceDiagram-WL72ISMW-DAe4Um17.js.gz +0 -0
- package/dist/ui/assets/{stateDiagram-FKZM4ZOC-BCOpNdHV.js → stateDiagram-FKZM4ZOC-CCesDu_C.js} +1 -1
- package/dist/ui/assets/stateDiagram-FKZM4ZOC-CCesDu_C.js.gz +0 -0
- package/dist/ui/assets/stateDiagram-v2-4FDKWEC3-CMlIrsoO.js +1 -0
- package/dist/ui/assets/{syntax-VJrU5BEu.js → syntax-C-M-8jOU.js} +1 -1
- package/dist/ui/assets/syntax-C-M-8jOU.js.gz +0 -0
- package/dist/ui/assets/{theme-DZtFA8b4.js → theme-BQZdiqwv.js} +1 -1
- package/dist/ui/assets/{timeline-definition-IT6M3QCI-Jh_WZzXv.js → timeline-definition-IT6M3QCI-D6P5txjT.js} +1 -1
- package/dist/ui/assets/timeline-definition-IT6M3QCI-D6P5txjT.js.gz +0 -0
- package/dist/ui/assets/{treemap-KMMF4GRG-CPYIgjxE.js → treemap-KMMF4GRG-Bitm3gy4.js} +1 -1
- package/dist/ui/assets/{knowledgeRoutes-CivaUqha.js → useUserLocalStorage-Ckb8HsIw.js} +1 -1
- package/dist/ui/assets/useUserLocalStorage-Ckb8HsIw.js.gz +0 -0
- package/dist/ui/assets/{xychartDiagram-PRI3JC2R-CERc7Rdb.js → xychartDiagram-PRI3JC2R-CsybjUbd.js} +1 -1
- package/dist/ui/assets/xychartDiagram-PRI3JC2R-CsybjUbd.js.gz +0 -0
- package/dist/ui/index.html +13 -11
- package/dist/ui/index.html.gz +0 -0
- package/package.json +10 -9
- package/dist/ui/assets/App-9s2WHM6S.js +0 -22
- package/dist/ui/assets/App-9s2WHM6S.js.gz +0 -0
- package/dist/ui/assets/App-BAdBsEnV.css.gz +0 -0
- package/dist/ui/assets/ArtifactConsentModal-ParNk5kW.js.gz +0 -0
- package/dist/ui/assets/ArtifactFullscreenPage-VQxLMCiN.js +0 -9
- package/dist/ui/assets/ArtifactFullscreenPage-VQxLMCiN.js.gz +0 -0
- package/dist/ui/assets/AutocompleteTextarea-3RchrIgk.js +0 -18
- package/dist/ui/assets/AutocompleteTextarea-3RchrIgk.js.gz +0 -0
- package/dist/ui/assets/CodeEditor.inner-D51Z_CLQ.js.gz +0 -0
- package/dist/ui/assets/ConversationView-Dyddw2b1.js +0 -34
- package/dist/ui/assets/ConversationView-Dyddw2b1.js.gz +0 -0
- package/dist/ui/assets/KnowledgePage-CdftslnF.js +0 -24
- package/dist/ui/assets/KnowledgePage-CdftslnF.js.gz +0 -0
- package/dist/ui/assets/MobileApp-BdBMpnJ1.js +0 -1
- package/dist/ui/assets/MobileApp-BdBMpnJ1.js.gz +0 -0
- package/dist/ui/assets/StreamdownDemoPage-B9wbgp2s.js.gz +0 -0
- package/dist/ui/assets/ThemeSwitcher-ubn6IOz9.js.gz +0 -0
- package/dist/ui/assets/antd-C-HfEC4E.js +0 -400
- package/dist/ui/assets/antd-C-HfEC4E.js.gz +0 -0
- package/dist/ui/assets/architecture-U656AL7Q-dkBewUpN.js +0 -1
- package/dist/ui/assets/architectureDiagram-VXUJARFQ-ChmZt3zk.js.gz +0 -0
- package/dist/ui/assets/blockDiagram-VD42YOAC-CzGHAHao.js.gz +0 -0
- package/dist/ui/assets/c4Diagram-YG6GDRKO-DscJyaWN.js.gz +0 -0
- package/dist/ui/assets/channel-DvRQqEqC.js +0 -1
- package/dist/ui/assets/chunk-55IACEB6-DojF2pZN.js +0 -1
- package/dist/ui/assets/chunk-ABZYJK2D-RzDCrjE6.js.gz +0 -0
- package/dist/ui/assets/chunk-AGHRB4JF-jidCS5Of.js.gz +0 -0
- package/dist/ui/assets/chunk-ATLVNIR6-BEIIfJtC.js.gz +0 -0
- package/dist/ui/assets/chunk-B4BG7PRW-B8b6dQQ2.js.gz +0 -0
- package/dist/ui/assets/chunk-DI55MBZ5-BfATX3V8.js.gz +0 -0
- package/dist/ui/assets/chunk-EXTU4WIE-BKt6lPJM.js +0 -1
- package/dist/ui/assets/chunk-HN2XXSSU-BCHvD80g.js.gz +0 -0
- package/dist/ui/assets/chunk-JA3XYJ7Z-Cp6dqHnY.js.gz +0 -0
- package/dist/ui/assets/chunk-JZLCHNYA-cKMooY3y.js.gz +0 -0
- package/dist/ui/assets/chunk-MI3HLSF2-BlzO5wOE.js.gz +0 -0
- package/dist/ui/assets/chunk-N4CR4FBY-pASDorUx.js +0 -2
- package/dist/ui/assets/chunk-N4CR4FBY-pASDorUx.js.gz +0 -0
- package/dist/ui/assets/chunk-QXUST7PY-C9l0muI0.js.gz +0 -0
- package/dist/ui/assets/chunk-QZHKN3VN-CZskCFCf.js +0 -1
- package/dist/ui/assets/chunk-S3R3BYOJ-VJiLzt2o.js.gz +0 -0
- package/dist/ui/assets/chunk-TZMSLE5B-DZwI0C_2.js.gz +0 -0
- package/dist/ui/assets/classDiagram-2ON5EDUG-BFASUbmZ.js +0 -1
- package/dist/ui/assets/classDiagram-v2-WZHVMYZB-BFASUbmZ.js +0 -1
- package/dist/ui/assets/cose-bilkent-S5V4N54A-Ipik-oSD.js.gz +0 -0
- package/dist/ui/assets/dagre-6UL2VRFP-BDpyWQnh.js.gz +0 -0
- package/dist/ui/assets/dagre-CgA4KhUX.js.gz +0 -0
- package/dist/ui/assets/diagram-PSM6KHXK-B4GRzxLJ.js.gz +0 -0
- package/dist/ui/assets/diagram-QEK2KX5R-BWPW28XI.js.gz +0 -0
- package/dist/ui/assets/diagram-S2PKOQOG-BIHhcGoV.js.gz +0 -0
- package/dist/ui/assets/editor-C-HJ7Yw0.js.gz +0 -0
- package/dist/ui/assets/emoji-D8F6B62m.js.gz +0 -0
- package/dist/ui/assets/erDiagram-Q2GNP2WA-ubTaAFcK.js.gz +0 -0
- package/dist/ui/assets/flowDiagram-NV44I4VS-BHLCTYjI.js.gz +0 -0
- package/dist/ui/assets/ganttDiagram-LVOFAZNH-ClC3pay1.js.gz +0 -0
- package/dist/ui/assets/gitGraphDiagram-NY62KEGX-Clqpiswu.js.gz +0 -0
- package/dist/ui/assets/index-D9OElx9A.css +0 -1
- package/dist/ui/assets/index-D9OElx9A.css.gz +0 -0
- package/dist/ui/assets/index-DxPuzG7E.js +0 -350
- package/dist/ui/assets/index-DxPuzG7E.js.gz +0 -0
- package/dist/ui/assets/journeyDiagram-XKPGCS4Q-CrVickA2.js.gz +0 -0
- package/dist/ui/assets/kanban-definition-3W4ZIXB7-C_BsdHYL.js.gz +0 -0
- package/dist/ui/assets/knowledgeRoutes-CivaUqha.js.gz +0 -0
- package/dist/ui/assets/linear-BsjageUB.js.gz +0 -0
- package/dist/ui/assets/mermaid-parser.core-DZdP-NFq.js.gz +0 -0
- package/dist/ui/assets/mermaid.core-TNrI0pHG.js.gz +0 -0
- package/dist/ui/assets/message-BtWWJ9Af.js +0 -36
- package/dist/ui/assets/message-BtWWJ9Af.js.gz +0 -0
- package/dist/ui/assets/mindmap-definition-VGOIOE7T-BbmaUjMY.js.gz +0 -0
- package/dist/ui/assets/particles-Dv28pjOd.js.gz +0 -0
- package/dist/ui/assets/pieDiagram-ADFJNKIX-BbyjfYu8.js.gz +0 -0
- package/dist/ui/assets/quadrantDiagram-AYHSOK5B-Bc3GqMKz.js.gz +0 -0
- package/dist/ui/assets/reactflow-BVYPxNhc.js.gz +0 -0
- package/dist/ui/assets/requirementDiagram-UZGBJVZJ-BQaKKL09.js.gz +0 -0
- package/dist/ui/assets/sandpack-BQW_FQ7G.js.gz +0 -0
- package/dist/ui/assets/sankeyDiagram-TZEHDZUN-DiJvDvhb.js.gz +0 -0
- package/dist/ui/assets/sequenceDiagram-WL72ISMW-Dw32824o.js.gz +0 -0
- package/dist/ui/assets/stateDiagram-FKZM4ZOC-BCOpNdHV.js.gz +0 -0
- package/dist/ui/assets/stateDiagram-v2-4FDKWEC3-Cuqwvgfg.js +0 -1
- package/dist/ui/assets/syntax-VJrU5BEu.js.gz +0 -0
- package/dist/ui/assets/timeline-definition-IT6M3QCI-Jh_WZzXv.js.gz +0 -0
- package/dist/ui/assets/xychartDiagram-PRI3JC2R-CERc7Rdb.js.gz +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/services/gateway.ts
|
|
2
|
+
import { PublicBaseUrlNotConfiguredError, requirePublicBaseUrl } from "@agor/core/config";
|
|
2
3
|
import {
|
|
3
4
|
GatewayChannelRepository,
|
|
4
5
|
MCPServerRepository,
|
|
@@ -9,7 +10,9 @@ import {
|
|
|
9
10
|
} from "@agor/core/db";
|
|
10
11
|
import {
|
|
11
12
|
formatGatewayContext,
|
|
12
|
-
|
|
13
|
+
formatGatewayFollowUpRoutingMessage,
|
|
14
|
+
formatGatewaySessionCreatedMessage,
|
|
15
|
+
formatGatewaySystemPayload,
|
|
13
16
|
getConnector,
|
|
14
17
|
hasConnector,
|
|
15
18
|
normalizeOutbound,
|
|
@@ -17,6 +20,7 @@ import {
|
|
|
17
20
|
} from "@agor/core/gateway";
|
|
18
21
|
import { resolveSessionDefaults } from "@agor/core/sessions";
|
|
19
22
|
import { SessionStatus } from "@agor/core/types";
|
|
23
|
+
import { getSessionUrl } from "@agor/core/utils/url";
|
|
20
24
|
function hasListeningConfig(channel) {
|
|
21
25
|
const config = channel.config;
|
|
22
26
|
switch (channel.channel_type) {
|
|
@@ -24,10 +28,15 @@ function hasListeningConfig(channel) {
|
|
|
24
28
|
return !!config.app_token;
|
|
25
29
|
case "github":
|
|
26
30
|
return !!(config.app_id && config.private_key && config.installation_id && config.watch_repos?.length);
|
|
31
|
+
case "teams":
|
|
32
|
+
return !!(config.app_id && config.app_password);
|
|
27
33
|
default:
|
|
28
34
|
return false;
|
|
29
35
|
}
|
|
30
36
|
}
|
|
37
|
+
function isSlackThinkingPlaceholder(text) {
|
|
38
|
+
return /^thinking\s*\.{3}$/i.test(text.trim());
|
|
39
|
+
}
|
|
31
40
|
function buildGitHubInitialPrompt(threadId, text, metadata) {
|
|
32
41
|
try {
|
|
33
42
|
const { owner, repo, number } = parseGitHubThreadId(threadId);
|
|
@@ -102,6 +111,26 @@ function buildGatewayContext(channel, data) {
|
|
|
102
111
|
extras
|
|
103
112
|
};
|
|
104
113
|
}
|
|
114
|
+
case "teams": {
|
|
115
|
+
const conversationType = meta.teams_conversation_type;
|
|
116
|
+
const isPersonal = conversationType === "personal";
|
|
117
|
+
let channelKind;
|
|
118
|
+
if (isPersonal) {
|
|
119
|
+
channelKind = "DM";
|
|
120
|
+
} else if (conversationType === "channel") {
|
|
121
|
+
channelKind = "Channel";
|
|
122
|
+
} else if (conversationType === "groupChat") {
|
|
123
|
+
channelKind = "Group Chat";
|
|
124
|
+
}
|
|
125
|
+
const channelName = isPersonal ? void 0 : meta.teams_channel_name ?? meta.teams_team_name ?? void 0;
|
|
126
|
+
return {
|
|
127
|
+
platform: "teams",
|
|
128
|
+
channelName,
|
|
129
|
+
channelKind,
|
|
130
|
+
userName: meta.teams_user_name ?? void 0,
|
|
131
|
+
userEmail: meta.teams_user_email ?? void 0
|
|
132
|
+
};
|
|
133
|
+
}
|
|
105
134
|
default:
|
|
106
135
|
return {
|
|
107
136
|
platform: channel.channel_type,
|
|
@@ -109,7 +138,7 @@ function buildGatewayContext(channel, data) {
|
|
|
109
138
|
};
|
|
110
139
|
}
|
|
111
140
|
}
|
|
112
|
-
var GatewayService = class {
|
|
141
|
+
var GatewayService = class _GatewayService {
|
|
113
142
|
channelRepo;
|
|
114
143
|
threadMapRepo;
|
|
115
144
|
usersRepo;
|
|
@@ -133,6 +162,21 @@ var GatewayService = class {
|
|
|
133
162
|
* previous one — only the final message matters.
|
|
134
163
|
*/
|
|
135
164
|
githubMessageBuffer = /* @__PURE__ */ new Map();
|
|
165
|
+
/**
|
|
166
|
+
* Slack status updates are serialized and lightly throttled so concurrent
|
|
167
|
+
* tool/message hooks do not race while deleting/reposting the transient row.
|
|
168
|
+
* Terminal states always bypass this throttle.
|
|
169
|
+
*/
|
|
170
|
+
slackProgressLastUpdate = /* @__PURE__ */ new Map();
|
|
171
|
+
slackProgressQueues = /* @__PURE__ */ new Map();
|
|
172
|
+
slackStreamsByTask = /* @__PURE__ */ new Map();
|
|
173
|
+
slackStreamStatusRefreshLast = /* @__PURE__ */ new Map();
|
|
174
|
+
slackStreamedMessageIds = /* @__PURE__ */ new Set();
|
|
175
|
+
slackStreamedTaskIds = /* @__PURE__ */ new Set();
|
|
176
|
+
slackStreamTaskByMessage = /* @__PURE__ */ new Map();
|
|
177
|
+
static SLACK_PROGRESS_MIN_UPDATE_MS = 2500;
|
|
178
|
+
static SLACK_STREAM_STATUS_REFRESH_MS = 300;
|
|
179
|
+
static SLACK_STREAMED_MESSAGE_CACHE_MAX = 500;
|
|
136
180
|
constructor(db, app) {
|
|
137
181
|
this.channelRepo = new GatewayChannelRepository(db);
|
|
138
182
|
this.threadMapRepo = new ThreadSessionMapRepository(db);
|
|
@@ -153,21 +197,479 @@ var GatewayService = class {
|
|
|
153
197
|
);
|
|
154
198
|
}
|
|
155
199
|
/**
|
|
156
|
-
* Send a
|
|
200
|
+
* Send a system message to the platform thread (fire-and-forget).
|
|
157
201
|
* Useful for giving the user visibility into what's happening.
|
|
158
202
|
*/
|
|
159
|
-
|
|
203
|
+
sendSystemMessage(channel, threadId, text, opts) {
|
|
160
204
|
if (channel.channel_type === "github") return;
|
|
205
|
+
if (channel.channel_type === "slack" && opts?.suppressSlack) return;
|
|
161
206
|
if (!hasConnector(channel.channel_type)) return;
|
|
162
207
|
try {
|
|
163
|
-
const connector = getConnector(channel.channel_type, channel.config);
|
|
208
|
+
const connector = this.activeListeners.get(channel.id) ?? getConnector(channel.channel_type, channel.config);
|
|
164
209
|
connector.sendMessage({
|
|
165
210
|
threadId,
|
|
166
|
-
|
|
211
|
+
...formatGatewaySystemPayload(channel.channel_type, text)
|
|
167
212
|
}).catch((err) => console.warn("[gateway] Debug message failed:", err));
|
|
168
213
|
} catch {
|
|
169
214
|
}
|
|
170
215
|
}
|
|
216
|
+
truncateSlackInline(value, maxChars = 70) {
|
|
217
|
+
const singleLine = value.replace(/\s+/g, " ").trim();
|
|
218
|
+
if (singleLine.length <= maxChars) return singleLine;
|
|
219
|
+
return `${singleLine.slice(0, Math.max(0, maxChars - 1))}\u2026`;
|
|
220
|
+
}
|
|
221
|
+
formatSlackLoadingMessage(text) {
|
|
222
|
+
return this.truncateSlackInline(text, 50);
|
|
223
|
+
}
|
|
224
|
+
makeSlackThreadIdForMessage(rootThreadId, messageTs) {
|
|
225
|
+
const lastHyphen = rootThreadId.lastIndexOf("-");
|
|
226
|
+
if (lastHyphen === -1) return null;
|
|
227
|
+
const channelId = rootThreadId.slice(0, lastHyphen);
|
|
228
|
+
if (!channelId || !messageTs) return null;
|
|
229
|
+
return `${channelId}-${messageTs}`;
|
|
230
|
+
}
|
|
231
|
+
async addSlackThreadAlias(mapping, messageTs, reason) {
|
|
232
|
+
const aliasThreadId = this.makeSlackThreadIdForMessage(mapping.thread_id, messageTs);
|
|
233
|
+
if (!aliasThreadId || aliasThreadId === mapping.thread_id) return;
|
|
234
|
+
const freshMapping = await this.threadMapRepo.findById(mapping.id);
|
|
235
|
+
const metadata = (freshMapping ?? mapping).metadata ?? {};
|
|
236
|
+
const aliases = Array.isArray(metadata.slack_thread_aliases) ? metadata.slack_thread_aliases.filter((alias) => typeof alias === "string") : [];
|
|
237
|
+
if (aliases.includes(aliasThreadId)) return;
|
|
238
|
+
await this.threadMapRepo.updateMetadata(mapping.id, {
|
|
239
|
+
...metadata,
|
|
240
|
+
slack_thread_aliases: [...aliases, aliasThreadId].slice(-50),
|
|
241
|
+
slack_thread_alias_last_reason: reason
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
getActiveSlackThreadId(mapping) {
|
|
245
|
+
const metadata = mapping.metadata ?? {};
|
|
246
|
+
return typeof metadata.slack_active_thread_id === "string" ? metadata.slack_active_thread_id : mapping.thread_id;
|
|
247
|
+
}
|
|
248
|
+
pickSlackRoutingMetadata(metadata) {
|
|
249
|
+
return {
|
|
250
|
+
...typeof metadata.slack_active_thread_id === "string" ? { slack_active_thread_id: metadata.slack_active_thread_id } : {},
|
|
251
|
+
...Array.isArray(metadata.slack_thread_aliases) ? { slack_thread_aliases: metadata.slack_thread_aliases } : {},
|
|
252
|
+
...typeof metadata.slack_thread_alias_last_reason === "string" ? { slack_thread_alias_last_reason: metadata.slack_thread_alias_last_reason } : {}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
async findSlackThreadAliasMapping(channelId, threadId) {
|
|
256
|
+
const mappings = channelId ? await this.threadMapRepo.findByChannel(channelId, "active") : (await this.threadMapRepo.findAll()).filter((mapping) => mapping.status === "active");
|
|
257
|
+
return mappings.find((mapping) => {
|
|
258
|
+
const metadata = mapping.metadata ?? {};
|
|
259
|
+
return Array.isArray(metadata.slack_thread_aliases) && metadata.slack_thread_aliases.includes(threadId);
|
|
260
|
+
}) ?? null;
|
|
261
|
+
}
|
|
262
|
+
parseGatewayTodos(raw) {
|
|
263
|
+
const candidate = typeof raw === "string" ? (() => {
|
|
264
|
+
try {
|
|
265
|
+
return JSON.parse(raw);
|
|
266
|
+
} catch {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
})() : raw;
|
|
270
|
+
if (!Array.isArray(candidate)) return [];
|
|
271
|
+
return candidate.map((item) => {
|
|
272
|
+
if (!item || typeof item !== "object") return null;
|
|
273
|
+
const record = item;
|
|
274
|
+
const content = typeof record.content === "string" ? record.content : typeof record.activeForm === "string" ? record.activeForm : null;
|
|
275
|
+
if (!content) return null;
|
|
276
|
+
const status = record.status;
|
|
277
|
+
if (status !== "pending" && status !== "in_progress" && status !== "completed" && status !== "stopped" && status !== "unknown") {
|
|
278
|
+
return { content, status: "pending" };
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
content,
|
|
282
|
+
...typeof record.activeForm === "string" ? { activeForm: record.activeForm } : {},
|
|
283
|
+
status
|
|
284
|
+
};
|
|
285
|
+
}).filter((item) => item !== null);
|
|
286
|
+
}
|
|
287
|
+
formatSlackToolSummary(toolName, input) {
|
|
288
|
+
if (!toolName) return "Waiting for the agent...";
|
|
289
|
+
const str = (value) => typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
290
|
+
const preview = (value, maxChars = 70) => {
|
|
291
|
+
const text = str(value);
|
|
292
|
+
return text ? this.truncateSlackInline(text, maxChars) : void 0;
|
|
293
|
+
};
|
|
294
|
+
const withPreview = (value) => value ? `\`${toolName}\` ${value}` : `\`${toolName}\``;
|
|
295
|
+
if (toolName === "TodoWrite") {
|
|
296
|
+
const todos = this.parseGatewayTodos(input?.todos);
|
|
297
|
+
if (todos.length > 0) {
|
|
298
|
+
const completed = todos.filter((todo) => todo.status === "completed").length;
|
|
299
|
+
const inProgress = todos.filter((todo) => todo.status === "in_progress").length;
|
|
300
|
+
const parts = [`${completed}/${todos.length} done`];
|
|
301
|
+
if (inProgress > 0) parts.push(`${inProgress} in progress`);
|
|
302
|
+
return withPreview(parts.join(", "));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
switch (toolName) {
|
|
306
|
+
case "Read":
|
|
307
|
+
case "Write":
|
|
308
|
+
case "Edit":
|
|
309
|
+
case "NotebookEdit":
|
|
310
|
+
return withPreview(preview(input?.file_path));
|
|
311
|
+
case "Bash":
|
|
312
|
+
case "exec_command":
|
|
313
|
+
return withPreview(preview(input?.description) ?? preview(input?.command));
|
|
314
|
+
case "Grep":
|
|
315
|
+
case "Glob":
|
|
316
|
+
return withPreview(preview(input?.pattern));
|
|
317
|
+
case "ToolSearch":
|
|
318
|
+
case "WebSearch":
|
|
319
|
+
case "web_search":
|
|
320
|
+
return withPreview(preview(input?.query));
|
|
321
|
+
case "WebFetch":
|
|
322
|
+
return withPreview(preview(input?.url));
|
|
323
|
+
case "Agent":
|
|
324
|
+
return withPreview(preview(input?.description));
|
|
325
|
+
case "Skill":
|
|
326
|
+
case "SlashCommand":
|
|
327
|
+
return withPreview(preview(input?.skill) ?? preview(input?.name));
|
|
328
|
+
case "Task":
|
|
329
|
+
return withPreview(preview(str(input?.prompt)?.split("\n")[0], 100));
|
|
330
|
+
case "edit_files": {
|
|
331
|
+
const changes = input?.changes;
|
|
332
|
+
if (Array.isArray(changes) && changes.length > 0) {
|
|
333
|
+
if (changes.length === 1) {
|
|
334
|
+
const change = changes[0];
|
|
335
|
+
const kind = str(change.kind) ?? "update";
|
|
336
|
+
const path = str(change.path) ?? "";
|
|
337
|
+
return withPreview(this.truncateSlackInline(`${kind} ${path}`.trim(), 70));
|
|
338
|
+
}
|
|
339
|
+
return withPreview(`${changes.length} files`);
|
|
340
|
+
}
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return `\`${toolName}\``;
|
|
345
|
+
}
|
|
346
|
+
buildSlackAssistantStatus(data, existingMetadata) {
|
|
347
|
+
if (data.state === "done") return "";
|
|
348
|
+
if (data.state === "failed") return "ran into an error.";
|
|
349
|
+
if (data.state === "queued") {
|
|
350
|
+
const position = typeof data.queue_position === "number" ? ` at position ${data.queue_position}` : "";
|
|
351
|
+
return `is queued${position}.`;
|
|
352
|
+
}
|
|
353
|
+
const latestToolName = data.tool_name ?? (typeof existingMetadata.slack_status_tool_name === "string" ? existingMetadata.slack_status_tool_name : void 0);
|
|
354
|
+
return latestToolName ? `is using ${this.truncateSlackInline(latestToolName, 40)}.` : "is working on your request.";
|
|
355
|
+
}
|
|
356
|
+
buildSlackAssistantLoadingMessage(data, existingMetadata) {
|
|
357
|
+
if (data.state === "done") return void 0;
|
|
358
|
+
if (data.state === "failed") return this.formatSlackLoadingMessage("Agor ran into an error.");
|
|
359
|
+
if (data.state === "queued") return this.formatSlackLoadingMessage("Queued in Agor\u2026");
|
|
360
|
+
const latestToolSummary = data.tool_name || data.tool_input ? this.formatSlackToolSummary(data.tool_name, data.tool_input) : typeof existingMetadata.slack_status_tool_summary === "string" ? existingMetadata.slack_status_tool_summary : void 0;
|
|
361
|
+
if (latestToolSummary) {
|
|
362
|
+
return this.formatSlackLoadingMessage(`Using ${latestToolSummary.replace(/`/g, "")}\u2026`);
|
|
363
|
+
}
|
|
364
|
+
const latestToolName = data.tool_name ?? (typeof existingMetadata.slack_status_tool_name === "string" ? existingMetadata.slack_status_tool_name : void 0);
|
|
365
|
+
if (latestToolName) {
|
|
366
|
+
return this.formatSlackLoadingMessage(`Using ${latestToolName}\u2026`);
|
|
367
|
+
}
|
|
368
|
+
return this.formatSlackLoadingMessage("Working in Agor\u2026");
|
|
369
|
+
}
|
|
370
|
+
stripSlackProgressMetadata(metadata) {
|
|
371
|
+
const {
|
|
372
|
+
slack_status_message_ts: _statusTs,
|
|
373
|
+
slack_status_started_at: _startedAt,
|
|
374
|
+
slack_status_tool_name: _toolName,
|
|
375
|
+
slack_status_tool_summary: _toolSummary,
|
|
376
|
+
slack_status_todos: _todos,
|
|
377
|
+
slack_status_state: _state,
|
|
378
|
+
slack_status_task_id: _taskId,
|
|
379
|
+
...rest
|
|
380
|
+
} = metadata;
|
|
381
|
+
return rest;
|
|
382
|
+
}
|
|
383
|
+
stripSlackProgressMessageMetadata(metadata) {
|
|
384
|
+
const { slack_status_message_ts: _statusTs, ...rest } = metadata;
|
|
385
|
+
return rest;
|
|
386
|
+
}
|
|
387
|
+
async refreshSlackAssistantStatusAfterStreamStart(threadId, connector, sessionId, taskId, metadata) {
|
|
388
|
+
if (!connector.setThreadStatus) return;
|
|
389
|
+
const progress = {
|
|
390
|
+
session_id: sessionId,
|
|
391
|
+
state: "working",
|
|
392
|
+
...taskId ? { task_id: taskId } : {}
|
|
393
|
+
};
|
|
394
|
+
const loadingMessage = this.buildSlackAssistantLoadingMessage(progress, metadata) ?? this.formatSlackLoadingMessage("Writing response\u2026");
|
|
395
|
+
await connector.setThreadStatus({
|
|
396
|
+
threadId,
|
|
397
|
+
status: this.buildSlackAssistantStatus(progress, metadata),
|
|
398
|
+
loadingMessages: [loadingMessage],
|
|
399
|
+
iconEmoji: ":hourglass_flowing_sand:"
|
|
400
|
+
});
|
|
401
|
+
if (taskId) {
|
|
402
|
+
this.slackStreamStatusRefreshLast.set(taskId, Date.now());
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
async refreshSlackAssistantStatusAfterStreamAppend(threadId, connector, sessionId, taskId, metadata) {
|
|
406
|
+
if (!taskId) return;
|
|
407
|
+
const now = Date.now();
|
|
408
|
+
const lastRefresh = this.slackStreamStatusRefreshLast.get(taskId) ?? 0;
|
|
409
|
+
if (now - lastRefresh < _GatewayService.SLACK_STREAM_STATUS_REFRESH_MS) return;
|
|
410
|
+
this.slackStreamStatusRefreshLast.set(taskId, now);
|
|
411
|
+
await this.refreshSlackAssistantStatusAfterStreamStart(
|
|
412
|
+
threadId,
|
|
413
|
+
connector,
|
|
414
|
+
sessionId,
|
|
415
|
+
taskId,
|
|
416
|
+
metadata
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Update Slack's native assistant status/stream chrome for a gateway thread.
|
|
421
|
+
*
|
|
422
|
+
* We expose a short, Slack-safe tool summary and TodoWrite plan state, never
|
|
423
|
+
* raw JSON args/results. Raw tool inputs are already persisted in Agor's
|
|
424
|
+
* transcript; Slack receives only a compact truncated preview.
|
|
425
|
+
*/
|
|
426
|
+
async updateProgress(data) {
|
|
427
|
+
const previous = this.slackProgressQueues.get(data.session_id) ?? Promise.resolve();
|
|
428
|
+
const next = previous.catch(() => void 0).then(() => this.updateProgressNow(data));
|
|
429
|
+
this.slackProgressQueues.set(data.session_id, next);
|
|
430
|
+
try {
|
|
431
|
+
await next;
|
|
432
|
+
} finally {
|
|
433
|
+
if (this.slackProgressQueues.get(data.session_id) === next) {
|
|
434
|
+
this.slackProgressQueues.delete(data.session_id);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
wasMessageStreamedToSlack(messageId) {
|
|
439
|
+
return this.slackStreamedMessageIds.has(messageId);
|
|
440
|
+
}
|
|
441
|
+
wasTaskStreamedToSlack(taskId) {
|
|
442
|
+
return !!taskId && this.slackStreamedTaskIds.has(taskId);
|
|
443
|
+
}
|
|
444
|
+
markMessageStreamedToSlack(messageId) {
|
|
445
|
+
this.slackStreamedMessageIds.add(messageId);
|
|
446
|
+
if (this.slackStreamedMessageIds.size > _GatewayService.SLACK_STREAMED_MESSAGE_CACHE_MAX) {
|
|
447
|
+
const oldest = this.slackStreamedMessageIds.values().next().value;
|
|
448
|
+
if (oldest) this.slackStreamedMessageIds.delete(oldest);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
markTaskStreamedToSlack(taskId) {
|
|
452
|
+
if (!taskId) return;
|
|
453
|
+
this.slackStreamedTaskIds.add(taskId);
|
|
454
|
+
if (this.slackStreamedTaskIds.size > _GatewayService.SLACK_STREAMED_MESSAGE_CACHE_MAX) {
|
|
455
|
+
const oldest = this.slackStreamedTaskIds.values().next().value;
|
|
456
|
+
if (oldest) this.slackStreamedTaskIds.delete(oldest);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
async stopSlackTaskStream(taskId, connector) {
|
|
460
|
+
if (!taskId) return;
|
|
461
|
+
const stream = this.slackStreamsByTask.get(taskId);
|
|
462
|
+
if (!stream) return;
|
|
463
|
+
if (!stream.hasContent && connector.deleteMessage) {
|
|
464
|
+
await connector.deleteMessage({
|
|
465
|
+
threadId: stream.threadId,
|
|
466
|
+
messageId: stream.ts
|
|
467
|
+
});
|
|
468
|
+
this.slackStreamsByTask.delete(taskId);
|
|
469
|
+
this.slackStreamStatusRefreshLast.delete(taskId);
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const streamConnector = connector;
|
|
473
|
+
if (!streamConnector.stopStream) return;
|
|
474
|
+
await streamConnector.stopStream({
|
|
475
|
+
threadId: stream.threadId,
|
|
476
|
+
ts: stream.ts
|
|
477
|
+
});
|
|
478
|
+
this.slackStreamsByTask.delete(taskId);
|
|
479
|
+
this.slackStreamStatusRefreshLast.delete(taskId);
|
|
480
|
+
}
|
|
481
|
+
async updateProgressNow(data) {
|
|
482
|
+
if (!this.hasActiveChannels) return;
|
|
483
|
+
const mapping = await this.threadMapRepo.findBySession(data.session_id);
|
|
484
|
+
if (!mapping) return;
|
|
485
|
+
const channel = await this.channelRepo.findById(mapping.channel_id);
|
|
486
|
+
if (!channel?.enabled || channel.channel_type !== "slack") return;
|
|
487
|
+
const now = Date.now();
|
|
488
|
+
const isTerminal = data.state === "done" || data.state === "failed";
|
|
489
|
+
const metadata = mapping.metadata ?? {};
|
|
490
|
+
const isNewTask = typeof data.task_id === "string" && data.task_id !== metadata.slack_status_task_id;
|
|
491
|
+
const isRestartingAfterTerminal = (data.state === "queued" || data.state === "working") && (metadata.slack_status_state === "done" || metadata.slack_status_state === "failed");
|
|
492
|
+
const lastUpdate = this.slackProgressLastUpdate.get(data.session_id) ?? 0;
|
|
493
|
+
if (!isTerminal && !data.tool_name && !isNewTask && !isRestartingAfterTerminal && now - lastUpdate < _GatewayService.SLACK_PROGRESS_MIN_UPDATE_MS) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
this.slackProgressLastUpdate.set(data.session_id, now);
|
|
497
|
+
const statusStartedAt = isNewTask || isRestartingAfterTerminal ? new Date(now).toISOString() : typeof metadata.slack_status_started_at === "string" ? metadata.slack_status_started_at : new Date(now).toISOString();
|
|
498
|
+
const toolTodos = data.tool_name === "TodoWrite" ? this.parseGatewayTodos(data.tool_input?.todos) : [];
|
|
499
|
+
const toolSummary = data.tool_name || data.tool_input ? this.formatSlackToolSummary(data.tool_name, data.tool_input) : void 0;
|
|
500
|
+
const baseMetadata = isNewTask || isRestartingAfterTerminal ? this.stripSlackProgressMetadata(metadata) : metadata;
|
|
501
|
+
const metadataWithStart = {
|
|
502
|
+
...baseMetadata,
|
|
503
|
+
slack_status_started_at: statusStartedAt,
|
|
504
|
+
slack_status_state: data.state,
|
|
505
|
+
...data.task_id ? { slack_status_task_id: data.task_id } : {},
|
|
506
|
+
...data.tool_name ? { slack_status_tool_name: data.tool_name } : {},
|
|
507
|
+
...toolSummary ? { slack_status_tool_summary: toolSummary } : {},
|
|
508
|
+
...toolTodos.length > 0 ? { slack_status_todos: toolTodos } : {}
|
|
509
|
+
};
|
|
510
|
+
const connector = this.activeListeners.get(channel.id) ?? getConnector(channel.channel_type, channel.config);
|
|
511
|
+
const activeTaskId = typeof metadata.slack_status_task_id === "string" ? metadata.slack_status_task_id : void 0;
|
|
512
|
+
try {
|
|
513
|
+
if (isTerminal) {
|
|
514
|
+
try {
|
|
515
|
+
await this.stopSlackTaskStream(activeTaskId, connector);
|
|
516
|
+
} catch (error) {
|
|
517
|
+
console.warn("[gateway] Failed to stop Slack task stream:", error);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const freshMapping = await this.threadMapRepo.findById(mapping.id);
|
|
521
|
+
const freshMetadata = freshMapping?.metadata ?? {};
|
|
522
|
+
const metadataForWrite = {
|
|
523
|
+
...isTerminal ? this.stripSlackProgressMetadata(metadataWithStart) : this.stripSlackProgressMessageMetadata(metadataWithStart),
|
|
524
|
+
...this.pickSlackRoutingMetadata(freshMetadata)
|
|
525
|
+
};
|
|
526
|
+
const slackThreadId = typeof metadataForWrite.slack_active_thread_id === "string" ? metadataForWrite.slack_active_thread_id : mapping.thread_id;
|
|
527
|
+
await this.threadMapRepo.updateMetadata(mapping.id, metadataForWrite);
|
|
528
|
+
if (!connector.setThreadStatus) return;
|
|
529
|
+
try {
|
|
530
|
+
const loadingMessage = this.buildSlackAssistantLoadingMessage(data, metadataWithStart);
|
|
531
|
+
await connector.setThreadStatus({
|
|
532
|
+
threadId: slackThreadId,
|
|
533
|
+
status: this.buildSlackAssistantStatus(data, metadataWithStart),
|
|
534
|
+
loadingMessages: loadingMessage ? [loadingMessage] : void 0,
|
|
535
|
+
iconEmoji: ":hourglass_flowing_sand:"
|
|
536
|
+
});
|
|
537
|
+
} catch (error) {
|
|
538
|
+
console.warn("[gateway] Failed to set Slack assistant status:", error);
|
|
539
|
+
}
|
|
540
|
+
} catch (error) {
|
|
541
|
+
console.warn("[gateway] Failed to update Slack progress status:", error);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
async handleMessageStreamingEvent(event, data) {
|
|
545
|
+
if (!this.hasActiveChannels) return;
|
|
546
|
+
const sessionId = typeof data.session_id === "string" ? data.session_id : void 0;
|
|
547
|
+
const messageId = typeof data.message_id === "string" ? data.message_id : void 0;
|
|
548
|
+
const taskId = typeof data.task_id === "string" ? data.task_id : void 0;
|
|
549
|
+
if (!sessionId || !messageId) return;
|
|
550
|
+
if (event === "streaming:start") {
|
|
551
|
+
if (taskId) {
|
|
552
|
+
this.slackStreamTaskByMessage.set(messageId, taskId);
|
|
553
|
+
}
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const taskKey = taskId ?? this.slackStreamTaskByMessage.get(messageId) ?? messageId;
|
|
557
|
+
const mapping = await this.threadMapRepo.findBySession(sessionId);
|
|
558
|
+
if (!mapping) return;
|
|
559
|
+
const channel = await this.channelRepo.findById(mapping.channel_id);
|
|
560
|
+
if (!channel?.enabled || channel.channel_type !== "slack") return;
|
|
561
|
+
const connector = this.activeListeners.get(channel.id) ?? getConnector(channel.channel_type, channel.config);
|
|
562
|
+
const streamConnector = connector;
|
|
563
|
+
if (!streamConnector.startStream || !streamConnector.appendStream) {
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
try {
|
|
567
|
+
const metadata = mapping.metadata ?? {};
|
|
568
|
+
const slackThreadId = this.getActiveSlackThreadId(mapping);
|
|
569
|
+
const recipientUserId = typeof metadata.slack_user_id === "string" ? metadata.slack_user_id : void 0;
|
|
570
|
+
const recipientTeamId = typeof metadata.slack_team_id === "string" ? metadata.slack_team_id : void 0;
|
|
571
|
+
if (event === "streaming:chunk") {
|
|
572
|
+
const chunk = typeof data.chunk === "string" ? data.chunk : "";
|
|
573
|
+
if (!chunk) return;
|
|
574
|
+
if (isSlackThinkingPlaceholder(chunk)) {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const existing = this.slackStreamsByTask.get(taskKey);
|
|
578
|
+
if (!existing) {
|
|
579
|
+
const ts = await streamConnector.startStream({
|
|
580
|
+
threadId: slackThreadId,
|
|
581
|
+
text: chunk,
|
|
582
|
+
recipientUserId,
|
|
583
|
+
recipientTeamId
|
|
584
|
+
});
|
|
585
|
+
this.slackStreamsByTask.set(taskKey, {
|
|
586
|
+
threadId: slackThreadId,
|
|
587
|
+
ts,
|
|
588
|
+
hasContent: true,
|
|
589
|
+
taskId: taskKey,
|
|
590
|
+
lastMessageId: messageId
|
|
591
|
+
});
|
|
592
|
+
await this.addSlackThreadAlias(mapping, ts, "stream");
|
|
593
|
+
try {
|
|
594
|
+
await this.refreshSlackAssistantStatusAfterStreamStart(
|
|
595
|
+
slackThreadId,
|
|
596
|
+
connector,
|
|
597
|
+
sessionId,
|
|
598
|
+
taskKey,
|
|
599
|
+
metadata
|
|
600
|
+
);
|
|
601
|
+
} catch (error) {
|
|
602
|
+
console.warn("[gateway] Failed to refresh Slack status after stream start:", error);
|
|
603
|
+
}
|
|
604
|
+
this.markMessageStreamedToSlack(messageId);
|
|
605
|
+
this.markTaskStreamedToSlack(taskKey);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
const text = existing.hasContent && existing.lastMessageId && existing.lastMessageId !== messageId ? `
|
|
609
|
+
|
|
610
|
+
${chunk}` : chunk;
|
|
611
|
+
await streamConnector.appendStream({
|
|
612
|
+
threadId: existing.threadId,
|
|
613
|
+
ts: existing.ts,
|
|
614
|
+
text
|
|
615
|
+
});
|
|
616
|
+
try {
|
|
617
|
+
await this.refreshSlackAssistantStatusAfterStreamAppend(
|
|
618
|
+
existing.threadId,
|
|
619
|
+
connector,
|
|
620
|
+
sessionId,
|
|
621
|
+
taskKey,
|
|
622
|
+
metadata
|
|
623
|
+
);
|
|
624
|
+
} catch (error) {
|
|
625
|
+
console.warn("[gateway] Failed to refresh Slack status after stream append:", error);
|
|
626
|
+
}
|
|
627
|
+
existing.hasContent = true;
|
|
628
|
+
existing.lastMessageId = messageId;
|
|
629
|
+
this.markMessageStreamedToSlack(messageId);
|
|
630
|
+
this.markTaskStreamedToSlack(taskKey);
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
if (event === "streaming:end") {
|
|
634
|
+
const existing = this.slackStreamsByTask.get(taskKey);
|
|
635
|
+
if (existing?.hasContent) {
|
|
636
|
+
this.markMessageStreamedToSlack(messageId);
|
|
637
|
+
this.markTaskStreamedToSlack(taskKey);
|
|
638
|
+
}
|
|
639
|
+
this.slackStreamTaskByMessage.delete(messageId);
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (event === "streaming:error") {
|
|
643
|
+
this.slackStreamTaskByMessage.delete(messageId);
|
|
644
|
+
}
|
|
645
|
+
} catch (error) {
|
|
646
|
+
this.slackStreamsByTask.delete(taskKey);
|
|
647
|
+
this.slackStreamTaskByMessage.delete(messageId);
|
|
648
|
+
console.warn("[gateway] Failed to mirror message stream to Slack:", error);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
async fetchExistingSessionUrlForGatewayUser(sessionId, user) {
|
|
652
|
+
try {
|
|
653
|
+
const baseUrl = await requirePublicBaseUrl();
|
|
654
|
+
return getSessionUrl(sessionId, baseUrl);
|
|
655
|
+
} catch (error) {
|
|
656
|
+
if (!(error instanceof PublicBaseUrlNotConfiguredError)) {
|
|
657
|
+
console.warn("[gateway] Failed to build public session URL:", error);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
try {
|
|
661
|
+
const sessionsService = this.app.service("sessions");
|
|
662
|
+
const sessionWithUrl = await sessionsService.get(sessionId, { user });
|
|
663
|
+
const sessionUrl = sessionWithUrl.url || null;
|
|
664
|
+
if (!sessionUrl) return null;
|
|
665
|
+
const hostname = new URL(sessionUrl).hostname;
|
|
666
|
+
if (hostname === "0.0.0.0") return null;
|
|
667
|
+
return sessionUrl;
|
|
668
|
+
} catch (error) {
|
|
669
|
+
console.warn("[gateway] Failed to fetch session URL:", error);
|
|
670
|
+
return null;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
171
673
|
/**
|
|
172
674
|
* Inbound routing: platform → session
|
|
173
675
|
*
|
|
@@ -182,12 +684,27 @@ var GatewayService = class {
|
|
|
182
684
|
if (!channel.enabled) {
|
|
183
685
|
throw new Error("Channel is disabled");
|
|
184
686
|
}
|
|
185
|
-
|
|
687
|
+
let existingMapping = await this.threadMapRepo.findByChannelAndThread(
|
|
186
688
|
channel.id,
|
|
187
689
|
data.thread_id
|
|
188
690
|
);
|
|
691
|
+
if (!existingMapping && channel.channel_type === "slack") {
|
|
692
|
+
existingMapping = await this.findSlackThreadAliasMapping(channel.id, data.thread_id);
|
|
693
|
+
if (existingMapping) {
|
|
694
|
+
console.log(
|
|
695
|
+
`[gateway] Found Slack thread alias: ${data.thread_id} \u2192 ${existingMapping.thread_id}`
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
if (existingMapping && channel.channel_type === "slack") {
|
|
700
|
+
console.log(
|
|
701
|
+
`[gateway] Slack inbound thread ${data.thread_id} \u2192 session ${shortId(existingMapping.session_id)} (root ${existingMapping.thread_id})`
|
|
702
|
+
);
|
|
703
|
+
}
|
|
189
704
|
if (!existingMapping) {
|
|
190
|
-
const
|
|
705
|
+
const exactThreadMapping = await this.threadMapRepo.findByThread(data.thread_id);
|
|
706
|
+
const aliasThreadMapping = channel.channel_type === "slack" ? await this.findSlackThreadAliasMapping(void 0, data.thread_id) : null;
|
|
707
|
+
const otherChannelMapping = exactThreadMapping && exactThreadMapping.channel_id !== channel.id ? exactThreadMapping : aliasThreadMapping && aliasThreadMapping.channel_id !== channel.id ? aliasThreadMapping : null;
|
|
191
708
|
if (otherChannelMapping) {
|
|
192
709
|
console.log(
|
|
193
710
|
`[gateway] IGNORED: Thread ${data.thread_id} owned by channel ${shortId(otherChannelMapping.channel_id)}, not ours (${shortId(channel.id)}). Silently dropping.`
|
|
@@ -220,7 +737,7 @@ var GatewayService = class {
|
|
|
220
737
|
console.error(
|
|
221
738
|
`[gateway] Channel "${channel.name}" has no agor_user_id and alignment is OFF. Cannot process message.`
|
|
222
739
|
);
|
|
223
|
-
this.
|
|
740
|
+
this.sendSystemMessage(channel, data.thread_id, errMsg);
|
|
224
741
|
if (channel.channel_type === "github" && data.metadata?.processing_comment_id) {
|
|
225
742
|
try {
|
|
226
743
|
const connector = getConnector(channel.channel_type, channel.config);
|
|
@@ -244,7 +761,7 @@ var GatewayService = class {
|
|
|
244
761
|
if (alignSlackUsers) {
|
|
245
762
|
if (data.metadata?.slack_user_email && typeof data.metadata.slack_user_email === "string") {
|
|
246
763
|
const email = data.metadata.slack_user_email.toLowerCase().trim();
|
|
247
|
-
const matchedUser = await this.usersRepo.
|
|
764
|
+
const matchedUser = await this.usersRepo.findByEmailForAlignment(email);
|
|
248
765
|
if (matchedUser) {
|
|
249
766
|
console.log(
|
|
250
767
|
`[gateway] Slack user aligned: ${email} \u2192 Agor user ${shortId(matchedUser.user_id)} (${matchedUser.name || matchedUser.email})`
|
|
@@ -252,7 +769,7 @@ var GatewayService = class {
|
|
|
252
769
|
user = await usersService.get(matchedUser.user_id);
|
|
253
770
|
} else {
|
|
254
771
|
console.log(`[gateway] Slack user alignment failed: no Agor user with email ${email}`);
|
|
255
|
-
this.
|
|
772
|
+
this.sendSystemMessage(
|
|
256
773
|
channel,
|
|
257
774
|
data.thread_id,
|
|
258
775
|
`User ${email} doesn't have an Agor account. Ask an admin to create an account with this email, or disable user alignment.`
|
|
@@ -267,7 +784,7 @@ var GatewayService = class {
|
|
|
267
784
|
console.log(
|
|
268
785
|
`[gateway] Slack user alignment failed: could not resolve email for Slack user ${data.user_name ?? "unknown"} (thread=${data.thread_id})`
|
|
269
786
|
);
|
|
270
|
-
this.
|
|
787
|
+
this.sendSystemMessage(
|
|
271
788
|
channel,
|
|
272
789
|
data.thread_id,
|
|
273
790
|
"Couldn't resolve your Slack identity. The bot may be missing the `users:read.email` scope, or your Slack profile has no email. Ask an admin to check the bot's scopes."
|
|
@@ -285,7 +802,7 @@ var GatewayService = class {
|
|
|
285
802
|
const userMap = channelConfig.user_map;
|
|
286
803
|
const mappedEmail = githubLogin && userMap?.[githubLogin] ? userMap[githubLogin].toLowerCase().trim() : null;
|
|
287
804
|
if (mappedEmail) {
|
|
288
|
-
const matchedUser = await this.usersRepo.
|
|
805
|
+
const matchedUser = await this.usersRepo.findByEmailForAlignment(mappedEmail);
|
|
289
806
|
if (matchedUser) {
|
|
290
807
|
console.log(
|
|
291
808
|
`[gateway] GitHub user aligned via user_map: ${githubLogin} \u2192 ${mappedEmail} \u2192 Agor user ${shortId(matchedUser.user_id)}`
|
|
@@ -301,7 +818,7 @@ var GatewayService = class {
|
|
|
301
818
|
if (!resolved) {
|
|
302
819
|
const githubEmail = data.metadata?.github_user_email && typeof data.metadata.github_user_email === "string" ? data.metadata.github_user_email.toLowerCase().trim() : null;
|
|
303
820
|
if (githubEmail) {
|
|
304
|
-
const matchedUser = await this.usersRepo.
|
|
821
|
+
const matchedUser = await this.usersRepo.findByEmailForAlignment(githubEmail);
|
|
305
822
|
if (matchedUser) {
|
|
306
823
|
console.log(
|
|
307
824
|
`[gateway] GitHub user aligned via email: ${githubLogin} (${githubEmail}) \u2192 Agor user ${shortId(matchedUser.user_id)}`
|
|
@@ -359,24 +876,34 @@ var GatewayService = class {
|
|
|
359
876
|
if (existingMapping) {
|
|
360
877
|
sessionId = existingMapping.session_id;
|
|
361
878
|
await this.threadMapRepo.updateLastMessage(existingMapping.id);
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
|
|
879
|
+
const existingMetadata = existingMapping.metadata ?? {};
|
|
880
|
+
await this.threadMapRepo.updateMetadata(existingMapping.id, {
|
|
881
|
+
...existingMetadata,
|
|
882
|
+
...data.metadata?.processing_comment_id ? { processing_comment_id: data.metadata.processing_comment_id } : {},
|
|
883
|
+
...typeof data.metadata?.slack_user_id === "string" ? { slack_user_id: data.metadata.slack_user_id } : {},
|
|
884
|
+
...typeof data.metadata?.slack_team_id === "string" ? { slack_team_id: data.metadata.slack_team_id } : {},
|
|
885
|
+
...channel.channel_type === "slack" ? { slack_active_thread_id: data.thread_id } : {}
|
|
886
|
+
});
|
|
887
|
+
if (channel.channel_type === "slack") {
|
|
888
|
+
console.log(
|
|
889
|
+
`[gateway] Slack active outbound thread for session ${shortId(sessionId)} set to ${data.thread_id}`
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
const sessionUrl = await this.fetchExistingSessionUrlForGatewayUser(sessionId, user);
|
|
893
|
+
if (sessionUrl) {
|
|
894
|
+
this.sendSystemMessage(
|
|
895
|
+
channel,
|
|
896
|
+
data.thread_id,
|
|
897
|
+
formatGatewayFollowUpRoutingMessage(sessionId, sessionUrl)
|
|
898
|
+
);
|
|
368
899
|
}
|
|
369
|
-
this.sendDebugMessage(
|
|
370
|
-
channel,
|
|
371
|
-
data.thread_id,
|
|
372
|
-
`Received follow-up, routing to session ${shortId(sessionId)}...`
|
|
373
|
-
);
|
|
374
900
|
} else {
|
|
375
901
|
const sessionsService = this.app.service("sessions");
|
|
376
|
-
this.
|
|
902
|
+
this.sendSystemMessage(
|
|
377
903
|
channel,
|
|
378
904
|
data.thread_id,
|
|
379
|
-
`Creating new ${agenticTool} session (${permissionMode} mode)
|
|
905
|
+
`Creating new ${agenticTool} session (${permissionMode} mode)...`,
|
|
906
|
+
{ suppressSlack: true }
|
|
380
907
|
);
|
|
381
908
|
const gatewaySource = {
|
|
382
909
|
channel_id: channel.id,
|
|
@@ -452,19 +979,16 @@ var GatewayService = class {
|
|
|
452
979
|
session_id: session.session_id,
|
|
453
980
|
branch_id: channel.target_branch_id,
|
|
454
981
|
status: "active",
|
|
455
|
-
metadata: data.metadata ?? null
|
|
982
|
+
metadata: channel.channel_type === "slack" ? { ...data.metadata ?? {}, slack_active_thread_id: data.thread_id } : data.metadata ?? null
|
|
456
983
|
});
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
984
|
+
const sessionUrl = await this.fetchExistingSessionUrlForGatewayUser(sessionId, user);
|
|
985
|
+
if (sessionUrl) {
|
|
986
|
+
this.sendSystemMessage(
|
|
987
|
+
channel,
|
|
988
|
+
data.thread_id,
|
|
989
|
+
formatGatewaySessionCreatedMessage(sessionId, sessionUrl)
|
|
990
|
+
);
|
|
464
991
|
}
|
|
465
|
-
const sessionIdShort = shortId(sessionId);
|
|
466
|
-
const message = sessionUrl ? `Session created: ${sessionUrl}` : `Session ${sessionIdShort} created, sending prompt to agent...`;
|
|
467
|
-
this.sendDebugMessage(channel, data.thread_id, message);
|
|
468
992
|
if (channel.channel_type === "github" && data.metadata?.processing_comment_id) {
|
|
469
993
|
try {
|
|
470
994
|
const connector = getConnector(channel.channel_type, channel.config);
|
|
@@ -507,19 +1031,36 @@ ${promptText}`;
|
|
|
507
1031
|
console.log(
|
|
508
1032
|
`[gateway] Message queued for session ${shortId(sessionId)} at position ${task.queue_position}`
|
|
509
1033
|
);
|
|
510
|
-
this.
|
|
1034
|
+
this.sendSystemMessage(
|
|
511
1035
|
channel,
|
|
512
1036
|
data.thread_id,
|
|
513
|
-
`Session is busy, message queued at position ${task.queue_position}
|
|
1037
|
+
`Session is busy, message queued at position ${task.queue_position}`,
|
|
1038
|
+
{ suppressSlack: true }
|
|
514
1039
|
);
|
|
1040
|
+
void this.updateProgress({
|
|
1041
|
+
session_id: sessionId,
|
|
1042
|
+
state: "queued",
|
|
1043
|
+
task_id: task.task_id,
|
|
1044
|
+
queue_position: task.queue_position
|
|
1045
|
+
});
|
|
515
1046
|
} else {
|
|
516
1047
|
console.log(
|
|
517
1048
|
`[gateway] Prompt sent to session ${shortId(sessionId)} via /sessions/:id/prompt`
|
|
518
1049
|
);
|
|
1050
|
+
void this.updateProgress({
|
|
1051
|
+
session_id: sessionId,
|
|
1052
|
+
state: "working",
|
|
1053
|
+
task_id: task.task_id
|
|
1054
|
+
});
|
|
519
1055
|
}
|
|
520
1056
|
} catch (error) {
|
|
521
1057
|
console.error("[gateway] Failed to send prompt to session:", error);
|
|
522
|
-
this.
|
|
1058
|
+
this.sendSystemMessage(channel, data.thread_id, `Error sending prompt: ${error}`);
|
|
1059
|
+
void this.updateProgress({
|
|
1060
|
+
session_id: sessionId,
|
|
1061
|
+
state: "failed",
|
|
1062
|
+
error_message: error instanceof Error ? error.message : String(error)
|
|
1063
|
+
});
|
|
523
1064
|
}
|
|
524
1065
|
return {
|
|
525
1066
|
success: true,
|
|
@@ -562,19 +1103,21 @@ ${promptText}`;
|
|
|
562
1103
|
return { routed: true, channelType: "github" };
|
|
563
1104
|
}
|
|
564
1105
|
try {
|
|
565
|
-
const connector = getConnector(channel.channel_type, channel.config);
|
|
1106
|
+
const connector = this.activeListeners.get(channel.id) ?? getConnector(channel.channel_type, channel.config);
|
|
566
1107
|
const { text, blocks } = normalizeOutbound(
|
|
567
1108
|
connector.formatMessage ? connector.formatMessage(data.message) : data.message
|
|
568
1109
|
);
|
|
569
|
-
|
|
570
|
-
|
|
1110
|
+
const threadId = channel.channel_type === "slack" ? this.getActiveSlackThreadId(mapping) : mapping.thread_id;
|
|
1111
|
+
const sentTs = await connector.sendMessage({
|
|
1112
|
+
threadId,
|
|
571
1113
|
text,
|
|
572
1114
|
blocks,
|
|
573
1115
|
metadata: data.metadata
|
|
574
1116
|
});
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
1117
|
+
if (channel.channel_type === "slack") {
|
|
1118
|
+
await this.addSlackThreadAlias(mapping, sentTs, "message");
|
|
1119
|
+
}
|
|
1120
|
+
console.log(`[gateway] Routed message to ${channel.channel_type} thread ${threadId}`);
|
|
578
1121
|
} catch (error) {
|
|
579
1122
|
console.error(`[gateway] Failed to route message to ${channel.channel_type}:`, error);
|
|
580
1123
|
return { routed: false, channelType: channel.channel_type };
|