@openacp/cli 0.6.10 → 2026.326.3
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/{action-detect-P7ZE4NEM.js → action-detect-QPA775HB.js} +2 -2
- package/dist/adapter-6ANPBSVU.js +16 -0
- package/dist/{discord-OMC52Y54.js → adapter-77ZCVABT.js} +520 -365
- package/dist/adapter-77ZCVABT.js.map +1 -0
- package/dist/{adapter-ZOANORGM.js → adapter-PQGHVG4K.js} +300 -93
- package/dist/adapter-PQGHVG4K.js.map +1 -0
- package/dist/{admin-6SYB6XCZ.js → admin-GBPZFFAU.js} +3 -3
- package/dist/agent-catalog-YHBFERYO.js +11 -0
- package/dist/{agent-dependencies-4OWBMZWZ.js → agent-dependencies-WS7Z2DFW.js} +2 -2
- package/dist/agent-registry-5LZT7CUB.js +9 -0
- package/dist/agent-store-VSHNY5GT.js +9 -0
- package/dist/{agents-QO7DKARJ.js → agents-BWU4MRRD.js} +3 -3
- package/dist/{api-client-CFQT5U7D.js → api-client-AQPNKXI2.js} +2 -2
- package/dist/api-server-3PYLRBCN.js +8 -0
- package/dist/api-server-CHVSUDBX.js +11 -0
- package/dist/{autostart-X33OGMX6.js → autostart-6JS565RY.js} +3 -3
- package/dist/chunk-2CX4IEEC.js +124 -0
- package/dist/chunk-2CX4IEEC.js.map +1 -0
- package/dist/{chunk-O7CPGUAI.js → chunk-4KGLKKQK.js} +4 -4
- package/dist/chunk-4KGLKKQK.js.map +1 -0
- package/dist/{chunk-W3EYKZNQ.js → chunk-4WXALZA3.js} +2 -2
- package/dist/chunk-4WXALZA3.js.map +1 -0
- package/dist/chunk-5OCGO27U.js +125 -0
- package/dist/chunk-5OCGO27U.js.map +1 -0
- package/dist/{chunk-OWP7RZ62.js → chunk-5ZOFBTOR.js} +118 -262
- package/dist/chunk-5ZOFBTOR.js.map +1 -0
- package/dist/chunk-6RXVEXF3.js +23 -0
- package/dist/chunk-6RXVEXF3.js.map +1 -0
- package/dist/{chunk-34M4OS5P.js → chunk-A6Y4GZM3.js} +3 -3
- package/dist/chunk-A6Y4GZM3.js.map +1 -0
- package/dist/chunk-AD3X6DGK.js +166 -0
- package/dist/chunk-AD3X6DGK.js.map +1 -0
- package/dist/{chunk-7QJS2XBD.js → chunk-AFKX424Q.js} +2 -2
- package/dist/chunk-AFKX424Q.js.map +1 -0
- package/dist/chunk-APS6UEFU.js +259 -0
- package/dist/chunk-APS6UEFU.js.map +1 -0
- package/dist/chunk-BLQUXO7S.js +113 -0
- package/dist/chunk-BLQUXO7S.js.map +1 -0
- package/dist/{chunk-WTZDAYZX.js → chunk-BQ6FR32N.js} +3 -3
- package/dist/chunk-BQ6FR32N.js.map +1 -0
- package/dist/chunk-FNRSWA2K.js +1 -0
- package/dist/chunk-FQEBWOZR.js +3557 -0
- package/dist/chunk-FQEBWOZR.js.map +1 -0
- package/dist/{chunk-4CTX774K.js → chunk-GJOY37U7.js} +4 -4
- package/dist/chunk-GJOY37U7.js.map +1 -0
- package/dist/{chunk-I7WC6E5S.js → chunk-HVBNCPAY.js} +2 -2
- package/dist/chunk-HVBNCPAY.js.map +1 -0
- package/dist/{chunk-2HMQOC7N.js → chunk-I3CGU5W7.js} +4 -4
- package/dist/chunk-I3CGU5W7.js.map +1 -0
- package/dist/{chunk-NVPG6JCL.js → chunk-L7YNNBI5.js} +3 -3
- package/dist/chunk-L7YNNBI5.js.map +1 -0
- package/dist/chunk-LGFWH3AE.js +26 -0
- package/dist/chunk-LGFWH3AE.js.map +1 -0
- package/dist/chunk-MLF4W5R6.js +101 -0
- package/dist/chunk-MLF4W5R6.js.map +1 -0
- package/dist/{chunk-KIRH7TUJ.js → chunk-MTSDOSXS.js} +3 -3
- package/dist/chunk-MTSDOSXS.js.map +1 -0
- package/dist/{chunk-J4SJTKIK.js → chunk-NAM4ERUW.js} +3 -3
- package/dist/chunk-NAM4ERUW.js.map +1 -0
- package/dist/{chunk-MKHUZLII.js → chunk-NBFIBGAT.js} +39 -25
- package/dist/chunk-NBFIBGAT.js.map +1 -0
- package/dist/{chunk-BNLGTZ34.js → chunk-O5RG4YZY.js} +3 -3
- package/dist/chunk-O5RG4YZY.js.map +1 -0
- package/dist/{chunk-JHYXKVV2.js → chunk-ODUM3D6X.js} +2 -2
- package/dist/chunk-ODUM3D6X.js.map +1 -0
- package/dist/chunk-OYSAN7UX.js +15 -0
- package/dist/chunk-OYSAN7UX.js.map +1 -0
- package/dist/chunk-P4SNGQNI.js +158 -0
- package/dist/chunk-P4SNGQNI.js.map +1 -0
- package/dist/{chunk-2CJ46J3C.js → chunk-PPSMUECX.js} +3 -3
- package/dist/chunk-PPSMUECX.js.map +1 -0
- package/dist/chunk-Q6ZXJTZB.js +56 -0
- package/dist/chunk-Q6ZXJTZB.js.map +1 -0
- package/dist/{chunk-XANPHG7W.js → chunk-QSDZDHNS.js} +7 -7
- package/dist/chunk-QSDZDHNS.js.map +1 -0
- package/dist/{chunk-33RP6K2O.js → chunk-QVMEF6FB.js} +6 -6
- package/dist/chunk-QVMEF6FB.js.map +1 -0
- package/dist/chunk-QWP76EBW.js +536 -0
- package/dist/chunk-QWP76EBW.js.map +1 -0
- package/dist/{chunk-V5GZQEIY.js → chunk-RBYBSSGO.js} +4 -4
- package/dist/chunk-RBYBSSGO.js.map +1 -0
- package/dist/{chunk-CS3KCJ5D.js → chunk-RKB2ZK6S.js} +555 -383
- package/dist/chunk-RKB2ZK6S.js.map +1 -0
- package/dist/{chunk-UKT3G5IA.js → chunk-SHTGQGAU.js} +7 -7
- package/dist/chunk-SHTGQGAU.js.map +1 -0
- package/dist/chunk-SNPYTMPR.js +51 -0
- package/dist/chunk-SNPYTMPR.js.map +1 -0
- package/dist/chunk-UB2QB6DE.js +124 -0
- package/dist/chunk-UB2QB6DE.js.map +1 -0
- package/dist/chunk-UNJUWWQO.js +1108 -0
- package/dist/chunk-UNJUWWQO.js.map +1 -0
- package/dist/chunk-V2M243KZ.js +445 -0
- package/dist/chunk-V2M243KZ.js.map +1 -0
- package/dist/chunk-V5JT5TPD.js +97 -0
- package/dist/chunk-V5JT5TPD.js.map +1 -0
- package/dist/chunk-W26AUH5B.js +61 -0
- package/dist/chunk-W26AUH5B.js.map +1 -0
- package/dist/chunk-WAAD23KY.js +222 -0
- package/dist/chunk-WAAD23KY.js.map +1 -0
- package/dist/chunk-WIIZNPCR.js +150 -0
- package/dist/chunk-WIIZNPCR.js.map +1 -0
- package/dist/chunk-WQCJTU2C.js +84 -0
- package/dist/chunk-WQCJTU2C.js.map +1 -0
- package/dist/chunk-WVLDNYOJ.js +150 -0
- package/dist/chunk-WVLDNYOJ.js.map +1 -0
- package/dist/chunk-WXVT3AOY.js +22 -0
- package/dist/chunk-WXVT3AOY.js.map +1 -0
- package/dist/{chunk-GAK6PIBW.js → chunk-XMMAGAT4.js} +2 -2
- package/dist/chunk-XMMAGAT4.js.map +1 -0
- package/dist/chunk-Y64XWMJ4.js +212 -0
- package/dist/chunk-Y64XWMJ4.js.map +1 -0
- package/dist/chunk-YEULD3SG.js +62 -0
- package/dist/chunk-YEULD3SG.js.map +1 -0
- package/dist/chunk-ZHGPZBS4.js +49 -0
- package/dist/chunk-ZHGPZBS4.js.map +1 -0
- package/dist/{chunk-JKBFUAJK.js → chunk-ZSLHHQPQ.js} +2 -2
- package/dist/chunk-ZSLHHQPQ.js.map +1 -0
- package/dist/cli.js +496 -150
- package/dist/cli.js.map +1 -1
- package/dist/{config-6S355X75.js → config-I4FMCJGZ.js} +3 -3
- package/dist/config-editor-HNEKXRLQ.js +11 -0
- package/dist/{config-registry-AHYI4MYL.js → config-registry-CUMNXFGK.js} +2 -2
- package/dist/context-XM6E22LM.js +10 -0
- package/dist/core-plugins-VEUNFTMB.js +27 -0
- package/dist/{daemon-4CS6HMB5.js → daemon-PXO5QPCR.js} +4 -4
- package/dist/dev-loader-RDC5E2CW.js +50 -0
- package/dist/dev-loader-RDC5E2CW.js.map +1 -0
- package/dist/discord-NOJQ5PZO.js +8 -0
- package/dist/doctor-H72BZOPA.js +10 -0
- package/dist/{doctor-OLYBO3V3.js → doctor-RF6BHMCC.js} +5 -5
- package/dist/file-service-EUODJAIT.js +9 -0
- package/dist/file-service-EUODJAIT.js.map +1 -0
- package/dist/index.d.ts +1293 -188
- package/dist/index.js +387 -48
- package/dist/index.js.map +1 -1
- package/dist/{install-cloudflared-Z7VCGOVG.js → install-cloudflared-AN24L4DP.js} +5 -5
- package/dist/install-cloudflared-AN24L4DP.js.map +1 -0
- package/dist/install-context-XPWTFT3J.js +78 -0
- package/dist/install-context-XPWTFT3J.js.map +1 -0
- package/dist/{install-jq-HUYSQWKR.js → install-jq-CRVDJGF3.js} +5 -5
- package/dist/install-jq-CRVDJGF3.js.map +1 -0
- package/dist/{integrate-PNEHRY2I.js → integrate-5C6KSU6D.js} +2 -2
- package/dist/integrate-5C6KSU6D.js.map +1 -0
- package/dist/{log-NXABYJTT.js → log-LZ7FTRKG.js} +2 -2
- package/dist/log-LZ7FTRKG.js.map +1 -0
- package/dist/main-T5WVCCFN.js +715 -0
- package/dist/main-T5WVCCFN.js.map +1 -0
- package/dist/{menu-YY5MKHEK.js → menu-YDQ2LWAR.js} +2 -2
- package/dist/menu-YDQ2LWAR.js.map +1 -0
- package/dist/{new-session-FEO4J4VU.js → new-session-AVQCNXRG.js} +5 -5
- package/dist/new-session-AVQCNXRG.js.map +1 -0
- package/dist/notifications-D5BRDNSU.js +9 -0
- package/dist/notifications-D5BRDNSU.js.map +1 -0
- package/dist/plugin-create-LYF5PP5W.js +327 -0
- package/dist/plugin-create-LYF5PP5W.js.map +1 -0
- package/dist/plugin-registry-WB3DR67H.js +8 -0
- package/dist/plugin-registry-WB3DR67H.js.map +1 -0
- package/dist/{post-upgrade-CJG5I7M2.js → post-upgrade-XLHZ6ZB7.js} +8 -8
- package/dist/post-upgrade-XLHZ6ZB7.js.map +1 -0
- package/dist/read-text-file-IRZM3QLM.js +8 -0
- package/dist/read-text-file-IRZM3QLM.js.map +1 -0
- package/dist/security-YNRBW6S7.js +9 -0
- package/dist/security-YNRBW6S7.js.map +1 -0
- package/dist/{session-IUSI7P5S.js → session-KZFA6Z26.js} +4 -4
- package/dist/session-KZFA6Z26.js.map +1 -0
- package/dist/{settings-RQPAM4KC.js → settings-MFYM7CZO.js} +4 -4
- package/dist/settings-MFYM7CZO.js.map +1 -0
- package/dist/settings-manager-MD2U4ZV2.js +8 -0
- package/dist/settings-manager-MD2U4ZV2.js.map +1 -0
- package/dist/{chunk-LCRLAV4G.js → setup-BAI2F24H.js} +154 -492
- package/dist/setup-BAI2F24H.js.map +1 -0
- package/dist/slack-KH7E3VBS.js +8 -0
- package/dist/slack-KH7E3VBS.js.map +1 -0
- package/dist/speech-2GHQNRIO.js +9 -0
- package/dist/speech-2GHQNRIO.js.map +1 -0
- package/dist/telegram-ZDC3JQF2.js +8 -0
- package/dist/telegram-ZDC3JQF2.js.map +1 -0
- package/dist/tunnel-M47I7H4B.js +8 -0
- package/dist/tunnel-M47I7H4B.js.map +1 -0
- package/dist/{tunnel-service-CJLUH6SZ.js → tunnel-service-WADYHREX.js} +17 -17
- package/dist/tunnel-service-WADYHREX.js.map +1 -0
- package/dist/usage-WYNK6ZC5.js +10 -0
- package/dist/usage-WYNK6ZC5.js.map +1 -0
- package/dist/validators-6CLEZUBD.js +8 -0
- package/dist/validators-6CLEZUBD.js.map +1 -0
- package/dist/validators-WSTBNKRW.js +12 -0
- package/dist/validators-WSTBNKRW.js.map +1 -0
- package/package.json +1 -1
- package/dist/adapter-ZOANORGM.js.map +0 -1
- package/dist/agent-catalog-FC3HGDEQ.js +0 -11
- package/dist/agent-registry-WT4NXPYG.js +0 -9
- package/dist/agent-store-VZLFPTZU.js +0 -9
- package/dist/chunk-2CJ46J3C.js.map +0 -1
- package/dist/chunk-2HMQOC7N.js.map +0 -1
- package/dist/chunk-33RP6K2O.js.map +0 -1
- package/dist/chunk-34M4OS5P.js.map +0 -1
- package/dist/chunk-4CTX774K.js.map +0 -1
- package/dist/chunk-7QJS2XBD.js.map +0 -1
- package/dist/chunk-BNLGTZ34.js.map +0 -1
- package/dist/chunk-CS3KCJ5D.js.map +0 -1
- package/dist/chunk-GAK6PIBW.js.map +0 -1
- package/dist/chunk-I7WC6E5S.js.map +0 -1
- package/dist/chunk-J4SJTKIK.js.map +0 -1
- package/dist/chunk-JHYXKVV2.js.map +0 -1
- package/dist/chunk-JKBFUAJK.js.map +0 -1
- package/dist/chunk-KIRH7TUJ.js.map +0 -1
- package/dist/chunk-LBIKITQT.js +0 -22
- package/dist/chunk-LBIKITQT.js.map +0 -1
- package/dist/chunk-LCRLAV4G.js.map +0 -1
- package/dist/chunk-LGP2YGRL.js +0 -4880
- package/dist/chunk-LGP2YGRL.js.map +0 -1
- package/dist/chunk-MKHUZLII.js.map +0 -1
- package/dist/chunk-NAMYZIS5.js +0 -1
- package/dist/chunk-NVPG6JCL.js.map +0 -1
- package/dist/chunk-O7CPGUAI.js.map +0 -1
- package/dist/chunk-OWP7RZ62.js.map +0 -1
- package/dist/chunk-UKT3G5IA.js.map +0 -1
- package/dist/chunk-V5GZQEIY.js.map +0 -1
- package/dist/chunk-VOIJ6OY4.js +0 -63
- package/dist/chunk-VOIJ6OY4.js.map +0 -1
- package/dist/chunk-W3EYKZNQ.js.map +0 -1
- package/dist/chunk-WTZDAYZX.js.map +0 -1
- package/dist/chunk-XANPHG7W.js.map +0 -1
- package/dist/config-editor-QQTZMWGD.js +0 -13
- package/dist/discord-OMC52Y54.js.map +0 -1
- package/dist/doctor-HZZ5BSHB.js +0 -10
- package/dist/install-cloudflared-Z7VCGOVG.js.map +0 -1
- package/dist/install-jq-HUYSQWKR.js.map +0 -1
- package/dist/integrate-PNEHRY2I.js.map +0 -1
- package/dist/main-XOZCLFUK.js +0 -238
- package/dist/main-XOZCLFUK.js.map +0 -1
- package/dist/post-upgrade-CJG5I7M2.js.map +0 -1
- package/dist/setup-XHS4OMPM.js +0 -37
- package/dist/tunnel-service-CJLUH6SZ.js.map +0 -1
- /package/dist/{action-detect-P7ZE4NEM.js.map → action-detect-QPA775HB.js.map} +0 -0
- /package/dist/{admin-6SYB6XCZ.js.map → adapter-6ANPBSVU.js.map} +0 -0
- /package/dist/{agent-catalog-FC3HGDEQ.js.map → admin-GBPZFFAU.js.map} +0 -0
- /package/dist/{agent-dependencies-4OWBMZWZ.js.map → agent-catalog-YHBFERYO.js.map} +0 -0
- /package/dist/{agent-registry-WT4NXPYG.js.map → agent-dependencies-WS7Z2DFW.js.map} +0 -0
- /package/dist/{agent-store-VZLFPTZU.js.map → agent-registry-5LZT7CUB.js.map} +0 -0
- /package/dist/{agents-QO7DKARJ.js.map → agent-store-VSHNY5GT.js.map} +0 -0
- /package/dist/{api-client-CFQT5U7D.js.map → agents-BWU4MRRD.js.map} +0 -0
- /package/dist/{autostart-X33OGMX6.js.map → api-client-AQPNKXI2.js.map} +0 -0
- /package/dist/{chunk-NAMYZIS5.js.map → api-server-3PYLRBCN.js.map} +0 -0
- /package/dist/{config-6S355X75.js.map → api-server-CHVSUDBX.js.map} +0 -0
- /package/dist/{config-editor-QQTZMWGD.js.map → autostart-6JS565RY.js.map} +0 -0
- /package/dist/{config-registry-AHYI4MYL.js.map → chunk-FNRSWA2K.js.map} +0 -0
- /package/dist/{daemon-4CS6HMB5.js.map → config-I4FMCJGZ.js.map} +0 -0
- /package/dist/{doctor-HZZ5BSHB.js.map → config-editor-HNEKXRLQ.js.map} +0 -0
- /package/dist/{doctor-OLYBO3V3.js.map → config-registry-CUMNXFGK.js.map} +0 -0
- /package/dist/{log-NXABYJTT.js.map → context-XM6E22LM.js.map} +0 -0
- /package/dist/{menu-YY5MKHEK.js.map → core-plugins-VEUNFTMB.js.map} +0 -0
- /package/dist/{new-session-FEO4J4VU.js.map → daemon-PXO5QPCR.js.map} +0 -0
- /package/dist/{session-IUSI7P5S.js.map → discord-NOJQ5PZO.js.map} +0 -0
- /package/dist/{settings-RQPAM4KC.js.map → doctor-H72BZOPA.js.map} +0 -0
- /package/dist/{setup-XHS4OMPM.js.map → doctor-RF6BHMCC.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/daemon.ts"],"sourcesContent":["import { spawn } from 'node:child_process'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\nimport { expandHome } from './config.js'\n\nconst DEFAULT_PID_PATH = path.join(os.homedir(), '.openacp', 'openacp.pid')\nconst DEFAULT_LOG_DIR = path.join(os.homedir(), '.openacp', 'logs')\nconst RUNNING_MARKER = path.join(os.homedir(), '.openacp', 'running')\n\nexport function writePidFile(pidPath: string, pid: number): void {\n const dir = path.dirname(pidPath)\n fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(pidPath, String(pid))\n}\n\nexport function readPidFile(pidPath: string): number | null {\n try {\n const content = fs.readFileSync(pidPath, 'utf-8').trim()\n const pid = parseInt(content, 10)\n return isNaN(pid) ? null : pid\n } catch {\n return null\n }\n}\n\nexport function removePidFile(pidPath: string): void {\n try {\n fs.unlinkSync(pidPath)\n } catch {\n // ignore if already gone\n }\n}\n\nexport function isProcessRunning(pidPath: string): boolean {\n const pid = readPidFile(pidPath)\n if (pid === null) return false\n try {\n process.kill(pid, 0)\n return true\n } catch {\n // Process not running, clean up stale PID file\n removePidFile(pidPath)\n return false\n }\n}\n\nexport function getStatus(pidPath: string = DEFAULT_PID_PATH): { running: boolean; pid?: number } {\n const pid = readPidFile(pidPath)\n if (pid === null) return { running: false }\n try {\n process.kill(pid, 0)\n return { running: true, pid }\n } catch {\n removePidFile(pidPath)\n return { running: false }\n }\n}\n\nexport function startDaemon(pidPath: string = DEFAULT_PID_PATH, logDir?: string): { pid: number } | { error: string } {\n // Mark as running so auto-start works on next boot\n markRunning()\n\n // Check if already running\n if (isProcessRunning(pidPath)) {\n const pid = readPidFile(pidPath)!\n return { error: `Already running (PID ${pid})` }\n }\n\n const resolvedLogDir = logDir ? expandHome(logDir) : DEFAULT_LOG_DIR\n fs.mkdirSync(resolvedLogDir, { recursive: true })\n const logFile = path.join(resolvedLogDir, 'openacp.log')\n\n // Find the CLI entry point\n const cliPath = path.resolve(process.argv[1])\n const nodePath = process.execPath\n\n const out = fs.openSync(logFile, 'a')\n const err = fs.openSync(logFile, 'a')\n\n const child = spawn(nodePath, [cliPath, '--daemon-child'], {\n detached: true,\n stdio: ['ignore', out, err],\n })\n\n // Close file descriptors in parent — child has its own copies\n fs.closeSync(out)\n fs.closeSync(err)\n\n if (!child.pid) {\n return { error: 'Failed to spawn daemon process' }\n }\n\n // PID file is written by the child process itself (in main.ts startServer)\n // to avoid race conditions and ensure consistency with LaunchAgent/systemd starts.\n // We still write it here as a fallback in case the child hasn't written it yet\n // when the parent needs to report the PID.\n writePidFile(pidPath, child.pid)\n child.unref()\n\n return { pid: child.pid }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nfunction isProcessAlive(pid: number): 'alive' | 'dead' | 'eperm' {\n try {\n process.kill(pid, 0)\n return 'alive'\n } catch (e) {\n const err = e as NodeJS.ErrnoException\n if (err.code === 'EPERM') return 'eperm'\n return 'dead'\n }\n}\n\nexport async function stopDaemon(pidPath: string = DEFAULT_PID_PATH): Promise<{ stopped: boolean; pid?: number; error?: string }> {\n const pid = readPidFile(pidPath)\n if (pid === null) return { stopped: false, error: 'Not running (no PID file)' }\n\n const status = isProcessAlive(pid)\n if (status === 'dead') {\n removePidFile(pidPath)\n return { stopped: false, error: 'Not running (stale PID file removed)' }\n }\n if (status === 'eperm') {\n removePidFile(pidPath)\n return { stopped: false, error: 'PID belongs to another process (stale PID file removed)' }\n }\n\n try {\n process.kill(pid, 'SIGTERM')\n } catch (e) {\n return { stopped: false, error: `Failed to stop: ${(e as Error).message}` }\n }\n\n clearRunning()\n\n const POLL_INTERVAL = 100\n const TIMEOUT = 5000\n const start = Date.now()\n\n while (Date.now() - start < TIMEOUT) {\n await sleep(POLL_INTERVAL)\n const s = isProcessAlive(pid)\n if (s === 'dead' || s === 'eperm') {\n removePidFile(pidPath)\n return { stopped: true, pid }\n }\n }\n\n try {\n process.kill(pid, 'SIGKILL')\n } catch (e) {\n const err = e as NodeJS.ErrnoException\n if (err.code === 'EPERM') {\n return { stopped: false, pid, error: 'PID may have been reused by another process. Run `openacp status` to verify, or manually delete the PID file.' }\n }\n }\n\n const killStart = Date.now()\n while (Date.now() - killStart < 1000) {\n await sleep(POLL_INTERVAL)\n const s = isProcessAlive(pid)\n if (s === 'dead' || s === 'eperm') {\n removePidFile(pidPath)\n return { stopped: true, pid }\n }\n }\n\n // SIGKILL sent but process still alive after 1s — extremely rare (uninterruptible I/O).\n return { stopped: false, pid, error: 'Process did not exit after SIGKILL (possible uninterruptible I/O). PID file retained.' }\n}\n\nexport function getPidPath(): string {\n return DEFAULT_PID_PATH\n}\n\n/** Mark that the daemon should auto-start on boot */\nexport function markRunning(): void {\n fs.mkdirSync(path.dirname(RUNNING_MARKER), { recursive: true })\n fs.writeFileSync(RUNNING_MARKER, '')\n}\n\n/** Remove running marker — daemon won't auto-start on boot */\nexport function clearRunning(): void {\n try { fs.unlinkSync(RUNNING_MARKER) } catch { /* ignore */ }\n}\n\n/** Check if the daemon was running before (should auto-start on boot) */\nexport function shouldAutoStart(): boolean {\n return fs.existsSync(RUNNING_MARKER)\n}\n"],"mappings":"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","names":["resolve"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/discord/commands/new-session.ts"],"sourcesContent":["import {\n ActionRowBuilder,\n ButtonBuilder,\n ButtonStyle,\n} from 'discord.js'\nimport type { ChatInputCommandInteraction, ButtonInteraction } from 'discord.js'\nimport { log } from '../../../core/log.js'\nimport { buildSessionControlKeyboard } from './admin.js'\nimport { createSessionThread, deleteSessionThread } from '../forums.js'\nimport type { DiscordAdapter } from '../adapter.js'\n\nexport async function handleNew(\n interaction: ChatInputCommandInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.deferReply({ ephemeral: true })\n\n const agentName = interaction.options.getString('agent') ?? undefined\n const workspace = interaction.options.getString('workspace') ?? undefined\n\n if (agentName) {\n await executeNewSession(interaction, adapter, agentName, workspace)\n return\n }\n\n // No agent specified — show agent picker\n const installedEntries = adapter.core.agentCatalog.getInstalledEntries()\n const agentKeys = Object.keys(installedEntries)\n const config = adapter.core.configManager.get()\n\n if (agentKeys.length === 0) {\n await interaction.editReply('❌ No agents installed. Use `/install` to install an agent first.')\n return\n }\n\n if (agentKeys.length === 1) {\n await executeNewSession(interaction, adapter, config.defaultAgent, workspace)\n return\n }\n\n // Multiple agents — show picker buttons\n const row = new ActionRowBuilder<ButtonBuilder>()\n for (const key of agentKeys) {\n const agent = installedEntries[key]!\n const label = key === config.defaultAgent ? `${agent.name} (default)` : agent.name\n row.addComponents(\n new ButtonBuilder()\n .setCustomId(`m:new:agent:${key}`)\n .setLabel(label)\n .setStyle(ButtonStyle.Primary),\n )\n }\n\n await interaction.editReply({\n content: '🤖 **Choose an agent:**',\n components: [row],\n })\n}\n\nexport async function handleNewChat(\n interaction: ChatInputCommandInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.deferReply({ ephemeral: true })\n\n // Get current thread to inherit config from\n const channelId = interaction.channelId\n const currentSession = adapter.core.sessionManager.getSessionByThread('discord', channelId)\n\n let agentName: string | undefined\n let workspace: string | undefined\n\n if (currentSession) {\n agentName = currentSession.agentName\n workspace = currentSession.workingDirectory\n } else {\n const record = adapter.core.sessionManager.getRecordByThread('discord', channelId)\n if (!record || record.status === 'cancelled' || record.status === 'error') {\n await interaction.editReply('No active session in this channel. Use `/new` to start one.')\n return\n }\n agentName = record.agentName\n workspace = record.workingDir\n }\n\n await executeNewSession(interaction, adapter, agentName, workspace)\n}\n\nexport async function executeNewSession(\n interaction: ChatInputCommandInteraction | ButtonInteraction,\n adapter: DiscordAdapter,\n agentName?: string,\n workspace?: string,\n): Promise<void> {\n const config = adapter.core.configManager.get()\n const resolvedAgent = agentName || config.defaultAgent\n\n log.info({ agentName: resolvedAgent, workspace }, '[discord-new-session] Creating session')\n\n const forumChannel = adapter.getForumChannel()\n if (!forumChannel) {\n const msg = '❌ Forum channel not configured. Please run setup first.'\n if (interaction.deferred || interaction.replied) {\n await interaction.editReply(msg)\n } else {\n await (interaction as ChatInputCommandInteraction).reply({ content: msg, ephemeral: true })\n }\n return\n }\n\n let thread: import('discord.js').ThreadChannel | undefined\n\n try {\n // Create forum thread BEFORE creating session to avoid race condition\n thread = await createSessionThread(forumChannel, `🔄 ${resolvedAgent} — New Session`)\n\n // Create session via core\n const session = await adapter.core.handleNewSession('discord', resolvedAgent, workspace)\n session.threadId = thread.id\n\n // Patch platform record with Discord thread ID\n await adapter.core.sessionManager.patchRecord(session.id, {\n platform: { threadId: thread.id },\n })\n\n // Send welcome message in the new thread\n const controlRow = buildSessionControlKeyboard(session.id, false, false)\n await thread.send({\n content:\n `✅ **Session started**\\n` +\n `**Agent:** ${session.agentName}\\n` +\n `**Workspace:** \\`${session.workingDirectory}\\`\\n\\n` +\n `This is your coding session — chat here to work with the agent.`,\n components: [controlRow],\n })\n\n // Reply to the interaction with a link to the thread\n const replyMsg = `✅ Session created → [Open thread](https://discord.com/channels/${adapter.getGuildId()}/${thread.id})`\n if (interaction.deferred || interaction.replied) {\n await interaction.editReply(replyMsg)\n } else {\n await (interaction as ChatInputCommandInteraction).reply({ content: replyMsg, ephemeral: true })\n }\n\n // Warm up model cache in background\n session.warmup().catch((err: unknown) => log.error({ err }, '[discord-new-session] Warm-up error'))\n } catch (err) {\n log.error({ err }, '[discord-new-session] Session creation failed')\n\n // Clean up orphaned thread on failure (archive+lock instead of permanent delete)\n if (thread) {\n try { await deleteSessionThread(adapter.getGuild(), thread.id) } catch { /* ignore */ }\n }\n\n const errMsg = `❌ ${err instanceof Error ? err.message : String(err)}`\n try {\n if (interaction.deferred || interaction.replied) {\n await interaction.editReply(errMsg)\n } else {\n await (interaction as ChatInputCommandInteraction).reply({ content: errMsg, ephemeral: true })\n }\n } catch { /* ignore */ }\n }\n}\n\nexport async function handleNewSessionButton(\n interaction: ButtonInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n const { customId } = interaction\n\n if (customId.startsWith('m:new:agent:')) {\n const agentKey = customId.replace('m:new:agent:', '')\n try { await interaction.deferUpdate() } catch { /* ignore */ }\n await executeNewSession(interaction, adapter, agentKey, undefined)\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
runConfigEditor
|
|
3
|
-
} from "./chunk-MKHUZLII.js";
|
|
4
|
-
import "./chunk-2CJ46J3C.js";
|
|
5
|
-
import "./chunk-LCRLAV4G.js";
|
|
6
|
-
import "./chunk-JKBFUAJK.js";
|
|
7
|
-
import "./chunk-33RP6K2O.js";
|
|
8
|
-
import "./chunk-GAK6PIBW.js";
|
|
9
|
-
import "./chunk-VUNV25KB.js";
|
|
10
|
-
export {
|
|
11
|
-
runConfigEditor
|
|
12
|
-
};
|
|
13
|
-
//# sourceMappingURL=config-editor-QQTZMWGD.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/discord/adapter.ts","../../src/adapters/discord/send-queue.ts","../../src/adapters/discord/formatting.ts","../../src/adapters/discord/tool-call-tracker.ts","../../src/adapters/discord/streaming.ts","../../src/adapters/discord/draft-manager.ts","../../src/adapters/discord/activity.ts","../../src/adapters/discord/skill-command-manager.ts","../../src/adapters/discord/permissions.ts","../../src/adapters/discord/commands/index.ts","../../src/adapters/discord/commands/menu.ts","../../src/adapters/discord/commands/integrate.ts","../../src/adapters/discord/commands/router.ts","../../src/adapters/discord/assistant.ts","../../src/adapters/discord/media.ts"],"sourcesContent":["import {\n Client,\n GatewayIntentBits,\n MessageFlags,\n type Guild,\n type ForumChannel,\n type TextChannel,\n type ThreadChannel,\n} from \"discord.js\";\nimport { ChannelAdapter } from \"../../core/channel.js\";\nimport type {\n OutgoingMessage,\n PermissionRequest,\n NotificationMessage,\n AgentCommand,\n PlanEntry,\n} from \"../../core/types.js\";\nimport type { OpenACPCore } from \"../../core/core.js\";\nimport type { Session } from \"../../core/session.js\";\nimport { log } from \"../../core/log.js\";\nimport type { DisplayVerbosity } from \"../shared/format-types.js\";\nimport { evaluateNoise } from \"../shared/message-formatter.js\";\nimport {\n dispatchMessage,\n type MessageHandlers,\n} from \"../shared/message-dispatcher.js\";\nimport type { DiscordChannelConfig } from \"./types.js\";\nimport { DiscordSendQueue } from \"./send-queue.js\";\nimport { ToolCallTracker } from \"./tool-call-tracker.js\";\nimport { DraftManager } from \"./draft-manager.js\";\nimport { ActivityTracker } from \"./activity.js\";\nimport { SkillCommandManager } from \"./skill-command-manager.js\";\nimport { PermissionHandler } from \"./permissions.js\";\nimport {\n ensureForums,\n createSessionThread as forumsCreateThread,\n renameSessionThread as forumsRenameThread,\n deleteSessionThread as forumsDeleteThread,\n ensureUnarchived,\n buildDeepLink,\n} from \"./forums.js\";\nimport {\n registerSlashCommands,\n handleSlashCommand,\n setupButtonCallbacks,\n} from \"./commands/index.js\";\nimport { spawnAssistant, buildWelcomeMessage } from \"./assistant.js\";\nimport type { Attachment } from \"../../core/types.js\";\nimport type { FileService } from \"../../core/file-service.js\";\nimport {\n buildFallbackText,\n downloadDiscordAttachment,\n isAttachmentTooLarge,\n} from \"./media.js\";\n\ninterface DiscordMessageCtx {\n sessionId: string;\n thread: ThreadChannel;\n isAssistant: boolean;\n}\n\nexport class DiscordAdapter extends ChannelAdapter<OpenACPCore> {\n private client: Client;\n private discordConfig: DiscordChannelConfig;\n private sendQueue: DiscordSendQueue;\n private toolTracker: ToolCallTracker;\n private draftManager: DraftManager;\n private skillManager!: SkillCommandManager;\n private permissionHandler!: PermissionHandler;\n private sessionTrackers: Map<string, ActivityTracker> = new Map();\n\n private get verbosity(): DisplayVerbosity {\n return (\n ((this.discordConfig as Record<string, unknown>)\n .displayVerbosity as DisplayVerbosity) ?? \"medium\"\n );\n }\n\n private guild!: Guild;\n private forumChannel!: ForumChannel | TextChannel;\n private notificationChannel!: TextChannel;\n private assistantSession: Session | null = null;\n private assistantInitializing = false;\n private fileService: FileService;\n\n constructor(core: OpenACPCore, config: DiscordChannelConfig) {\n super(core, config);\n this.discordConfig = config;\n\n this.client = new Client({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n ],\n });\n\n this.sendQueue = new DiscordSendQueue();\n this.toolTracker = new ToolCallTracker(this.sendQueue);\n this.draftManager = new DraftManager(this.sendQueue);\n this.fileService = core.fileService;\n\n // Wire discord.js rate limit events to send queue\n this.client.rest.on(\"rateLimited\", (info) => {\n log.warn(\n { route: info.route, timeToReset: info.timeToReset },\n \"[DiscordAdapter] Rate limited\",\n );\n this.sendQueue.onRateLimited();\n });\n }\n\n // ─── start ────────────────────────────────────────────────────────────────\n\n start(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.client.once(\"ready\", async () => {\n try {\n log.info(\n { guildId: this.discordConfig.guildId },\n \"[DiscordAdapter] Client ready, initializing...\",\n );\n\n // Fetch guild\n const guild =\n this.client.guilds.cache.get(this.discordConfig.guildId) ??\n (await this.client.guilds\n .fetch(this.discordConfig.guildId)\n .catch(() => null));\n if (!guild) {\n throw new Error(`Guild not found: ${this.discordConfig.guildId}`);\n }\n this.guild = guild;\n\n // Ensure forum + notification channels exist\n const saveConfig = (updates: Record<string, unknown>) =>\n this.core.configManager.save(\n updates as Parameters<typeof this.core.configManager.save>[0],\n );\n const { forumChannel, notificationChannel } = await ensureForums(\n guild,\n {\n forumChannelId: this.discordConfig.forumChannelId,\n notificationChannelId: this.discordConfig.notificationChannelId,\n },\n saveConfig,\n );\n this.forumChannel = forumChannel;\n this.notificationChannel = notificationChannel;\n\n // Init managers that need guild/guildId\n this.skillManager = new SkillCommandManager(\n this.sendQueue,\n this.core.sessionManager,\n );\n this.permissionHandler = new PermissionHandler(\n guild.id,\n (sessionId) => this.core.sessionManager.getSession(sessionId),\n (notification) => this.sendNotification(notification),\n );\n\n // Register slash commands\n await registerSlashCommands(guild);\n\n // Wire interaction + message handlers\n this.setupInteractionHandler();\n this.setupMessageHandler();\n\n // Welcome message\n const welcomeMsg = buildWelcomeMessage(this.core);\n try {\n await this.notificationChannel.send(welcomeMsg);\n } catch (err) {\n log.warn(\n { err },\n \"[DiscordAdapter] Failed to send welcome message\",\n );\n }\n\n // Spawn assistant session\n await this.setupAssistant();\n\n log.info(\"[DiscordAdapter] Initialization complete\");\n resolve();\n } catch (err) {\n log.error({ err }, \"[DiscordAdapter] Initialization failed\");\n reject(err);\n }\n });\n\n this.client.login(this.discordConfig.botToken).catch(reject);\n });\n }\n\n // ─── stop ─────────────────────────────────────────────────────────────────\n\n async stop(): Promise<void> {\n if (this.assistantSession) {\n try {\n await this.assistantSession.destroy();\n } catch (err) {\n log.warn(\n { err },\n \"[DiscordAdapter] Failed to destroy assistant session\",\n );\n }\n this.assistantSession = null;\n }\n this.client.destroy();\n log.info(\"[DiscordAdapter] Stopped\");\n }\n\n // ─── Interaction handler ──────────────────────────────────────────────────\n\n private setupInteractionHandler(): void {\n this.client.on(\"interactionCreate\", async (interaction) => {\n try {\n if (interaction.isChatInputCommand()) {\n await handleSlashCommand(interaction, this);\n return;\n }\n\n if (interaction.isButton()) {\n // Permission buttons take priority\n const handled =\n await this.permissionHandler.handleButtonInteraction(interaction);\n if (!handled) {\n await setupButtonCallbacks(interaction, this);\n }\n }\n } catch (err) {\n log.error({ err }, \"[DiscordAdapter] interactionCreate handler error\");\n }\n });\n }\n\n // ─── Message handler ──────────────────────────────────────────────────────\n\n private setupMessageHandler(): void {\n this.client.on(\"messageCreate\", async (message) => {\n try {\n // Ignore bots and self\n if (message.author.bot) return;\n\n // Ignore DMs\n if (!message.guild) return;\n\n // Ignore messages from the wrong guild\n if (message.guild.id !== this.guild.id) return;\n\n // Only process messages in threads\n if (!message.channel.isThread()) return;\n\n const threadId = message.channel.id;\n const userId = message.author.id;\n let text = message.content;\n\n log.debug(\n {\n threadId,\n userId,\n text: text.slice(0, 50),\n attachmentCount: message.attachments.size,\n },\n \"[DiscordAdapter] messageCreate received\",\n );\n\n // Ignore messages with no text and no attachments\n if (!text && message.attachments.size === 0) return;\n\n // Resolve sessionId for file storage (fallback to \"unknown\" for new sessions)\n const sessionId =\n this.core.sessionManager.getSessionByThread(\"discord\", threadId)\n ?.id ?? \"unknown\";\n\n // Process attachments\n if (message.attachments.size > 0) {\n log.info(\n {\n sessionId,\n attachments: message.attachments.map((a) => ({\n name: a.name,\n size: a.size,\n contentType: a.contentType,\n url: a.url?.slice(0, 80),\n })),\n },\n \"[discord-media] Processing incoming attachments\",\n );\n }\n const attachments = await this.processIncomingAttachments(\n message,\n sessionId,\n );\n\n // Generate fallback text if message has attachments but no text\n if (!text && attachments.length > 0) {\n text = buildFallbackText(attachments);\n }\n\n // If all attachment downloads failed and no text, notify user\n if (!text && attachments.length === 0 && message.attachments.size > 0) {\n try {\n await message.reply(\"Failed to process attachment(s)\");\n } catch {\n /* best effort */\n }\n return;\n }\n\n // Route assistant thread messages to assistant\n if (\n this.discordConfig.assistantThreadId &&\n threadId === this.discordConfig.assistantThreadId\n ) {\n if (this.assistantSession && text) {\n await this.assistantSession.enqueuePrompt(\n text,\n attachments.length > 0 ? attachments : undefined,\n );\n }\n return;\n }\n\n // Route to core for session dispatch\n await this.core.handleMessage({\n channelId: \"discord\",\n threadId,\n userId,\n text,\n ...(attachments.length > 0 ? { attachments } : {}),\n });\n } catch (err) {\n log.error({ err }, \"[DiscordAdapter] messageCreate handler error\");\n }\n });\n }\n\n // ─── Assistant ────────────────────────────────────────────────────────────\n\n private async setupAssistant(): Promise<void> {\n let threadId = this.discordConfig.assistantThreadId;\n\n // Verify existing thread is still accessible\n if (threadId) {\n try {\n const existing =\n this.guild.channels.cache.get(threadId) ??\n (await this.guild.channels.fetch(threadId));\n if (existing && existing.isThread()) {\n await ensureUnarchived(\n existing as import(\"discord.js\").ThreadChannel,\n );\n log.info(\n { threadId },\n \"[DiscordAdapter] Reusing existing assistant thread\",\n );\n } else {\n log.warn(\n { threadId },\n \"[DiscordAdapter] Assistant thread not found, recreating...\",\n );\n threadId = null;\n }\n } catch {\n log.warn(\n { threadId },\n \"[DiscordAdapter] Assistant thread inaccessible, recreating...\",\n );\n threadId = null;\n }\n }\n\n if (!threadId) {\n // Create a new thread for the assistant\n const thread = await forumsCreateThread(this.forumChannel, \"Assistant\");\n threadId = thread.id;\n await this.core.configManager.save({\n channels: { discord: { assistantThreadId: thread.id } },\n } as Parameters<typeof this.core.configManager.save>[0]);\n log.info({ threadId }, \"[DiscordAdapter] Created assistant thread\");\n }\n\n this.assistantInitializing = true;\n try {\n const { session, ready } = await spawnAssistant(this.core, threadId);\n this.assistantSession = session;\n ready.finally(() => {\n this.assistantInitializing = false;\n });\n } catch (err) {\n this.assistantInitializing = false;\n log.error({ err }, \"[DiscordAdapter] Failed to spawn assistant\");\n }\n }\n\n async respawnAssistant(): Promise<void> {\n if (this.assistantSession) {\n try {\n await this.assistantSession.destroy();\n } catch {\n /* ignore */\n }\n this.assistantSession = null;\n }\n await this.setupAssistant();\n }\n\n // ─── Incoming media ──────────────────────────────────────────────────\n\n private async processIncomingAttachments(\n message: import(\"discord.js\").Message,\n sessionId: string,\n ): Promise<Attachment[]> {\n if (message.attachments.size === 0) return [];\n\n const isVoiceMessage = message.flags.has(MessageFlags.IsVoiceMessage);\n\n const results = await Promise.allSettled(\n message.attachments.map(async (discordAtt) => {\n const buffer = await downloadDiscordAttachment(\n discordAtt.url,\n discordAtt.name ?? \"attachment\",\n );\n if (!buffer) return null;\n\n let data = buffer;\n let fileName = discordAtt.name ?? \"attachment\";\n let mimeType = discordAtt.contentType ?? \"application/octet-stream\";\n\n // Convert voice messages from OGG Opus to WAV\n if (isVoiceMessage && mimeType.includes(\"ogg\")) {\n try {\n data = await this.fileService.convertOggToWav(buffer);\n fileName = \"voice.wav\";\n mimeType = \"audio/wav\";\n } catch (err) {\n log.warn(\n { err },\n \"[discord-media] OGG→WAV conversion failed, saving original\",\n );\n }\n }\n\n return this.fileService.saveFile(sessionId, fileName, data, mimeType);\n }),\n );\n\n const rejected = results.filter((r) => r.status === \"rejected\");\n if (rejected.length > 0) {\n log.warn(\n { rejected: rejected.map((r) => (r as PromiseRejectedResult).reason) },\n \"[discord-media] Some attachments failed\",\n );\n }\n\n const saved = results\n .filter(\n (r): r is PromiseFulfilledResult<Attachment | null> =>\n r.status === \"fulfilled\",\n )\n .map((r) => r.value)\n .filter((att): att is Attachment => att !== null);\n\n log.info(\n { count: saved.length, files: saved.map((a) => a.fileName) },\n \"[discord-media] Attachments processed\",\n );\n return saved;\n }\n\n // ─── Helper: resolve thread ───────────────────────────────────────────────\n\n private async getThread(sessionId: string): Promise<ThreadChannel | null> {\n const session = this.core.sessionManager.getSession(sessionId);\n const threadId = session?.threadId;\n if (!threadId) {\n log.warn({ sessionId }, \"[DiscordAdapter] No threadId for session\");\n return null;\n }\n try {\n const channel =\n this.guild.channels.cache.get(threadId) ??\n (await this.guild.channels.fetch(threadId));\n if (channel && channel.isThread()) return channel as ThreadChannel;\n log.warn(\n { sessionId, threadId },\n \"[DiscordAdapter] Channel is not a thread\",\n );\n return null;\n } catch (err) {\n log.warn(\n { err, sessionId, threadId },\n \"[DiscordAdapter] Failed to fetch thread\",\n );\n return null;\n }\n }\n\n // ─── Helper: get or create activity tracker ──────────────────────────────\n\n private getOrCreateTracker(\n sessionId: string,\n thread: ThreadChannel,\n ): ActivityTracker {\n if (!this.sessionTrackers.has(sessionId)) {\n this.sessionTrackers.set(\n sessionId,\n new ActivityTracker(thread, this.sendQueue),\n );\n }\n return this.sessionTrackers.get(sessionId)!;\n }\n\n // --- MessageHandlers for dispatchMessage ---\n\n private messageHandlers: MessageHandlers<DiscordMessageCtx> = {\n onThought: async (ctx, _content) => {\n const tracker = this.getOrCreateTracker(ctx.sessionId, ctx.thread);\n await tracker.onThought();\n },\n\n onText: async (ctx, content) => {\n const tracker = this.getOrCreateTracker(ctx.sessionId, ctx.thread);\n await tracker.onTextStart();\n const draft = this.draftManager.getOrCreate(ctx.sessionId, ctx.thread);\n draft.append(content.text);\n this.draftManager.appendText(ctx.sessionId, content.text);\n },\n\n onToolCall: async (ctx, content) => {\n const meta = content.metadata ?? {};\n const toolName = String(meta.name ?? content.text ?? \"Tool\");\n const toolKind = String(meta.kind ?? \"other\");\n const noiseAction = evaluateNoise(toolName, toolKind, meta.rawInput);\n if (noiseAction === \"hide\" && this.verbosity !== \"high\") return;\n if (noiseAction === \"collapse\" && this.verbosity === \"low\") return;\n\n const tracker = this.getOrCreateTracker(ctx.sessionId, ctx.thread);\n await tracker.onToolCall();\n await this.draftManager.finalize(\n ctx.sessionId,\n ctx.thread,\n ctx.isAssistant,\n );\n await this.toolTracker.trackNewCall(\n ctx.sessionId,\n ctx.thread,\n {\n id: String(meta.id ?? \"\"),\n name: toolName,\n kind: meta.kind as string | undefined,\n status: String(meta.status ?? \"running\"),\n content: meta.content,\n rawInput: meta.rawInput,\n viewerLinks: meta.viewerLinks as\n | { file?: string; diff?: string }\n | undefined,\n viewerFilePath: meta.viewerFilePath as string | undefined,\n displaySummary: meta.displaySummary as string | undefined,\n displayTitle: meta.displayTitle as string | undefined,\n displayKind: meta.displayKind as string | undefined,\n },\n this.verbosity,\n );\n },\n\n onToolUpdate: async (ctx, content) => {\n const meta = content.metadata ?? {};\n await this.toolTracker.updateCall(\n ctx.sessionId,\n {\n id: String(meta.id ?? \"\"),\n name: content.text || String(meta.name ?? \"\"),\n kind: meta.kind as string | undefined,\n status: String(meta.status ?? \"completed\"),\n content: meta.content,\n rawInput: meta.rawInput,\n viewerLinks: meta.viewerLinks as\n | { file?: string; diff?: string }\n | undefined,\n viewerFilePath: meta.viewerFilePath as string | undefined,\n displaySummary: meta.displaySummary as string | undefined,\n displayTitle: meta.displayTitle as string | undefined,\n displayKind: meta.displayKind as string | undefined,\n },\n this.verbosity,\n );\n },\n\n onPlan: async (ctx, content) => {\n const entries = (content.metadata?.entries ?? []) as PlanEntry[];\n const tracker = this.getOrCreateTracker(ctx.sessionId, ctx.thread);\n await tracker.onPlan(entries, this.verbosity);\n },\n\n onUsage: async (ctx, content) => {\n await this.draftManager.finalize(\n ctx.sessionId,\n ctx.thread,\n ctx.isAssistant,\n );\n const meta = content.metadata ?? {};\n const tracker = this.getOrCreateTracker(ctx.sessionId, ctx.thread);\n await tracker.sendUsage(\n {\n tokensUsed: meta.tokensUsed as number | undefined,\n contextSize: meta.contextSize as number | undefined,\n cost: meta.cost as number | undefined,\n },\n this.verbosity,\n );\n // Send usage notification to notification channel\n try {\n const deepLink = buildDeepLink(this.guild.id, ctx.thread.id);\n await this.sendNotification({\n sessionId: ctx.sessionId,\n type: \"completed\",\n summary: content.text || \"Session completed\",\n deepLink,\n });\n } catch {\n /* best effort */\n }\n },\n\n onSessionEnd: async (ctx, _content) => {\n await this.draftManager.finalize(\n ctx.sessionId,\n ctx.thread,\n ctx.isAssistant,\n );\n const tracker = this.getOrCreateTracker(ctx.sessionId, ctx.thread);\n await tracker.cleanup();\n this.toolTracker.cleanup(ctx.sessionId);\n this.sessionTrackers.delete(ctx.sessionId);\n await this.skillManager.cleanup(ctx.sessionId);\n try {\n await this.sendQueue.enqueue(\n () => ctx.thread.send({ content: \"✅ Done\" }),\n { type: \"other\" },\n );\n } catch {\n /* best effort */\n }\n },\n\n onError: async (ctx, content) => {\n await this.draftManager.finalize(\n ctx.sessionId,\n ctx.thread,\n ctx.isAssistant,\n );\n const tracker = this.getOrCreateTracker(ctx.sessionId, ctx.thread);\n await tracker.cleanup();\n this.toolTracker.cleanup(ctx.sessionId);\n this.sessionTrackers.delete(ctx.sessionId);\n try {\n await this.sendQueue.enqueue(\n () => ctx.thread.send({ content: `❌ Error: ${content.text}` }),\n { type: \"other\" },\n );\n } catch {\n /* best effort */\n }\n },\n\n onAttachment: async (ctx, content) => {\n if (!content.attachment) return;\n const { attachment } = content;\n await this.draftManager.finalize(\n ctx.sessionId,\n ctx.thread,\n ctx.isAssistant,\n );\n\n // Discord free tier limit: 25MB\n if (isAttachmentTooLarge(attachment.size)) {\n log.warn(\n {\n sessionId: ctx.sessionId,\n fileName: attachment.fileName,\n size: attachment.size,\n },\n \"[discord-media] File too large (>25MB)\",\n );\n try {\n await this.sendQueue.enqueue(\n () =>\n ctx.thread.send({\n content: `⚠️ File too large to send (${Math.round(attachment.size / 1024 / 1024)}MB): ${attachment.fileName}`,\n }),\n { type: \"other\" },\n );\n } catch {\n /* best effort */\n }\n return;\n }\n\n try {\n await this.sendQueue.enqueue(\n () =>\n ctx.thread.send({\n files: [\n { attachment: attachment.filePath, name: attachment.fileName },\n ],\n }),\n { type: \"other\" },\n );\n\n // Strip [TTS]...[/TTS] block from the text message after audio is sent.\n // This fires after sendQueue completes, so the draft message already exists.\n // stripPattern is best-effort and handles missing/finalized drafts gracefully.\n if (attachment.type === \"audio\") {\n const draft = this.draftManager.getDraft(ctx.sessionId);\n if (draft) {\n draft.stripPattern(/\\[TTS\\][\\s\\S]*?\\[\\/TTS\\]/g).catch(() => {});\n }\n }\n } catch (err) {\n log.error(\n { err, sessionId: ctx.sessionId, fileName: attachment.fileName },\n \"[discord-media] Failed to send attachment\",\n );\n }\n },\n\n onSystemMessage: async (ctx, content) => {\n try {\n await this.sendQueue.enqueue(\n () => ctx.thread.send({ content: content.text }),\n { type: \"other\" },\n );\n } catch {\n /* best effort */\n }\n },\n };\n\n // ─── sendMessage ──────────────────────────────────────────────────────────\n\n async sendMessage(\n sessionId: string,\n content: OutgoingMessage,\n ): Promise<void> {\n // Suppress output while assistant is initializing its system prompt\n if (\n this.assistantInitializing &&\n this.assistantSession &&\n sessionId === this.assistantSession.id\n ) {\n return;\n }\n\n const thread = await this.getThread(sessionId);\n if (!thread) return;\n\n await ensureUnarchived(thread);\n\n const isAssistant =\n this.assistantSession != null && sessionId === this.assistantSession.id;\n\n const ctx: DiscordMessageCtx = { sessionId, thread, isAssistant };\n await dispatchMessage(this.messageHandlers, ctx, content, this.verbosity);\n }\n\n // ─── sendPermissionRequest ────────────────────────────────────────────────\n\n async sendPermissionRequest(\n sessionId: string,\n request: PermissionRequest,\n ): Promise<void> {\n const session = this.core.sessionManager.getSession(sessionId);\n if (!session) {\n log.warn(\n { sessionId },\n \"[DiscordAdapter] sendPermissionRequest: session not found\",\n );\n return;\n }\n\n const thread = await this.getThread(sessionId);\n if (!thread) return;\n\n await this.permissionHandler.sendPermissionRequest(\n session,\n request,\n thread,\n );\n }\n\n // ─── sendNotification ─────────────────────────────────────────────────────\n\n async sendNotification(notification: NotificationMessage): Promise<void> {\n if (!this.notificationChannel) return;\n\n const typeIcon: Record<string, string> = {\n completed: \"✅\",\n error: \"❌\",\n permission: \"🔐\",\n input_required: \"💬\",\n };\n\n const icon = typeIcon[notification.type] ?? \"ℹ️\";\n const name = notification.sessionName\n ? ` **${notification.sessionName}**`\n : \"\";\n let text = `${icon}${name}: ${notification.summary}`;\n if (notification.deepLink) {\n text += `\\n${notification.deepLink}`;\n }\n\n try {\n await this.sendQueue.enqueue(\n () => this.notificationChannel.send({ content: text }),\n { type: \"other\" },\n );\n } catch (err) {\n log.warn({ err }, \"[DiscordAdapter] Failed to send notification\");\n }\n }\n\n // ─── createSessionThread ─────────────────────────────────────────────────\n\n async createSessionThread(sessionId: string, name: string): Promise<string> {\n const thread = await forumsCreateThread(this.forumChannel, name);\n\n // Persist threadId on session record\n const session = this.core.sessionManager.getSession(sessionId);\n if (session) {\n session.threadId = thread.id;\n }\n\n const record = this.core.sessionManager.getSessionRecord(sessionId);\n if (record) {\n await this.core.sessionManager.patchRecord(sessionId, {\n platform: { ...record.platform, threadId: thread.id },\n });\n }\n\n return thread.id;\n }\n\n // ─── renameSessionThread ──────────────────────────────────────────────────\n\n async renameSessionThread(sessionId: string, newName: string): Promise<void> {\n const session = this.core.sessionManager.getSession(sessionId);\n const threadId = session?.threadId;\n if (!threadId) return;\n await forumsRenameThread(this.guild, threadId, newName);\n }\n\n // ─── deleteSessionThread ──────────────────────────────────────────────────\n\n override async deleteSessionThread(sessionId: string): Promise<void> {\n const session = this.core.sessionManager.getSession(sessionId);\n const threadId = session?.threadId;\n if (!threadId) return;\n await forumsDeleteThread(this.guild, threadId);\n }\n\n // ─── sendSkillCommands ────────────────────────────────────────────────────\n\n override async sendSkillCommands(\n sessionId: string,\n commands: AgentCommand[],\n ): Promise<void> {\n const thread = await this.getThread(sessionId);\n if (!thread) return;\n await this.skillManager.send(sessionId, thread, commands);\n }\n\n // ─── cleanupSkillCommands ─────────────────────────────────────────────────\n\n override async cleanupSkillCommands(sessionId: string): Promise<void> {\n await this.skillManager.cleanup(sessionId);\n }\n\n // ─── Public helpers (for slash commands) ─────────────────────────────────\n\n getForumChannel(): ForumChannel | TextChannel {\n return this.forumChannel;\n }\n\n getGuild(): Guild {\n return this.guild;\n }\n\n getGuildId(): string {\n return this.guild.id;\n }\n\n getAssistantSessionId(): string | null {\n return this.assistantSession?.id ?? null;\n }\n\n getAssistantThreadId(): string | null {\n return this.discordConfig.assistantThreadId;\n }\n}\n","import { log } from '../../core/log.js'\n\nexport type QueueItemType = 'text' | 'other'\n\ninterface QueueItem<T = unknown> {\n fn: () => Promise<T>\n type: QueueItemType\n key?: string\n resolve: (value: T | undefined) => void\n reject: (err: unknown) => void\n}\n\nexport class DiscordSendQueue {\n private items: QueueItem[] = []\n private processing = false\n private lastExec = 0\n private minInterval: number\n\n constructor(minInterval = 1000) {\n this.minInterval = minInterval\n }\n\n enqueue<T>(\n fn: () => Promise<T>,\n opts: { type: 'text' | 'other'; key?: string },\n ): Promise<T | undefined> {\n const type = opts.type\n const key = opts.key\n\n return new Promise<T | undefined>((resolve, reject) => {\n if (type === 'text' && key) {\n const idx = this.items.findIndex(\n (item) => item.type === 'text' && item.key === key,\n )\n if (idx !== -1) {\n // Resolve old pending item with undefined (dedup: replace with newer)\n this.items[idx].resolve(undefined)\n this.items[idx] = { fn, type, key, resolve, reject } as QueueItem\n this.scheduleProcess()\n return\n }\n }\n\n this.items.push({ fn, type, key, resolve, reject } as QueueItem)\n this.scheduleProcess()\n })\n }\n\n onRateLimited(): void {\n log.warn('[DiscordSendQueue] Rate limited — dropping queued text items')\n const remaining: QueueItem[] = []\n for (const item of this.items) {\n if (item.type === 'text') {\n item.resolve(undefined)\n } else {\n remaining.push(item)\n }\n }\n this.items = remaining\n }\n\n private scheduleProcess(): void {\n if (this.processing) return\n if (this.items.length === 0) return\n\n const elapsed = Date.now() - this.lastExec\n const delay = Math.max(0, this.minInterval - elapsed)\n\n this.processing = true\n setTimeout(() => void this.processNext(), delay)\n }\n\n private async processNext(): Promise<void> {\n const item = this.items.shift()\n if (!item) {\n this.processing = false\n return\n }\n\n try {\n const result = await item.fn()\n item.resolve(result)\n } catch (err) {\n item.reject(err)\n } finally {\n this.lastExec = Date.now()\n this.processing = false\n this.scheduleProcess()\n }\n }\n}\n","import type { PlanEntry } from \"../../core/types.js\";\nimport type {\n ToolCallMeta,\n ToolUpdateMeta,\n ViewerLinks,\n} from \"../shared/format-types.js\";\nimport { STATUS_ICONS, KIND_ICONS } from \"../shared/format-types.js\";\nimport {\n progressBar,\n formatTokens,\n truncateContent,\n stripCodeFences,\n splitMessage as sharedSplitMessage,\n} from \"../shared/format-utils.js\";\nimport {\n extractContentText,\n formatToolSummary,\n formatToolTitle,\n resolveToolIcon,\n} from \"../shared/message-formatter.js\";\nimport type { DisplayVerbosity } from \"../shared/format-types.js\";\n\nfunction formatViewerLinks(links?: ViewerLinks, filePath?: string): string {\n if (!links) return \"\";\n const fileName = filePath ? filePath.split(\"/\").pop() || filePath : \"\";\n let text = \"\\n\";\n if (links.file) text += `\\n[View ${fileName || \"file\"}](${links.file})`;\n if (links.diff)\n text += `\\n[View diff${fileName ? ` — ${fileName}` : \"\"}](${links.diff})`;\n return text;\n}\n\nfunction formatHighDetails(\n rawInput: unknown,\n content: unknown,\n maxLen: number,\n): string {\n let text = \"\";\n if (rawInput) {\n const inputStr =\n typeof rawInput === \"string\"\n ? rawInput\n : JSON.stringify(rawInput, null, 2);\n if (inputStr && inputStr !== \"{}\") {\n text += `\\n**Input:**\\n\\`\\`\\`\\n${truncateContent(inputStr, maxLen)}\\n\\`\\`\\``;\n }\n }\n const details = stripCodeFences(extractContentText(content));\n if (details) {\n text += `\\n**Output:**\\n\\`\\`\\`\\n${truncateContent(details, maxLen)}\\n\\`\\`\\``;\n }\n return text;\n}\n\nexport function formatToolCall(\n tool: ToolCallMeta,\n verbosity: DisplayVerbosity = \"medium\",\n): string {\n const si = resolveToolIcon(tool);\n const name = tool.name || \"Tool\";\n const label =\n verbosity === \"low\"\n ? formatToolTitle(name, tool.rawInput, tool.displayTitle)\n : formatToolSummary(name, tool.rawInput, tool.displaySummary);\n let text = `${si} **${label}**`;\n // viewer links always shown regardless of verbosity\n text += formatViewerLinks(tool.viewerLinks, tool.viewerFilePath);\n // high only: rawInput + content\n if (verbosity === \"high\") {\n text += formatHighDetails(tool.rawInput, tool.content, 500);\n }\n return text;\n}\n\nexport function formatToolUpdate(\n update: ToolUpdateMeta,\n verbosity: DisplayVerbosity = \"medium\",\n): string {\n return formatToolCall(update, verbosity);\n}\n\nexport function formatPlan(\n entries: PlanEntry[],\n verbosity: DisplayVerbosity = \"medium\",\n): string {\n // medium: summary count only\n if (verbosity === \"medium\") {\n const done = entries.filter((e) => e.status === \"completed\").length;\n return `📋 **Plan:** ${done}/${entries.length} steps completed`;\n }\n // high: full entries\n const statusIcon: Record<string, string> = {\n pending: \"⏳\",\n in_progress: \"🔄\",\n completed: \"✅\",\n };\n const lines = entries.map(\n (e, i) => `${statusIcon[e.status] || \"⬜\"} ${i + 1}. ${e.content}`,\n );\n return `**Plan:**\\n${lines.join(\"\\n\")}`;\n}\n\nexport function formatUsage(\n usage: { tokensUsed?: number; contextSize?: number; cost?: number },\n verbosity: DisplayVerbosity = \"medium\",\n): string {\n const { tokensUsed, contextSize, cost } = usage;\n if (tokensUsed == null) return \"📊 Usage data unavailable\";\n\n // medium: compact one-line\n if (verbosity === \"medium\") {\n const costStr = cost != null ? ` · $${cost.toFixed(2)}` : \"\";\n return `📊 ${formatTokens(tokensUsed)} tokens${costStr}`;\n }\n\n // high: full progress bar + cost\n if (contextSize == null) return `📊 ${formatTokens(tokensUsed)} tokens`;\n const ratio = tokensUsed / contextSize;\n const pct = Math.round(ratio * 100);\n const bar = progressBar(ratio);\n const emoji = pct >= 85 ? \"⚠️\" : \"📊\";\n let text = `${emoji} ${formatTokens(tokensUsed)} / ${formatTokens(contextSize)} tokens\\n${bar} ${pct}%`;\n if (cost != null) text += `\\n💰 $${cost.toFixed(2)}`;\n return text;\n}\n\nexport function splitMessage(text: string, maxLength = 1800): string[] {\n return sharedSplitMessage(text, maxLength);\n}\n","import type { TextChannel, ThreadChannel, Message } from \"discord.js\";\nimport { log } from \"../../core/log.js\";\nimport { formatToolCall, formatToolUpdate } from \"./formatting.js\";\nimport type { DiscordSendQueue } from \"./send-queue.js\";\nimport type {\n ToolCallMeta,\n ViewerLinks,\n DisplayVerbosity,\n} from \"../shared/format-types.js\";\n\ninterface ToolCallState {\n message?: Message;\n name: string;\n kind?: string;\n rawInput?: unknown;\n viewerLinks?: ViewerLinks;\n viewerFilePath?: string;\n ready: Promise<void>;\n}\n\nexport class ToolCallTracker {\n sessions: Map<string, Map<string, ToolCallState>> = new Map();\n\n constructor(private sendQueue: DiscordSendQueue) {}\n\n async trackNewCall(\n sessionId: string,\n thread: TextChannel | ThreadChannel,\n tool: ToolCallMeta,\n verbosity: DisplayVerbosity = \"medium\",\n ): Promise<void> {\n if (!this.sessions.has(sessionId)) {\n this.sessions.set(sessionId, new Map());\n }\n\n let resolveReady!: () => void;\n const ready = new Promise<void>((r) => {\n resolveReady = r;\n });\n\n const state: ToolCallState = {\n message: undefined,\n name: tool.name,\n kind: tool.kind,\n rawInput: tool.rawInput,\n viewerLinks: tool.viewerLinks,\n viewerFilePath: tool.viewerFilePath,\n ready,\n };\n\n this.sessions.get(sessionId)!.set(tool.id, state);\n\n const content = formatToolCall(tool, verbosity);\n\n try {\n const msg = await this.sendQueue.enqueue(() => thread.send({ content }), {\n type: \"other\",\n });\n if (msg) state.message = msg;\n } catch (err) {\n log.warn(\n { err, toolId: tool.id },\n \"[ToolCallTracker] trackNewCall() send failed\",\n );\n } finally {\n resolveReady();\n }\n }\n\n async updateCall(\n sessionId: string,\n update: ToolCallMeta & { status: string },\n verbosity: DisplayVerbosity = \"medium\",\n ): Promise<void> {\n const toolState = this.sessions.get(sessionId)?.get(update.id);\n if (!toolState) return;\n\n // Accumulate fields from intermediate updates\n if (update.viewerLinks) toolState.viewerLinks = update.viewerLinks;\n if (update.viewerFilePath) toolState.viewerFilePath = update.viewerFilePath;\n if (update.name) toolState.name = update.name;\n if (update.kind) toolState.kind = update.kind;\n\n // Only edit on terminal status — minimizes API calls to avoid rate limits\n const isTerminal =\n update.status === \"completed\" || update.status === \"failed\";\n if (!isTerminal) return;\n\n // Wait for initial send to complete before editing\n await toolState.ready;\n\n if (!toolState.message) return;\n\n log.debug(\n {\n toolId: update.id,\n status: update.status,\n hasViewerLinks: !!toolState.viewerLinks,\n name: toolState.name,\n msgId: toolState.message.id,\n },\n \"[ToolCallTracker] Tool completed, preparing edit\",\n );\n\n const merged = {\n ...update,\n name: toolState.name,\n kind: toolState.kind,\n rawInput: toolState.rawInput,\n viewerLinks: toolState.viewerLinks,\n viewerFilePath: toolState.viewerFilePath,\n };\n const content = formatToolUpdate(merged, verbosity);\n\n try {\n await this.sendQueue.enqueue(() => toolState.message!.edit({ content }), {\n type: \"other\",\n });\n } catch (err) {\n log.warn(\n {\n err,\n msgId: toolState.message.id,\n contentLen: content.length,\n hasViewerLinks: !!merged.viewerLinks,\n },\n \"[ToolCallTracker] Tool update edit failed\",\n );\n }\n }\n\n cleanup(sessionId: string): void {\n this.sessions.delete(sessionId);\n }\n}\n","import type { TextChannel, ThreadChannel, Message } from 'discord.js'\nimport { splitMessage } from './formatting.js'\nimport type { DiscordSendQueue } from './send-queue.js'\n\nconst FLUSH_INTERVAL = 5000\nconst MAX_DISPLAY_LENGTH = 1900\n\nexport class MessageDraft {\n private buffer: string = ''\n private message?: Message\n private flushTimer?: ReturnType<typeof setTimeout>\n private flushPromise: Promise<void> = Promise.resolve()\n private lastSentBuffer: string = ''\n private displayTruncated = false\n private firstFlushPending = false\n\n constructor(\n private thread: TextChannel | ThreadChannel,\n private sendQueue: DiscordSendQueue,\n private sessionId: string,\n ) {}\n\n append(text: string): void {\n if (!text) return\n this.buffer += text\n this.scheduleFlush()\n }\n\n getBuffer(): string {\n return this.buffer\n }\n\n private scheduleFlush(): void {\n if (this.flushTimer) return\n this.flushTimer = setTimeout(() => {\n this.flushTimer = undefined\n this.flushPromise = this.flushPromise\n .then(() => this.flush())\n .catch(() => {})\n }, FLUSH_INTERVAL)\n }\n\n async flush(): Promise<void> {\n if (!this.buffer) return\n if (this.firstFlushPending) return\n\n // Snapshot buffer before any await — append() can be called concurrently\n const snapshot = this.buffer\n\n let content = snapshot\n let truncated = false\n if (content.length > MAX_DISPLAY_LENGTH) {\n content = snapshot.slice(0, MAX_DISPLAY_LENGTH) + '…'\n truncated = true\n }\n\n if (!content) return\n\n if (!this.message) {\n this.firstFlushPending = true\n try {\n const result = await this.sendQueue.enqueue(\n () => this.thread.send({ content }),\n { type: 'other' },\n )\n if (result) {\n this.message = result\n if (!truncated) {\n this.lastSentBuffer = snapshot\n this.displayTruncated = false\n } else {\n this.displayTruncated = true\n }\n }\n } catch {\n // send failed — next flush will retry\n } finally {\n this.firstFlushPending = false\n }\n } else {\n // Skip if content hasn't changed since last send\n if (!truncated && snapshot === this.lastSentBuffer) return\n\n try {\n const result = await this.sendQueue.enqueue(\n () => this.message!.edit({ content }),\n { type: 'text', key: this.sessionId },\n )\n // Only mark as sent if the edit was actually executed (not deduped/dropped)\n if (result !== undefined) {\n if (!truncated) {\n this.lastSentBuffer = snapshot\n this.displayTruncated = false\n } else {\n this.displayTruncated = true\n }\n }\n } catch {\n // Don't reset message — transient errors should not cause duplicate sends\n }\n }\n }\n\n async stripPattern(pattern: RegExp): Promise<void> {\n if (!this.message || !this.buffer) return\n\n const stripped = this.buffer.replace(pattern, '').trim()\n if (stripped === this.buffer.trim()) return\n\n this.buffer = stripped\n this.lastSentBuffer = stripped\n\n if (!stripped) return\n\n try {\n await this.sendQueue.enqueue(\n () => this.message!.edit({ content: stripped }),\n { type: 'other' },\n )\n } catch {\n // Best effort — non-critical edit\n }\n }\n\n async finalize(): Promise<void> {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer)\n this.flushTimer = undefined\n }\n\n // Wait for any in-progress flush to complete\n await this.flushPromise\n\n if (!this.buffer) return\n\n // Skip if buffer was already fully sent and nothing new appended\n if (this.message && this.buffer === this.lastSentBuffer && !this.displayTruncated) {\n return\n }\n\n // Try to send full buffer as a single message (most common case)\n if (this.buffer.length <= MAX_DISPLAY_LENGTH) {\n const content = this.buffer\n try {\n if (this.message) {\n await this.sendQueue.enqueue(\n () => this.message!.edit({ content }),\n { type: 'other' },\n )\n } else {\n await this.sendQueue.enqueue(\n () => this.thread.send({ content }),\n { type: 'other' },\n )\n }\n return\n } catch {\n // Fall through to split approach\n }\n }\n\n // Buffer exceeds limit or single send failed — split and send chunks\n const chunks = splitMessage(this.buffer, MAX_DISPLAY_LENGTH)\n\n for (let i = 0; i < chunks.length; i++) {\n const content = chunks[i]\n try {\n if (i === 0 && this.message) {\n await this.sendQueue.enqueue(\n () => this.message!.edit({ content }),\n { type: 'other' },\n )\n } else {\n const msg = await this.sendQueue.enqueue(\n () => this.thread.send({ content }),\n { type: 'other' },\n )\n if (msg) {\n this.message = msg\n }\n }\n } catch {\n // Skip this chunk — best effort\n }\n }\n }\n}\n","import type { TextChannel, ThreadChannel } from 'discord.js'\nimport { MessageDraft } from './streaming.js'\nimport type { DiscordSendQueue } from './send-queue.js'\nimport { detectAction, storeAction, buildActionKeyboard } from './action-detect.js'\n\nexport class DraftManager {\n drafts: Map<string, MessageDraft> = new Map()\n textBuffers: Map<string, string> = new Map()\n\n constructor(\n private sendQueue: DiscordSendQueue,\n ) {}\n\n getOrCreate(sessionId: string, thread: TextChannel | ThreadChannel): MessageDraft {\n let draft = this.drafts.get(sessionId)\n if (!draft) {\n draft = new MessageDraft(thread, this.sendQueue, sessionId)\n this.drafts.set(sessionId, draft)\n }\n return draft\n }\n\n hasDraft(sessionId: string): boolean {\n return this.drafts.has(sessionId)\n }\n\n getDraft(sessionId: string): MessageDraft | undefined {\n return this.drafts.get(sessionId)\n }\n\n appendText(sessionId: string, text: string): void {\n this.textBuffers.set(sessionId, (this.textBuffers.get(sessionId) ?? '') + text)\n }\n\n /**\n * Finalize the current draft.\n * If isAssistant is true, detects action patterns in the accumulated text and sends\n * action buttons as a follow-up message.\n */\n async finalize(\n sessionId: string,\n thread?: TextChannel | ThreadChannel,\n isAssistant?: boolean,\n ): Promise<void> {\n const draft = this.drafts.get(sessionId)\n if (!draft) return\n\n // Delete BEFORE awaiting to prevent concurrent finalize() calls\n // from double-finalizing the same draft\n this.drafts.delete(sessionId)\n await draft.finalize()\n\n // Detect actions in assistant responses and attach action buttons\n if (isAssistant && thread) {\n const fullText = this.textBuffers.get(sessionId)\n this.textBuffers.delete(sessionId)\n if (fullText) {\n const detected = detectAction(fullText)\n if (detected) {\n const actionId = storeAction(detected)\n const components = [buildActionKeyboard(actionId, detected)]\n try {\n await this.sendQueue.enqueue(\n () => thread.send({ components }),\n { type: 'other' },\n )\n } catch {\n // Best effort — action buttons are non-critical\n }\n }\n }\n } else {\n this.textBuffers.delete(sessionId)\n }\n }\n\n cleanup(sessionId: string): void {\n this.drafts.delete(sessionId)\n this.textBuffers.delete(sessionId)\n }\n}\n","import type { TextChannel, ThreadChannel, Message } from \"discord.js\";\nimport { EmbedBuilder } from \"discord.js\";\nimport { log } from \"../../core/log.js\";\nimport type { PlanEntry } from \"../../core/types.js\";\nimport { formatUsage, formatPlan } from \"./formatting.js\";\nimport type { DisplayVerbosity } from \"../shared/format-types.js\";\nimport type { DiscordSendQueue } from \"./send-queue.js\";\n\n// ─── ThinkingIndicator ────────────────────────────────────────────────────────\n\nconst TYPING_REFRESH_MS = 8_000;\n\nexport class ThinkingIndicator {\n private dismissed = false;\n private refreshTimer?: ReturnType<typeof setInterval>;\n\n constructor(private channel: TextChannel | ThreadChannel) {}\n\n async show(): Promise<void> {\n if (this.dismissed) return;\n try {\n await this.channel.sendTyping();\n this.startRefreshTimer();\n } catch (err) {\n log.warn({ err }, \"[ThinkingIndicator] sendTyping() failed\");\n }\n }\n\n dismiss(): void {\n this.dismissed = true;\n this.stopRefreshTimer();\n }\n\n reset(): void {\n this.dismissed = false;\n }\n\n private startRefreshTimer(): void {\n this.stopRefreshTimer();\n this.refreshTimer = setInterval(() => {\n if (this.dismissed) {\n this.stopRefreshTimer();\n return;\n }\n this.channel.sendTyping().catch(() => {});\n }, TYPING_REFRESH_MS);\n }\n\n private stopRefreshTimer(): void {\n if (this.refreshTimer) {\n clearInterval(this.refreshTimer);\n this.refreshTimer = undefined;\n }\n }\n}\n\n// ─── UsageMessage ─────────────────────────────────────────────────────────────\n\nexport class UsageMessage {\n private message?: Message;\n\n constructor(\n private thread: TextChannel | ThreadChannel,\n private sendQueue: DiscordSendQueue,\n ) {}\n\n async send(\n usage: { tokensUsed?: number; contextSize?: number; cost?: number },\n verbosity: DisplayVerbosity = \"medium\",\n ): Promise<void> {\n const text = formatUsage(usage, verbosity);\n const embed = new EmbedBuilder().setDescription(text);\n try {\n if (this.message) {\n await this.sendQueue.enqueue(\n () => this.message!.edit({ embeds: [embed] }),\n { type: \"other\" },\n );\n } else {\n const result = await this.sendQueue.enqueue(\n () => this.thread.send({ embeds: [embed] }),\n { type: \"other\" },\n );\n if (result) this.message = result;\n }\n } catch (err) {\n log.warn({ err }, \"[UsageMessage] send() failed\");\n }\n }\n\n async delete(): Promise<void> {\n if (!this.message) return;\n const msg = this.message;\n this.message = undefined;\n try {\n await this.sendQueue.enqueue(() => msg.delete(), { type: \"other\" });\n } catch (err) {\n log.warn({ err }, \"[UsageMessage] delete() failed\");\n }\n }\n}\n\n// ─── PlanCard ─────────────────────────────────────────────────────────────────\n\nconst PLAN_DEBOUNCE_MS = 3_500;\n\nexport class PlanCard {\n private message?: Message;\n private flushPromise: Promise<void> = Promise.resolve();\n private latestEntries?: PlanEntry[];\n private lastSentText?: string;\n private flushTimer?: ReturnType<typeof setTimeout>;\n private verbosity: DisplayVerbosity = \"medium\";\n\n constructor(\n private thread: TextChannel | ThreadChannel,\n private sendQueue: DiscordSendQueue,\n ) {}\n\n setVerbosity(v: DisplayVerbosity): void {\n this.verbosity = v;\n }\n\n update(entries: PlanEntry[]): void {\n this.latestEntries = entries;\n if (this.flushTimer) clearTimeout(this.flushTimer);\n this.flushTimer = setTimeout(() => {\n this.flushTimer = undefined;\n this.flushPromise = this.flushPromise\n .then(() => this._flush())\n .catch(() => {});\n }, PLAN_DEBOUNCE_MS);\n }\n\n async finalize(): Promise<void> {\n if (!this.latestEntries) return;\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = undefined;\n }\n await this.flushPromise;\n this.flushPromise = this.flushPromise\n .then(() => this._flush())\n .catch(() => {});\n await this.flushPromise;\n }\n\n destroy(): void {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = undefined;\n }\n }\n\n private async _flush(): Promise<void> {\n if (!this.latestEntries) return;\n const text = formatPlan(this.latestEntries, this.verbosity);\n if (this.message && text === this.lastSentText) return;\n this.lastSentText = text;\n const embed = new EmbedBuilder().setDescription(text);\n try {\n if (this.message) {\n await this.sendQueue.enqueue(\n () => this.message!.edit({ embeds: [embed] }),\n { type: \"other\" },\n );\n } else {\n const result = await this.sendQueue.enqueue(\n () => this.thread.send({ embeds: [embed] }),\n { type: \"other\" },\n );\n if (result) this.message = result;\n }\n } catch (err) {\n log.warn({ err }, \"[PlanCard] flush failed\");\n }\n }\n}\n\n// ─── ActivityTracker ──────────────────────────────────────────────────────────\n\nexport class ActivityTracker {\n private isFirstEvent = true;\n private hasPlanCard = false;\n private thinking: ThinkingIndicator;\n private planCard: PlanCard;\n private usage: UsageMessage;\n\n constructor(\n private thread: TextChannel | ThreadChannel,\n private sendQueue: DiscordSendQueue,\n ) {\n this.thinking = new ThinkingIndicator(thread);\n this.planCard = new PlanCard(thread, sendQueue);\n this.usage = new UsageMessage(thread, sendQueue);\n }\n\n async onNewPrompt(): Promise<void> {\n this.isFirstEvent = true;\n this.hasPlanCard = false;\n this.thinking.dismiss();\n this.thinking.reset();\n }\n\n async onThought(): Promise<void> {\n await this._firstEventGuard();\n await this.thinking.show();\n }\n\n async onTextStart(): Promise<void> {\n await this._firstEventGuard();\n this.thinking.dismiss();\n }\n\n async onToolCall(): Promise<void> {\n await this._firstEventGuard();\n this.thinking.dismiss();\n this.thinking.reset();\n }\n\n async onPlan(\n entries: PlanEntry[],\n verbosity?: DisplayVerbosity,\n ): Promise<void> {\n await this._firstEventGuard();\n this.thinking.dismiss();\n this.hasPlanCard = true;\n if (verbosity) this.planCard.setVerbosity(verbosity);\n this.planCard.update(entries);\n }\n\n async sendUsage(\n usage: { tokensUsed?: number; contextSize?: number; cost?: number },\n verbosity: DisplayVerbosity = \"medium\",\n ): Promise<void> {\n await this.usage.send(usage, verbosity);\n }\n\n async cleanup(): Promise<void> {\n this.thinking.dismiss();\n this.planCard.destroy();\n if (this.hasPlanCard) {\n await this.planCard.finalize();\n }\n }\n\n private async _firstEventGuard(): Promise<void> {\n if (!this.isFirstEvent) return;\n this.isFirstEvent = false;\n await this.usage.delete();\n }\n}\n","import type { Message, TextChannel, ThreadChannel } from 'discord.js'\nimport { log } from '../../core/log.js'\nimport type { AgentCommand } from '../../core/types.js'\nimport type { SessionManager } from '../../core/session-manager.js'\nimport type { DiscordPlatformData } from '../../core/types.js'\nimport type { DiscordSendQueue } from './send-queue.js'\n\nconst DISCORD_MSG_LIMIT = 1900\n\nfunction buildSkillContent(commands: AgentCommand[]): string {\n const sorted = [...commands].sort((a, b) => a.name.localeCompare(b.name))\n const header = '**Available Skills**\\n'\n const lines = sorted.map((c) => `\\`/${c.name}\\``)\n let content = header\n for (const line of lines) {\n const candidate = content + '\\n' + line\n if (candidate.length > DISCORD_MSG_LIMIT) break\n content = candidate\n }\n return content\n}\n\nexport class SkillCommandManager {\n private messages: Map<string, Message> = new Map()\n\n constructor(\n private sendQueue: DiscordSendQueue,\n private sessionManager: SessionManager,\n ) {}\n\n async send(\n sessionId: string,\n thread: TextChannel | ThreadChannel,\n commands: AgentCommand[],\n ): Promise<void> {\n // Restore from persisted platform data if not in memory\n if (!this.messages.has(sessionId)) {\n const record = this.sessionManager.getSessionRecord(sessionId)\n const platform = record?.platform as DiscordPlatformData | undefined\n if (platform?.skillMsgId) {\n try {\n const msg = await thread.messages.fetch(platform.skillMsgId)\n if (msg) this.messages.set(sessionId, msg)\n } catch {\n // Message may no longer exist — will send a new one\n }\n }\n }\n\n // Empty commands → remove pinned message\n if (commands.length === 0) {\n await this.cleanup(sessionId)\n return\n }\n\n const content = buildSkillContent(commands)\n const existingMsg = this.messages.get(sessionId)\n\n if (existingMsg) {\n try {\n await existingMsg.edit({ content })\n return\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : ''\n if (msg.includes('Unknown Message') || msg.includes('10008')) {\n // Message no longer exists — fall through to send a new one\n this.messages.delete(sessionId)\n } else {\n // Transient error or not-modified — just return\n return\n }\n }\n }\n\n // Send new message and pin it\n try {\n const msg = await this.sendQueue.enqueue(\n () => thread.send({ content }),\n { type: 'other' },\n )\n\n if (!msg) return\n\n this.messages.set(sessionId, msg)\n\n // Persist skillMsgId so it survives restarts\n const record = this.sessionManager.getSessionRecord(sessionId)\n if (record) {\n await this.sessionManager.patchRecord(sessionId, {\n platform: { ...record.platform, skillMsgId: msg.id },\n })\n }\n\n // Pin the message\n try {\n await msg.pin()\n } catch (err) {\n log.warn({ err, sessionId }, '[SkillCommandManager] Failed to pin skill message')\n }\n } catch (err) {\n log.error({ err, sessionId }, '[SkillCommandManager] Failed to send skill commands')\n }\n }\n\n async cleanup(sessionId: string): Promise<void> {\n const msg = this.messages.get(sessionId)\n this.messages.delete(sessionId)\n\n if (msg) {\n try {\n await msg.edit({ content: '*Session ended*' })\n await msg.unpin()\n } catch {\n // Message may already be deleted\n }\n }\n\n // Clear persisted skillMsgId\n const record = this.sessionManager.getSessionRecord(sessionId)\n if (record) {\n const platform = record.platform\n if (platform && typeof platform === 'object' && 'threadId' in platform) {\n const { skillMsgId: _removed, ...rest } = platform as unknown as DiscordPlatformData\n await this.sessionManager.patchRecord(sessionId, { platform: rest })\n }\n }\n }\n}\n","import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'\nimport type { ButtonInteraction, TextChannel, ThreadChannel } from 'discord.js'\nimport { nanoid } from 'nanoid'\nimport type { PermissionRequest, NotificationMessage } from '../../core/types.js'\nimport type { Session } from '../../core/session.js'\nimport { log } from '../../core/log.js'\nimport { buildDeepLink } from './forums.js'\n\ninterface PendingPermission {\n sessionId: string\n requestId: string\n options: { id: string; isAllow: boolean }[]\n guildId: string\n channelId: string\n messageId?: string\n}\n\nexport class PermissionHandler {\n private pending: Map<string, PendingPermission> = new Map()\n\n constructor(\n private guildId: string,\n private getSession: (sessionId: string) => Session | undefined,\n private sendNotification: (notification: NotificationMessage) => Promise<void>,\n ) {}\n\n async sendPermissionRequest(\n session: Session,\n request: PermissionRequest,\n thread: TextChannel | ThreadChannel,\n ): Promise<void> {\n // Short callback key (Discord 100-char customId limit)\n const callbackKey = nanoid(8)\n this.pending.set(callbackKey, {\n sessionId: session.id,\n requestId: request.id,\n options: request.options.map((o) => ({ id: o.id, isAllow: o.isAllow })),\n guildId: this.guildId,\n channelId: thread.id,\n })\n\n // Build action row with buttons\n const row = new ActionRowBuilder<ButtonBuilder>()\n for (const option of request.options) {\n const emoji = option.isAllow ? '✅' : '❌'\n row.addComponents(\n new ButtonBuilder()\n .setCustomId(`p:${callbackKey}:${option.id}`)\n .setLabel(`${emoji} ${option.label}`)\n .setStyle(option.isAllow ? ButtonStyle.Success : ButtonStyle.Danger),\n )\n }\n\n // Send permission message in session thread\n let messageId: string | undefined\n try {\n const msg = await thread.send({\n content: `🔐 **Permission request:**\\n\\n${request.description}`,\n components: [row],\n })\n messageId = msg.id\n // Store messageId for deep link\n const pendingEntry = this.pending.get(callbackKey)\n if (pendingEntry) pendingEntry.messageId = messageId\n } catch (err) {\n log.warn({ err, sessionId: session.id }, '[PermissionHandler] Failed to send permission request')\n return\n }\n\n // Build deep link for notification\n const deepLink = buildDeepLink(this.guildId, thread.id, messageId)\n\n // Fire-and-forget notification to avoid sendQueue deadlock\n void this.sendNotification({\n sessionId: session.id,\n sessionName: session.name,\n type: 'permission',\n summary: request.description,\n deepLink,\n })\n }\n\n async handleButtonInteraction(interaction: ButtonInteraction): Promise<boolean> {\n const data = interaction.customId\n if (!data.startsWith('p:')) return false\n\n const parts = data.split(':')\n if (parts.length < 3) return false\n\n const [, callbackKey, optionId] = parts\n\n const pending = this.pending.get(callbackKey)\n if (!pending) {\n try {\n await interaction.reply({ content: '❌ Permission request expired', ephemeral: true })\n } catch { /* ignore */ }\n return true\n }\n\n const session = this.getSession(pending.sessionId)\n const option = pending.options.find((o) => o.id === optionId)\n const isAllow = option?.isAllow ?? false\n\n log.info(\n { requestId: pending.requestId, optionId, isAllow },\n '[PermissionHandler] Permission responded',\n )\n\n if (session?.permissionGate.requestId === pending.requestId) {\n session.permissionGate.resolve(optionId)\n }\n\n this.pending.delete(callbackKey)\n\n try {\n await interaction.reply({ content: '✅ Responded', ephemeral: true })\n } catch { /* ignore */ }\n\n // Remove buttons from the original message\n try {\n await interaction.message.edit({ components: [] })\n } catch { /* ignore */ }\n\n return true\n }\n}\n","import { SlashCommandBuilder } from \"discord.js\";\nimport type { Guild } from \"discord.js\";\n\nexport const SLASH_COMMANDS = [\n new SlashCommandBuilder()\n .setName(\"new\")\n .setDescription(\"Create a new agent session\")\n .addStringOption((o) =>\n o.setName(\"agent\").setDescription(\"Agent to use\").setRequired(false),\n )\n .addStringOption((o) =>\n o\n .setName(\"workspace\")\n .setDescription(\"Workspace directory\")\n .setRequired(false),\n ),\n\n new SlashCommandBuilder()\n .setName(\"newchat\")\n .setDescription(\n \"New chat in current thread, inheriting agent and workspace\",\n ),\n\n new SlashCommandBuilder()\n .setName(\"cancel\")\n .setDescription(\"Cancel the current session\"),\n\n new SlashCommandBuilder()\n .setName(\"status\")\n .setDescription(\"Show session or global status\"),\n\n new SlashCommandBuilder()\n .setName(\"sessions\")\n .setDescription(\"List all sessions\"),\n\n new SlashCommandBuilder()\n .setName(\"agents\")\n .setDescription(\"List available agents\"),\n\n new SlashCommandBuilder()\n .setName(\"install\")\n .setDescription(\"Install an agent by name\")\n .addStringOption((o) =>\n o\n .setName(\"name\")\n .setDescription(\"Agent name to install\")\n .setRequired(true),\n ),\n\n new SlashCommandBuilder()\n .setName(\"menu\")\n .setDescription(\"Show the action menu\"),\n\n new SlashCommandBuilder().setName(\"help\").setDescription(\"Show help\"),\n\n new SlashCommandBuilder()\n .setName(\"dangerous\")\n .setDescription(\"Toggle dangerous mode for the current session\"),\n\n new SlashCommandBuilder()\n .setName(\"restart\")\n .setDescription(\"Restart OpenACP\"),\n\n new SlashCommandBuilder()\n .setName(\"update\")\n .setDescription(\"Update to the latest version\"),\n\n new SlashCommandBuilder()\n .setName(\"integrate\")\n .setDescription(\"Manage agent integrations\"),\n\n new SlashCommandBuilder()\n .setName(\"settings\")\n .setDescription(\"Show configuration settings\"),\n\n new SlashCommandBuilder()\n .setName(\"doctor\")\n .setDescription(\"Run system diagnostics\"),\n\n new SlashCommandBuilder()\n .setName(\"handoff\")\n .setDescription(\"Generate a terminal resume command for this session\"),\n\n new SlashCommandBuilder()\n .setName(\"clear\")\n .setDescription(\"Reset the assistant session\"),\n\n new SlashCommandBuilder()\n .setName(\"tts\")\n .setDescription(\"Toggle Text to Speech for the current session\")\n .addStringOption((o) =>\n o\n .setName(\"mode\")\n .setDescription(\n \"on = persistent, off = disable, empty = next message only\",\n )\n .setRequired(false)\n .addChoices({ name: \"on\", value: \"on\" }, { name: \"off\", value: \"off\" }),\n ),\n\n new SlashCommandBuilder()\n .setName(\"verbosity\")\n .setDescription(\"Set display verbosity level\")\n .addStringOption((o) =>\n o\n .setName(\"level\")\n .setDescription(\"Verbosity level\")\n .setRequired(true)\n .addChoices(\n { name: \"low — minimal, title only\", value: \"low\" },\n { name: \"medium — balanced (default)\", value: \"medium\" },\n { name: \"high — full detail with content\", value: \"high\" },\n ),\n ),\n];\n\nexport async function registerSlashCommands(guild: Guild): Promise<void> {\n await guild.commands.set(SLASH_COMMANDS.map((cmd) => cmd.toJSON()));\n}\n\nexport { handleSlashCommand, setupButtonCallbacks } from \"./router.js\";\n","import {\n ActionRowBuilder,\n ButtonBuilder,\n ButtonStyle,\n} from 'discord.js'\nimport type { ChatInputCommandInteraction, ButtonInteraction } from 'discord.js'\nimport { log } from '../../../core/log.js'\nimport type { DiscordAdapter } from '../adapter.js'\n\nexport function buildMenuKeyboard(): ActionRowBuilder<ButtonBuilder>[] {\n const row1 = new ActionRowBuilder<ButtonBuilder>().addComponents(\n new ButtonBuilder().setCustomId('m:new').setLabel('🆕 New Session').setStyle(ButtonStyle.Primary),\n new ButtonBuilder().setCustomId('m:sessions').setLabel('📋 Sessions').setStyle(ButtonStyle.Secondary),\n new ButtonBuilder().setCustomId('m:status').setLabel('📊 Status').setStyle(ButtonStyle.Secondary),\n )\n const row2 = new ActionRowBuilder<ButtonBuilder>().addComponents(\n new ButtonBuilder().setCustomId('m:agents').setLabel('🤖 Agents').setStyle(ButtonStyle.Secondary),\n new ButtonBuilder().setCustomId('m:settings').setLabel('⚙️ Settings').setStyle(ButtonStyle.Secondary),\n new ButtonBuilder().setCustomId('m:integrate').setLabel('🔗 Integrate').setStyle(ButtonStyle.Secondary),\n )\n const row3 = new ActionRowBuilder<ButtonBuilder>().addComponents(\n new ButtonBuilder().setCustomId('m:tts').setLabel('🔊 TTS').setStyle(ButtonStyle.Secondary),\n new ButtonBuilder().setCustomId('m:restart').setLabel('🔄 Restart').setStyle(ButtonStyle.Secondary),\n new ButtonBuilder().setCustomId('m:update').setLabel('⬆️ Update').setStyle(ButtonStyle.Secondary),\n new ButtonBuilder().setCustomId('m:help').setLabel('❓ Help').setStyle(ButtonStyle.Secondary),\n new ButtonBuilder().setCustomId('m:doctor').setLabel('🩺 Doctor').setStyle(ButtonStyle.Secondary),\n )\n return [row1, row2, row3]\n}\n\nexport async function handleMenu(\n interaction: ChatInputCommandInteraction,\n _adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.reply({\n content: '**OpenACP Menu**\\nChoose an action:',\n components: buildMenuKeyboard(),\n ephemeral: true,\n })\n}\n\nexport async function handleHelp(\n interaction: ChatInputCommandInteraction,\n _adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.reply({\n content:\n `📖 **OpenACP Help**\\n\\n` +\n `🚀 **Getting Started**\\n` +\n `Use 🆕 New Session or \\`/new\\` to start coding with AI.\\n` +\n `Each session gets its own forum thread — chat there to work with the agent.\\n\\n` +\n `💡 **Common Commands**\\n` +\n `\\`/new [agent] [workspace]\\` — Create new session\\n` +\n `\\`/newchat\\` — New chat, same agent & workspace\\n` +\n `\\`/cancel\\` — Cancel current session\\n` +\n `\\`/status\\` — Show session or system status\\n` +\n `\\`/sessions\\` — List all sessions\\n` +\n `\\`/agents\\` — Browse & install agents\\n` +\n `\\`/install <name>\\` — Install an agent\\n\\n` +\n `⚙️ **System**\\n` +\n `\\`/restart\\` — Restart OpenACP\\n` +\n `\\`/update\\` — Update to latest version\\n` +\n `\\`/integrate\\` — Manage agent integrations\\n` +\n `\\`/settings\\` — View configuration\\n` +\n `\\`/menu\\` — Show action menu\\n\\n` +\n `🔒 **Session Options**\\n` +\n `\\`/dangerous\\` — Toggle dangerous mode (auto-approve permissions)\\n` +\n `\\`/tts\\` — Toggle Text to Speech (on/off/next message)\\n` +\n `\\`/handoff\\` — Continue session in your terminal\\n` +\n `\\`/clear\\` — Clear assistant session history\\n\\n` +\n `🩺 **Diagnostics**\\n` +\n `\\`/doctor\\` — Run system diagnostics`,\n ephemeral: true,\n })\n}\n\nexport async function handleClear(\n interaction: ChatInputCommandInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.deferReply({ ephemeral: true })\n\n if (!adapter.getAssistantSessionId()) {\n await interaction.editReply('⚠️ Assistant is not available.')\n return\n }\n\n try {\n await adapter.respawnAssistant()\n await interaction.editReply('✅ Assistant history cleared.')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n await interaction.editReply(`❌ Failed to clear: \\`${message}\\``)\n }\n}\n\nexport async function handleMenuButton(\n interaction: ButtonInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n const { customId } = interaction\n\n try {\n await interaction.deferUpdate()\n } catch { /* expired */ }\n\n try {\n switch (customId) {\n case 'm:new': {\n // Delegate to new-session handler\n const { handleNew } = await import('./new-session.js')\n // Create a fake slash command interaction proxy for the button context\n // We just show the menu inline instead\n await interaction.followUp({ content: 'Use `/new` to create a new session.', ephemeral: true })\n break\n }\n case 'm:sessions': {\n const { handleSessions } = await import('./session.js')\n // Use followUp to show the sessions list\n await showSessionsList(interaction, adapter)\n break\n }\n case 'm:status': {\n await showGlobalStatus(interaction, adapter)\n break\n }\n case 'm:agents': {\n const { showAgentsList } = await import('./agents.js')\n await showAgentsList(interaction, adapter)\n break\n }\n case 'm:settings': {\n const { showSettingsInfo } = await import('./settings.js')\n await showSettingsInfo(interaction, adapter)\n break\n }\n case 'm:integrate': {\n await interaction.followUp({ content: 'Use `/integrate` to manage integrations.', ephemeral: true })\n break\n }\n case 'm:restart': {\n const { handleRestart } = await import('./admin.js')\n if (!adapter.core.requestRestart) {\n await interaction.followUp({ content: '⚠️ Restart not available.', ephemeral: true })\n } else {\n await interaction.followUp({ content: '🔄 Restarting OpenACP...', ephemeral: true })\n await new Promise((r) => setTimeout(r, 500))\n await adapter.core.requestRestart()\n }\n break\n }\n case 'm:update': {\n await interaction.followUp({ content: '⚠️ Update not implemented yet. Run `npm install -g @openacp/cli@latest` in your terminal.', ephemeral: true })\n break\n }\n case 'm:help': {\n await interaction.followUp({ content: 'Use `/help` for command reference.', ephemeral: true })\n break\n }\n case 'm:doctor': {\n const { runDoctorInline } = await import('./doctor.js')\n await runDoctorInline(interaction, adapter)\n break\n }\n case 'm:tts': {\n const channelId = interaction.channelId\n const session = adapter.core.sessionManager.getSessionByThread('discord', channelId)\n if (!session) {\n await interaction.followUp({ content: '⚠️ No active session in this channel. Use `/tts` inside a session thread.', ephemeral: true })\n } else {\n const newMode = session.voiceMode === 'on' ? 'off' : 'on'\n session.setVoiceMode(newMode)\n const msg = newMode === 'on'\n ? '🔊 Text to Speech enabled for this session.'\n : '🔇 Text to Speech disabled.'\n await interaction.followUp({ content: msg, ephemeral: true })\n }\n break\n }\n default:\n log.warn({ customId }, '[discord-menu] Unhandled menu button')\n }\n } catch (err) {\n log.error({ err, customId }, '[discord-menu] Menu button handler failed')\n try {\n await interaction.followUp({ content: `❌ Action failed: ${err instanceof Error ? err.message : String(err)}`, ephemeral: true })\n } catch { /* ignore */ }\n }\n}\n\nasync function showGlobalStatus(interaction: ButtonInteraction, adapter: DiscordAdapter): Promise<void> {\n const sessions = adapter.core.sessionManager.listSessions('discord')\n const active = sessions.filter((s: any) => s.status === 'active' || s.status === 'initializing')\n await interaction.followUp({\n content:\n `**OpenACP Status**\\n` +\n `Active sessions: ${active.length}\\n` +\n `Total sessions: ${sessions.length}`,\n ephemeral: true,\n })\n}\n\nasync function showSessionsList(interaction: ButtonInteraction, adapter: DiscordAdapter): Promise<void> {\n const allRecords = adapter.core.sessionManager.listRecords()\n if (allRecords.length === 0) {\n await interaction.followUp({ content: 'No sessions found.', ephemeral: true })\n return\n }\n\n const STATUS_EMOJI: Record<string, string> = {\n active: '🟢', initializing: '🟡', finished: '✅', error: '❌', cancelled: '⛔',\n }\n const STATUS_ORDER: Record<string, number> = {\n active: 0, initializing: 1, error: 2, finished: 3, cancelled: 4,\n }\n\n allRecords.sort(\n (a: any, b: any) => (STATUS_ORDER[a.status] ?? 5) - (STATUS_ORDER[b.status] ?? 5),\n )\n\n const lines = allRecords.slice(0, 20).map((r: any) => {\n const emoji = STATUS_EMOJI[r.status] || '⚪'\n const name = r.name?.trim() || `${r.agentName} session`\n return `${emoji} **${name}** \\`[${r.status}]\\``\n })\n\n const truncated = allRecords.length > 20 ? `\\n\\n*...and ${allRecords.length - 20} more*` : ''\n\n await interaction.followUp({\n content: `**Sessions: ${allRecords.length}**\\n\\n${lines.join('\\n')}${truncated}`,\n ephemeral: true,\n })\n}\n","import type { ChatInputCommandInteraction, ButtonInteraction } from 'discord.js'\nimport { log } from '../../../core/log.js'\nimport type { DiscordAdapter } from '../adapter.js'\n\nexport async function handleIntegrate(\n interaction: ChatInputCommandInteraction,\n _adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.deferReply({ ephemeral: true })\n // Stub: integration management not yet implemented for Discord\n await interaction.editReply(\n '🔗 **Integrations**\\n\\nIntegration management via Discord is not yet implemented.\\n\\nUse the CLI: `openacp integrate`',\n )\n}\n\nexport async function handleIntegrateButton(\n interaction: ButtonInteraction,\n _adapter: DiscordAdapter,\n): Promise<void> {\n // Stub: integration button callbacks not yet implemented for Discord\n log.debug({ customId: interaction.customId }, '[discord-integrate] Button stub called')\n try {\n await interaction.reply({\n content: '🔗 Integration management via Discord is not yet implemented. Use the CLI: `openacp integrate`',\n ephemeral: true,\n })\n } catch { /* ignore */ }\n}\n","import type {\n ChatInputCommandInteraction,\n ButtonInteraction,\n} from \"discord.js\";\nimport { log } from \"../../../core/log.js\";\nimport type { DiscordAdapter } from \"../adapter.js\";\n\nimport {\n handleNew,\n handleNewChat,\n handleNewSessionButton,\n} from \"./new-session.js\";\nimport {\n handleCancel,\n handleStatus,\n handleSessions,\n handleHandoff,\n handleCleanupButton,\n} from \"./session.js\";\nimport {\n handleDangerous,\n handleDangerousButton,\n handleTTS,\n handleTTSButton,\n handleRestart,\n handleUpdate,\n handleVerbosity,\n} from \"./admin.js\";\nimport {\n handleMenu,\n handleHelp,\n handleClear,\n handleMenuButton,\n} from \"./menu.js\";\nimport { handleAgents, handleInstall, handleAgentButton } from \"./agents.js\";\nimport { handleDoctor, handleDoctorButton } from \"./doctor.js\";\nimport { handleIntegrate, handleIntegrateButton } from \"./integrate.js\";\nimport { handleSettings, handleSettingsButton } from \"./settings.js\";\n\nexport async function handleSlashCommand(\n interaction: ChatInputCommandInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n const { commandName } = interaction;\n\n try {\n switch (commandName) {\n case \"new\":\n await handleNew(interaction, adapter);\n break;\n case \"newchat\":\n await handleNewChat(interaction, adapter);\n break;\n case \"cancel\":\n await handleCancel(interaction, adapter);\n break;\n case \"status\":\n await handleStatus(interaction, adapter);\n break;\n case \"sessions\":\n await handleSessions(interaction, adapter);\n break;\n case \"agents\":\n await handleAgents(interaction, adapter);\n break;\n case \"install\":\n await handleInstall(interaction, adapter);\n break;\n case \"menu\":\n await handleMenu(interaction, adapter);\n break;\n case \"help\":\n await handleHelp(interaction, adapter);\n break;\n case \"dangerous\":\n await handleDangerous(interaction, adapter);\n break;\n case \"restart\":\n await handleRestart(interaction, adapter);\n break;\n case \"update\":\n await handleUpdate(interaction, adapter);\n break;\n case \"integrate\":\n await handleIntegrate(interaction, adapter);\n break;\n case \"settings\":\n await handleSettings(interaction, adapter);\n break;\n case \"doctor\":\n await handleDoctor(interaction, adapter);\n break;\n case \"handoff\":\n await handleHandoff(interaction, adapter);\n break;\n case \"clear\":\n await handleClear(interaction, adapter);\n break;\n case \"tts\":\n await handleTTS(interaction, adapter);\n break;\n case \"verbosity\":\n await handleVerbosity(interaction, adapter);\n break;\n default:\n log.warn({ commandName }, \"[discord-router] Unknown slash command\");\n if (!interaction.replied && !interaction.deferred) {\n await interaction.reply({\n content: `Unknown command: /${commandName}`,\n ephemeral: true,\n });\n }\n }\n } catch (err) {\n log.error(\n { err, commandName },\n \"[discord-router] Slash command handler failed\",\n );\n try {\n const errMsg = `❌ Command failed: ${err instanceof Error ? err.message : String(err)}`;\n if (interaction.deferred) {\n await interaction.editReply(errMsg);\n } else if (!interaction.replied) {\n await interaction.reply({ content: errMsg, ephemeral: true });\n }\n } catch {\n /* ignore reply errors */\n }\n }\n}\n\nexport async function setupButtonCallbacks(\n interaction: ButtonInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n const { customId } = interaction;\n\n try {\n // Ordered from most specific to least specific\n if (customId.startsWith(\"a:dismiss:\")) {\n // Action dismiss button — remove buttons from the message\n try {\n await interaction.update({ components: [] });\n } catch {\n /* ignore */\n }\n return;\n }\n\n if (customId.startsWith(\"a:\")) {\n // Action confirm button (new session or cancel from assistant suggestions)\n const { getAction, removeAction } = await import(\"../action-detect.js\");\n const actionId = customId.slice(2);\n const action = getAction(actionId);\n if (!action) {\n await interaction.reply({\n content: \"❌ Action expired.\",\n ephemeral: true,\n });\n return;\n }\n removeAction(actionId);\n if (action.type === \"new_session\") {\n await executeNewSession(\n interaction,\n adapter,\n action.agent,\n action.workspace,\n );\n } else if (action.type === \"cancel_session\") {\n await executeCancelSession(interaction, adapter);\n }\n return;\n }\n\n if (customId.startsWith(\"d:\")) {\n await handleDangerousButton(interaction, adapter);\n return;\n }\n\n if (customId.startsWith(\"v:\")) {\n await handleTTSButton(interaction, adapter);\n return;\n }\n\n if (customId.startsWith(\"m:new:\")) {\n await handleNewSessionButton(interaction, adapter);\n return;\n }\n\n if (customId === \"m:cleanup\" || customId.startsWith(\"m:cleanup:\")) {\n await handleCleanupButton(interaction, adapter);\n return;\n }\n\n if (customId === \"m:doctor\" || customId.startsWith(\"m:doctor:\")) {\n await handleDoctorButton(interaction, adapter);\n return;\n }\n\n if (customId.startsWith(\"ag:\")) {\n await handleAgentButton(interaction, adapter);\n return;\n }\n\n if (customId.startsWith(\"na:\")) {\n // New session with specific agent (from install confirmation)\n const agentKey = customId.slice(3);\n try {\n await interaction.deferReply({ ephemeral: true });\n } catch {\n /* ignore */\n }\n await executeNewSession(interaction, adapter, agentKey, undefined);\n return;\n }\n\n if (customId.startsWith(\"s:\")) {\n await handleSettingsButton(interaction, adapter);\n return;\n }\n\n if (customId.startsWith(\"i:\")) {\n await handleIntegrateButton(interaction, adapter);\n return;\n }\n\n // Catch-all m: handler\n if (customId.startsWith(\"m:\")) {\n await handleMenuButton(interaction, adapter);\n return;\n }\n\n log.warn({ customId }, \"[discord-router] Unhandled button interaction\");\n } catch (err) {\n log.error({ err, customId }, \"[discord-router] Button callback failed\");\n try {\n const errMsg = `❌ Action failed: ${err instanceof Error ? err.message : String(err)}`;\n if (!interaction.replied && !interaction.deferred) {\n await interaction.reply({ content: errMsg, ephemeral: true });\n } else {\n await interaction.followUp({ content: errMsg, ephemeral: true });\n }\n } catch {\n /* ignore reply errors */\n }\n }\n}\n\n// Helper: execute new session from button interaction\nasync function executeNewSession(\n interaction: ButtonInteraction,\n adapter: DiscordAdapter,\n agentName?: string,\n workspace?: string,\n): Promise<void> {\n const { executeNewSession: doExecute } = await import(\"./new-session.js\");\n await doExecute(interaction, adapter, agentName, workspace);\n}\n\n// Helper: execute cancel session from button interaction\nasync function executeCancelSession(\n interaction: ButtonInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n const { executeCancelSession: doCancel } = await import(\"./session.js\");\n await doCancel(interaction, adapter);\n}\n","import type { OpenACPCore } from '../../core/core.js'\nimport type { Session } from '../../core/session.js'\nimport { log } from '../../core/log.js'\nimport { PRODUCT_GUIDE } from '../../product-guide.js'\n\nexport interface SpawnAssistantResult {\n session: Session\n /** Resolves when the background system prompt completes (or fails). */\n ready: Promise<void>\n}\n\n/**\n * Spawns an assistant session for the Discord adapter.\n * Creates a session with the default agent, sets the threadId to the given Discord thread/channel,\n * and fires a system prompt in the background.\n */\nexport async function spawnAssistant(\n core: OpenACPCore,\n threadId: string,\n): Promise<SpawnAssistantResult> {\n const config = core.configManager.get()\n\n log.info({ agent: config.defaultAgent, threadId }, '[discord-assistant] Creating assistant session...')\n\n const session = await core.createSession({\n channelId: 'discord',\n agentName: config.defaultAgent,\n workingDirectory: core.configManager.resolveWorkspace(),\n initialName: 'Assistant', // Prevent auto-naming from triggering after system prompt\n })\n session.threadId = threadId\n\n log.info({ sessionId: session.id, threadId }, '[discord-assistant] Assistant agent spawned')\n\n // Build dynamic context for system prompt\n const systemPrompt = buildAssistantSystemPrompt(core)\n\n // Fire system prompt in background — don't block startup\n const ready = session\n .enqueuePrompt(systemPrompt)\n .then(() => {\n log.info({ sessionId: session.id }, '[discord-assistant] System prompt completed')\n })\n .catch((err) => {\n log.warn({ err }, '[discord-assistant] System prompt failed')\n })\n\n return { session, ready }\n}\n\n/**\n * Build a welcome message summarising active sessions and available agents.\n */\nexport function buildWelcomeMessage(core: OpenACPCore): string {\n const allRecords = core.sessionManager.listRecords()\n const activeCount = allRecords.filter(\n (r) => r.status === 'active' || r.status === 'initializing',\n ).length\n const errorCount = allRecords.filter((r) => r.status === 'error').length\n const totalCount = allRecords.length\n\n const installedEntries = core.agentCatalog.getInstalledEntries()\n const agents = Object.keys(installedEntries)\n const config = core.configManager.get()\n const defaultAgent = config.defaultAgent\n\n const agentList = agents\n .map((a) => `${a}${a === defaultAgent ? ' (default)' : ''}`)\n .join(', ')\n\n if (totalCount === 0) {\n return `👋 **OpenACP is ready!**\\n\\nNo sessions yet. Use \\`/new\\` to start, or ask me anything!\\n\\nAgents: ${agentList}`\n }\n\n if (errorCount > 0) {\n return (\n `👋 **OpenACP is ready!**\\n\\n` +\n `📊 ${activeCount} active, ${errorCount} errors / ${totalCount} total\\n` +\n `⚠️ ${errorCount} session${errorCount > 1 ? 's have' : ' has'} errors — ask me to check.\\n\\n` +\n `Agents: ${agentList}`\n )\n }\n\n return (\n `👋 **OpenACP is ready!**\\n\\n` +\n `📊 ${activeCount} active / ${totalCount} total\\n` +\n `Agents: ${agentList}`\n )\n}\n\n/**\n * Build the system prompt for the Discord assistant session.\n * Includes current state, available agents, and action playbook.\n */\nexport function buildAssistantSystemPrompt(core: OpenACPCore): string {\n const config = core.configManager.get()\n\n const allRecords = core.sessionManager.listRecords()\n const activeCount = allRecords.filter(\n (r) => r.status === 'active' || r.status === 'initializing',\n ).length\n\n const statusCounts = new Map<string, number>()\n for (const r of allRecords) {\n statusCounts.set(r.status, (statusCounts.get(r.status) ?? 0) + 1)\n }\n const topicBreakdown =\n Array.from(statusCounts.entries())\n .map(([status, count]) => `${status}: ${count}`)\n .join(', ') || 'none'\n\n const installedEntries = core.agentCatalog.getInstalledEntries()\n const installedAgents = Object.keys(installedEntries)\n const agentNames = installedAgents.length ? installedAgents.join(', ') : Object.keys(config.agents).join(', ')\n\n const availableItems = core.agentCatalog.getAvailable()\n const availableAgentCount = availableItems.filter((i) => !i.installed).length\n\n return `You are the OpenACP Assistant — a helpful guide for managing AI coding sessions on Discord.\n\n## Current State\n- Active sessions: ${activeCount} / ${allRecords.length} total\n- Sessions by status: ${topicBreakdown}\n- Installed agents: ${agentNames}\n- Available in ACP Registry: ${availableAgentCount} more agents (use \\`/agents\\` to browse)\n- Default agent: ${config.defaultAgent}\n- Workspace base directory: ${config.workspace.baseDir}\n- Platform: Discord\n\n## Discord Context\n- Each session gets its own forum thread in the OpenACP sessions channel\n- Users interact with sessions by chatting in those threads\n- Slash commands: /new, /cancel, /status, /sessions, /agents, /install, /menu, /help, /dangerous, /restart, /update, /integrate, /settings, /doctor, /handoff, /clear\n\n## Action Playbook\n\n### Create Session\n- The workspace is the project directory where the agent will work (read, write, execute code). It should be a specific project folder like \\`~/code/my-project\\` or \\`${config.workspace.baseDir}/my-app\\`.\n- Ask which agent to use (if multiple are installed). Installed: ${agentNames}\n- Ask which project directory to use as workspace. Suggest \\`${config.workspace.baseDir}\\` as the base.\n- Create via: \\`openacp api new <agent> <workspace>\\`\n\n### Browse & Install Agents\n- Guide users to \\`/agents\\` command to see all available agents\n- For CLI users: \\`openacp agents install <name>\\`\n- Some agents need login/setup after install — guide users to \\`openacp agents info <name>\\`\n- To run agent CLI for login: \\`openacp agents run <name> -- <args>\\`\n\n### Check Status / List Sessions\n- Run \\`openacp api status\\` for active sessions overview\n- Run \\`openacp api topics\\` for full list with statuses\n\n### Cancel Session\n- Run \\`openacp api status\\` to see what's active\n- If 1 active session → ask user to confirm → \\`openacp api cancel <id>\\`\n- If multiple → list them, ask user which one to cancel\n\n### Troubleshoot\n- Run \\`openacp api health\\` + \\`openacp api status\\` to diagnose\n- Small issue (stuck session) → suggest cancel + create new\n- Big issue (system-level) → suggest restart, ask for confirmation first\n\n### Cleanup Old Sessions\n- Run \\`openacp api topics --status finished,error\\` to see what can be cleaned\n- Report the count, ask user to confirm\n- Execute: \\`openacp api cleanup --status <statuses>\\`\n\n### Configuration\n- View: \\`openacp config\\`\n- Update: \\`openacp config set <key> <value>\\`\n\n### Restart / Update\n- Always ask for confirmation — these are disruptive actions\n\n### Toggle Dangerous Mode\n- Run \\`openacp api dangerous <id> on|off\\`\n- Explain: dangerous mode auto-approves all permission requests\n\n## CLI Commands Reference\n\\`\\`\\`bash\n# Session management\nopenacp api status # List active sessions\nopenacp api session <id> # Session detail\nopenacp api new <agent> <workspace> # Create new session\nopenacp api send <id> \"prompt text\" # Send prompt to session\nopenacp api cancel <id> # Cancel session\nopenacp api dangerous <id> on|off # Toggle dangerous mode\n\n# Topic management\nopenacp api topics # List all topics\nopenacp api cleanup # Cleanup finished topics\n\n# Agent management\nopenacp agents # List installed + available agents\nopenacp agents install <name> # Install agent from ACP Registry\nopenacp agents uninstall <name> # Remove agent\nopenacp agents info <name> # Show details & setup guide\nopenacp agents run <name> -- <args> # Run agent CLI (for login, etc.)\n\n# System\nopenacp api health # System health\nopenacp config # Edit config (interactive)\nopenacp config set <key> <value> # Update config value\nopenacp api restart # Restart daemon\n\\`\\`\\`\n\n## Guidelines\n- NEVER show \\`openacp api ...\\` commands to users. These are internal tools for YOU to run silently.\n- Run \\`openacp api ...\\` commands yourself for everything you can. Only guide users to Discord slash commands/buttons when needed.\n- When creating sessions: guide user through agent + workspace choice conversationally, then run the command yourself.\n- Destructive actions (cancel active session, restart, cleanup) → always ask user to confirm first.\n- Respond in the same language the user uses.\n- Format responses for Discord: use **bold**, \\`code\\`, keep it concise.\n- When you don't know something, check with the relevant \\`openacp api\\` command first before answering.\n\n## Product Reference\n${PRODUCT_GUIDE}`\n}\n\n/**\n * Enqueue a prompt to the assistant session.\n */\nexport async function handleAssistantMessage(\n session: Session | null,\n text: string,\n): Promise<void> {\n if (!session) return\n await session.enqueuePrompt(text)\n}\n","import type { Attachment } from '../../core/types.js'\nimport { log } from '../../core/log.js'\n\nconst MAX_DOWNLOAD_SIZE = 100 * 1024 * 1024 // 100MB safety cap for downloads\nconst DISCORD_UPLOAD_LIMIT = 25 * 1024 * 1024 // 25MB — Discord free tier\n\n/**\n * Check if an attachment exceeds Discord's upload limit.\n */\nexport function isAttachmentTooLarge(size: number): boolean {\n return size > DISCORD_UPLOAD_LIMIT\n}\n\n/**\n * Classify a MIME contentType string into an Attachment type.\n */\nexport function classifyAttachmentType(\n contentType: string | null | undefined,\n): Attachment['type'] {\n if (!contentType) return 'file'\n if (contentType.startsWith('image/')) return 'image'\n if (contentType.startsWith('audio/')) return 'audio'\n return 'file'\n}\n\n/**\n * Build fallback text when message.content is empty but attachments exist.\n * Mirrors Telegram adapter's pattern: [Photo: filename], [Audio: filename], [File: filename]\n */\nexport function buildFallbackText(\n attachments: Array<{ type: Attachment['type']; fileName: string }>,\n): string {\n return attachments\n .map((att) => {\n const label = att.type === 'image' ? 'Photo' : att.type === 'audio' ? 'Audio' : 'File'\n return `[${label}: ${att.fileName}]`\n })\n .join(' ')\n}\n\n/**\n * Download a file from a Discord attachment URL.\n * Returns the buffer or null on failure.\n */\nexport async function downloadDiscordAttachment(\n url: string,\n fileName: string,\n): Promise<Buffer | null> {\n try {\n const response = await fetch(url)\n if (!response.ok) {\n log.warn({ url, status: response.status, fileName }, '[discord-media] Download failed')\n return null\n }\n const buffer = Buffer.from(await response.arrayBuffer())\n if (buffer.length > MAX_DOWNLOAD_SIZE) {\n log.warn({ fileName, size: buffer.length }, '[discord-media] File exceeds download size cap')\n return null\n }\n return buffer\n } catch (err) {\n log.error({ err, url, fileName }, '[discord-media] Download error')\n return null\n }\n}\n"],"mappings":"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","names":["splitMessage","splitMessage","ActionRowBuilder","ButtonBuilder","ButtonStyle","ActionRowBuilder","ButtonBuilder","ButtonStyle","handleNew","handleSessions","handleRestart"]}
|
package/dist/doctor-HZZ5BSHB.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tunnel/providers/install-cloudflared.ts"],"sourcesContent":["import { ensureBinary, type BinarySpec } from '../../core/install-binary.js'\n\nconst CLOUDFLARED_SPEC: BinarySpec = {\n name: 'cloudflared',\n githubBaseUrl: 'https://github.com/cloudflare/cloudflared/releases/latest/download',\n platforms: {\n darwin: {\n x64: 'cloudflared-darwin-amd64.tgz',\n arm64: 'cloudflared-darwin-amd64.tgz',\n },\n win32: {\n x64: 'cloudflared-windows-amd64.exe',\n },\n linux: {\n x64: 'cloudflared-linux-amd64',\n arm64: 'cloudflared-linux-arm64',\n },\n },\n isArchive: (url) => url.endsWith('.tgz'),\n}\n\nexport async function ensureCloudflared(): Promise<string> {\n return ensureBinary(CLOUDFLARED_SPEC)\n}\n"],"mappings":";;;;;;;;AAEA,IAAM,mBAA+B;AAAA,EACnC,MAAM;AAAA,EACN,eAAe;AAAA,EACf,WAAW;AAAA,IACT,QAAQ;AAAA,MACN,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,IACA,OAAO;AAAA,MACL,KAAK;AAAA,IACP;AAAA,IACA,OAAO;AAAA,MACL,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,WAAW,CAAC,QAAQ,IAAI,SAAS,MAAM;AACzC;AAEA,eAAsB,oBAAqC;AACzD,SAAO,aAAa,gBAAgB;AACtC;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/install-jq.ts"],"sourcesContent":["import { ensureBinary, type BinarySpec } from './install-binary.js'\n\nconst JQ_SPEC: BinarySpec = {\n name: 'jq',\n githubBaseUrl: 'https://github.com/jqlang/jq/releases/latest/download',\n platforms: {\n darwin: {\n x64: 'jq-macos-amd64',\n arm64: 'jq-macos-arm64',\n },\n win32: {\n x64: 'jq-windows-amd64.exe',\n },\n linux: {\n x64: 'jq-linux-amd64',\n arm64: 'jq-linux-arm64',\n },\n },\n}\n\nexport async function ensureJq(): Promise<string> {\n return ensureBinary(JQ_SPEC)\n}\n"],"mappings":";;;;;;;;AAEA,IAAM,UAAsB;AAAA,EAC1B,MAAM;AAAA,EACN,eAAe;AAAA,EACf,WAAW;AAAA,IACT,QAAQ;AAAA,MACN,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,IACA,OAAO;AAAA,MACL,KAAK;AAAA,IACP;AAAA,IACA,OAAO;AAAA,MACL,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,eAAsB,WAA4B;AAChD,SAAO,aAAa,OAAO;AAC7B;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/integrate.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, chmodSync, rmdirSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { getAgentCapabilities, commandExists, listAgentsWithIntegration } from \"../core/agent-dependencies.js\";\nimport type { AgentIntegrationSpec } from \"../core/agent-dependencies.js\";\n\nexport interface IntegrationResult {\n success: boolean;\n logs: string[];\n}\n\nexport interface IntegrationItem {\n id: string;\n name: string;\n description: string;\n isInstalled(): boolean;\n install(): Promise<IntegrationResult>;\n uninstall(): Promise<IntegrationResult>;\n}\n\nexport interface AgentIntegration {\n items: IntegrationItem[];\n}\n\nconst HOOK_MARKER = \"openacp-inject-session.sh\";\n\nfunction expandPath(p: string): string {\n return p.replace(/^~/, homedir());\n}\n\n// --- Script generators ---\n\nfunction generateInjectScript(_agentKey: string, spec: AgentIntegrationSpec): string {\n const sidVar = spec.sessionIdVar ?? \"SESSION_ID\";\n const cwdVar = spec.workingDirVar ?? \"WORKING_DIR\";\n\n // Resolve jq: check ~/.openacp/bin first, then PATH\n const jqResolver = `JQ=$(command -v jq 2>/dev/null || echo \"$HOME/.openacp/bin/jq\")`;\n\n if (spec.outputFormat === \"plaintext\") {\n return `#!/bin/bash\n${jqResolver}\nINPUT=$(cat)\nSESSION_ID=$(echo \"$INPUT\" | \"$JQ\" -r '${spec.sessionIdField}')\nCWD=$(echo \"$INPUT\" | \"$JQ\" -r '.cwd')\n\necho \"${sidVar}: $SESSION_ID\"\necho \"${cwdVar}: $CWD\"\n\nexit 0\n`;\n }\n\n // JSON output (Gemini, Cline, Cursor)\n return `#!/bin/bash\n${jqResolver}\nINPUT=$(cat)\nSESSION_ID=$(echo \"$INPUT\" | \"$JQ\" -r '${spec.sessionIdField}')\nCWD=$(echo \"$INPUT\" | \"$JQ\" -r '.cwd')\n\n\"$JQ\" -n --arg sid \"$SESSION_ID\" --arg cwd \"$CWD\" \\\\\n '{\"additionalContext\":\"${sidVar}: \\\\($sid)\\\\n${cwdVar}: \\\\($cwd)\"}'\n\nexit 0\n`;\n}\n\nfunction generateHandoffScript(agentKey: string): string {\n return `#!/bin/bash\nSESSION_ID=$1\nCWD=$2\nCHANNEL=$3\n\nif [ -z \"$SESSION_ID\" ]; then\n echo \"Usage: openacp-handoff.sh <session_id> [cwd] [channel]\"\n exit 1\nfi\n\nopenacp adopt ${agentKey} \"$SESSION_ID\" \\${CWD:+--cwd \"$CWD\"} \\${CHANNEL:+--channel \"$CHANNEL\"}\n`;\n}\n\nfunction generateTunnelCommand(): string {\n return `---\ndescription: Expose local ports to the internet. Use when user wants to share, preview, or access their local dev server remotely. Triggers on phrases like \"expose port\", \"map port\", \"share my app\", \"make it public\", \"open tunnel\", \"public URL\", \"share localhost\", \"preview on phone\", \"access from outside\", \"forward port\", \"ngrok\", \"cloudflare tunnel\", etc.\n---\n\nYou have access to OpenACP tunnel management via CLI. This creates a public URL for any local port (dev servers, APIs, static sites, etc.) using Cloudflare tunnel.\n\n## Commands\n\n\\`\\`\\`bash\n# Create a tunnel — exposes local port to the internet\nopenacp tunnel add <port> --label <name>\n\n# List all active tunnels with their public URLs\nopenacp tunnel list\n\n# Stop a specific tunnel\nopenacp tunnel stop <port>\n\n# Stop all tunnels\nopenacp tunnel stop-all\n\\`\\`\\`\n\n## When to use\n\nUser wants to:\n- **Share their local app** — \"share this on my phone\", \"let my friend see this\", \"preview on mobile\"\n- **Expose a port** — \"expose port 3000\", \"map port 5173\", \"make port 8080 public\"\n- **Get a public URL** — \"give me a public URL\", \"I need an external link\", \"make localhost accessible\"\n- **Open a tunnel** — \"open tunnel\", \"start tunnel\", \"tunnel this\", \"bật tunnel\"\n- **Forward/proxy a port** — \"forward port 3000\", \"proxy my server\"\n- **Deploy preview** — \"deploy preview\", \"share a preview link\"\n- **Access remotely** — \"access from my phone\", \"access from outside\"\n- **Manage tunnels** — \"show tunnels\", \"list tunnels\", \"stop tunnel\", \"close tunnel\", \"kill tunnel\"\n\n## How to respond\n\n1. Run the CLI command\n2. Share the public URL with the user\n3. Mention the URL works on any device (phone, tablet, other computer)\n4. If the user hasn't started a dev server yet, remind them to start one first\n\n## Example flow\n\nUser: \"I want to see this React app on my phone\"\n→ Check if dev server is running (e.g. port 5173 for Vite)\n→ Run: \\`openacp tunnel add 5173 --label react-app\\`\n→ Share the public URL\n`;\n}\n\nfunction generateHandoffCommand(_agentKey: string, spec: AgentIntegrationSpec): string {\n const sidVar = spec.sessionIdVar ?? \"SESSION_ID\";\n const cwdVar = spec.workingDirVar ?? \"WORKING_DIR\";\n const hooksDir = expandPath(spec.hooksDirPath);\n\n return `---\ndescription: Transfer current session to OpenACP (Telegram/Discord)\n---\n\nLook at the context injected at the start of this message to find\n${sidVar} and ${cwdVar}, then run:\n\nbash ${hooksDir}openacp-handoff.sh <${sidVar}> <${cwdVar}> <args if any>\n\nUsage: /openacp:handoff [channel]\n channel: telegram, discord, or omit for default adapter\n\nExamples:\n /openacp:handoff\n /openacp:handoff discord\n /openacp:handoff telegram\n`;\n}\n\n// --- Settings mergers ---\n\nfunction mergeSettingsJson(settingsPath: string, hookEvent: string, hookScriptPath: string): void {\n const fullPath = expandPath(settingsPath);\n let settings: Record<string, unknown> = {};\n\n if (existsSync(fullPath)) {\n const raw = readFileSync(fullPath, \"utf-8\");\n writeFileSync(`${fullPath}.bak`, raw);\n settings = JSON.parse(raw);\n }\n\n const hooks = (settings.hooks ?? {}) as Record<string, unknown[]>;\n settings.hooks = hooks;\n\n const eventHooks = (hooks[hookEvent] ?? []) as Array<{ hooks?: Array<{ type?: string; command?: string }> }>;\n hooks[hookEvent] = eventHooks;\n\n const alreadyInstalled = eventHooks.some((group) =>\n group.hooks?.some((h) => h.command?.includes(HOOK_MARKER)),\n );\n\n if (!alreadyInstalled) {\n eventHooks.push({\n hooks: [{ type: \"command\", command: hookScriptPath }],\n });\n }\n\n mkdirSync(dirname(fullPath), { recursive: true });\n writeFileSync(fullPath, JSON.stringify(settings, null, 2) + \"\\n\");\n}\n\nfunction mergeHooksJson(settingsPath: string, hookEvent: string, hookScriptPath: string): void {\n const fullPath = expandPath(settingsPath);\n let config: Record<string, unknown> = { version: 1 };\n\n if (existsSync(fullPath)) {\n const raw = readFileSync(fullPath, \"utf-8\");\n writeFileSync(`${fullPath}.bak`, raw);\n config = JSON.parse(raw);\n }\n\n const hooks = (config.hooks ?? {}) as Record<string, unknown[]>;\n config.hooks = hooks;\n\n const eventHooks = (hooks[hookEvent] ?? []) as Array<{ command?: string }>;\n hooks[hookEvent] = eventHooks;\n\n const alreadyInstalled = eventHooks.some((h) => h.command?.includes(HOOK_MARKER));\n\n if (!alreadyInstalled) {\n eventHooks.push({ command: hookScriptPath });\n }\n\n mkdirSync(dirname(fullPath), { recursive: true });\n writeFileSync(fullPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\nfunction removeFromSettingsJson(settingsPath: string, hookEvent: string): void {\n const fullPath = expandPath(settingsPath);\n if (!existsSync(fullPath)) return;\n\n const raw = readFileSync(fullPath, \"utf-8\");\n const settings = JSON.parse(raw);\n const hooks = settings.hooks as Record<string, unknown[]> | undefined;\n if (!hooks?.[hookEvent]) return;\n\n hooks[hookEvent] = (hooks[hookEvent] as Array<{ hooks?: Array<{ command?: string }> }>).filter(\n (group) => !group.hooks?.some((h) => h.command?.includes(\"openacp-\")),\n );\n\n if ((hooks[hookEvent] as unknown[]).length === 0) {\n delete hooks[hookEvent];\n }\n\n writeFileSync(fullPath, JSON.stringify(settings, null, 2) + \"\\n\");\n}\n\nfunction removeFromHooksJson(settingsPath: string, hookEvent: string): void {\n const fullPath = expandPath(settingsPath);\n if (!existsSync(fullPath)) return;\n\n const raw = readFileSync(fullPath, \"utf-8\");\n const config = JSON.parse(raw);\n const hooks = config.hooks as Record<string, unknown[]> | undefined;\n if (!hooks?.[hookEvent]) return;\n\n hooks[hookEvent] = (hooks[hookEvent] as Array<{ command?: string }>).filter(\n (h) => !h.command?.includes(\"openacp-\"),\n );\n\n if ((hooks[hookEvent] as unknown[]).length === 0) {\n delete hooks[hookEvent];\n }\n\n writeFileSync(fullPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\n// --- Core install/uninstall ---\n\nexport async function installIntegration(agentKey: string, spec: AgentIntegrationSpec): Promise<IntegrationResult> {\n const logs: string[] = [];\n try {\n // Check jq\n if (!commandExists(\"jq\")) {\n return {\n success: false,\n logs: [\"jq is required for handoff hooks. Install: brew install jq (macOS) or apt install jq (Linux)\"],\n };\n }\n\n const hooksDir = expandPath(spec.hooksDirPath);\n mkdirSync(hooksDir, { recursive: true });\n\n // Inject script\n const injectPath = join(hooksDir, \"openacp-inject-session.sh\");\n writeFileSync(injectPath, generateInjectScript(agentKey, spec));\n chmodSync(injectPath, 0o755);\n logs.push(`Created ${injectPath}`);\n\n // Handoff script\n const handoffPath = join(hooksDir, \"openacp-handoff.sh\");\n writeFileSync(handoffPath, generateHandoffScript(agentKey));\n chmodSync(handoffPath, 0o755);\n logs.push(`Created ${handoffPath}`);\n\n // Slash command / skill\n if (spec.commandsPath && spec.handoffCommandName) {\n if (spec.commandFormat === \"skill\") {\n const skillDir = expandPath(join(spec.commandsPath, spec.handoffCommandName));\n mkdirSync(skillDir, { recursive: true });\n const skillPath = join(skillDir, \"SKILL.md\");\n writeFileSync(skillPath, generateHandoffCommand(agentKey, spec));\n logs.push(`Created ${skillPath}`);\n } else {\n const cmdsDir = expandPath(spec.commandsPath);\n mkdirSync(cmdsDir, { recursive: true });\n const cmdPath = join(cmdsDir, `${spec.handoffCommandName}.md`);\n writeFileSync(cmdPath, generateHandoffCommand(agentKey, spec));\n logs.push(`Created ${cmdPath}`);\n }\n }\n\n // Merge settings\n const injectFullPath = join(hooksDir, \"openacp-inject-session.sh\");\n if (spec.settingsFormat === \"hooks_json\") {\n mergeHooksJson(spec.settingsPath, spec.hookEvent, injectFullPath);\n } else {\n mergeSettingsJson(spec.settingsPath, spec.hookEvent, injectFullPath);\n }\n logs.push(`Updated ${expandPath(spec.settingsPath)}`);\n\n return { success: true, logs };\n } catch (err) {\n logs.push(`Error: ${err instanceof Error ? err.message : String(err)}`);\n return { success: false, logs };\n }\n}\n\nexport async function uninstallIntegration(agentKey: string, spec: AgentIntegrationSpec): Promise<IntegrationResult> {\n const logs: string[] = [];\n try {\n const hooksDir = expandPath(spec.hooksDirPath);\n\n // Remove hook scripts\n for (const filename of [\"openacp-inject-session.sh\", \"openacp-handoff.sh\"]) {\n const filePath = join(hooksDir, filename);\n if (existsSync(filePath)) {\n unlinkSync(filePath);\n logs.push(`Removed ${filePath}`);\n }\n }\n\n // Remove slash command / skill\n if (spec.commandsPath && spec.handoffCommandName) {\n if (spec.commandFormat === \"skill\") {\n const skillDir = expandPath(join(spec.commandsPath, spec.handoffCommandName));\n const skillPath = join(skillDir, \"SKILL.md\");\n if (existsSync(skillPath)) {\n unlinkSync(skillPath);\n try { rmdirSync(skillDir); } catch { /* not empty */ }\n logs.push(`Removed ${skillPath}`);\n }\n } else {\n const cmdPath = expandPath(join(spec.commandsPath, `${spec.handoffCommandName}.md`));\n if (existsSync(cmdPath)) {\n unlinkSync(cmdPath);\n logs.push(`Removed ${cmdPath}`);\n }\n }\n }\n\n // Clean settings\n if (spec.settingsFormat === \"hooks_json\") {\n removeFromHooksJson(spec.settingsPath, spec.hookEvent);\n } else {\n removeFromSettingsJson(spec.settingsPath, spec.hookEvent);\n }\n logs.push(`Updated ${expandPath(spec.settingsPath)}`);\n\n return { success: true, logs };\n } catch (err) {\n logs.push(`Error: ${err instanceof Error ? err.message : String(err)}`);\n return { success: false, logs };\n }\n}\n\n// --- Public API (backward compat with existing cmdIntegrate / Telegram integrate) ---\n\nfunction buildHandoffItem(agentKey: string, spec: AgentIntegrationSpec): IntegrationItem {\n const hooksDir = expandPath(spec.hooksDirPath);\n return {\n id: \"handoff\",\n name: \"Handoff\",\n description: \"Transfer sessions between terminal and messaging platforms\",\n isInstalled(): boolean {\n return (\n existsSync(join(hooksDir, \"openacp-inject-session.sh\")) &&\n existsSync(join(hooksDir, \"openacp-handoff.sh\"))\n );\n },\n install: () => installIntegration(agentKey, spec),\n uninstall: () => uninstallIntegration(agentKey, spec),\n };\n}\n\nfunction getSkillBasePath(spec: AgentIntegrationSpec): string {\n // Skills go into the agent's skills directory (sibling to commands)\n // Claude: ~/.claude/skills/, Cursor: ~/.cursor/skills/\n const base = spec.commandsPath!;\n // If commandsPath is commands/, use skills/ instead\n const skillsBase = base.replace(/\\/commands\\/?$/, \"/skills/\");\n return expandPath(skillsBase);\n}\n\nfunction buildTunnelItem(spec: AgentIntegrationSpec): IntegrationItem | null {\n if (!spec.commandsPath) return null;\n\n function getTunnelPath(): string {\n return join(getSkillBasePath(spec), \"openacp-tunnel\", \"SKILL.md\");\n }\n\n return {\n id: \"tunnel\",\n name: \"Tunnel\",\n description: \"Expose local ports to the internet via OpenACP tunnel\",\n isInstalled(): boolean {\n return existsSync(getTunnelPath());\n },\n async install(): Promise<IntegrationResult> {\n const logs: string[] = [];\n try {\n const skillPath = getTunnelPath();\n mkdirSync(dirname(skillPath), { recursive: true });\n writeFileSync(skillPath, generateTunnelCommand());\n logs.push(`Created ${skillPath}`);\n return { success: true, logs };\n } catch (err) {\n logs.push(`Error: ${err instanceof Error ? err.message : String(err)}`);\n return { success: false, logs };\n }\n },\n async uninstall(): Promise<IntegrationResult> {\n const logs: string[] = [];\n try {\n const skillPath = getTunnelPath();\n if (existsSync(skillPath)) {\n unlinkSync(skillPath);\n try { rmdirSync(dirname(skillPath)); } catch { /* not empty */ }\n logs.push(`Removed ${skillPath}`);\n }\n return { success: true, logs };\n } catch (err) {\n logs.push(`Error: ${err instanceof Error ? err.message : String(err)}`);\n return { success: false, logs };\n }\n },\n };\n}\n\nexport function getIntegration(agentName: string): AgentIntegration | undefined {\n const caps = getAgentCapabilities(agentName);\n if (!caps.integration) return undefined;\n const items: IntegrationItem[] = [buildHandoffItem(agentName, caps.integration)];\n const tunnelItem = buildTunnelItem(caps.integration);\n if (tunnelItem) items.push(tunnelItem);\n return { items };\n}\n\nexport function listIntegrations(): string[] {\n return listAgentsWithIntegration();\n}\n"],"mappings":"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","names":[]}
|