@ashx-j/lunr 0.2.9 → 0.2.11

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.
Files changed (145) hide show
  1. package/dist/builtin-extensions/ashxj-spinners.d.ts +1 -4
  2. package/dist/builtin-extensions/ashxj-spinners.d.ts.map +1 -1
  3. package/dist/builtin-extensions/ashxj-spinners.js +3 -29
  4. package/dist/builtin-extensions/ashxj-spinners.js.map +1 -1
  5. package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
  6. package/dist/builtin-extensions/ashxj-tui.js +5 -4
  7. package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
  8. package/dist/builtin-extensions/lunr-cron.d.ts.map +1 -1
  9. package/dist/builtin-extensions/lunr-cron.js +6 -1
  10. package/dist/builtin-extensions/lunr-cron.js.map +1 -1
  11. package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.d.ts.map +1 -1
  12. package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.js +1 -1
  13. package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.js.map +1 -1
  14. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  15. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +3 -4
  16. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  17. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
  18. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +2 -2
  19. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
  20. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +2 -2
  21. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
  22. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +4 -4
  23. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
  24. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  25. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +5 -1
  26. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  27. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  28. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +8 -7
  29. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  30. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +2 -2
  31. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
  32. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js +2 -2
  33. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
  34. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +4 -3
  35. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
  36. package/dist/builtin-extensions/pi-subagents/src/shared/types.js +2 -2
  37. package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
  38. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +4 -6
  39. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  40. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +26 -18
  41. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  42. package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
  43. package/dist/builtin-extensions/pi-web-access/index.js +3 -1
  44. package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
  45. package/dist/catalog/models.json +1 -1
  46. package/dist/catalog/providers/nvidia.json +1 -1
  47. package/dist/catalog/providers/opencode-go.json +1 -1
  48. package/dist/catalog/providers/opencode.json +1 -1
  49. package/dist/catalog/providers/openrouter.json +1 -1
  50. package/dist/catalog/providers/qwen-cloud-cn.json +1 -1
  51. package/dist/catalog/providers/vercel-ai-gateway.json +1 -1
  52. package/dist/catalog/providers/xai.json +1 -1
  53. package/dist/catalog/publication.json +3 -3
  54. package/dist/core/agent-session.d.ts +2 -0
  55. package/dist/core/agent-session.d.ts.map +1 -1
  56. package/dist/core/agent-session.js +11 -3
  57. package/dist/core/agent-session.js.map +1 -1
  58. package/dist/core/customize.d.ts +1 -0
  59. package/dist/core/customize.d.ts.map +1 -1
  60. package/dist/core/customize.js +3 -0
  61. package/dist/core/customize.js.map +1 -1
  62. package/dist/core/models-store.d.ts.map +1 -1
  63. package/dist/core/models-store.js +13 -1
  64. package/dist/core/models-store.js.map +1 -1
  65. package/dist/core/permissions.d.ts +9 -0
  66. package/dist/core/permissions.d.ts.map +1 -1
  67. package/dist/core/permissions.js +60 -20
  68. package/dist/core/permissions.js.map +1 -1
  69. package/dist/core/plan-mode.d.ts.map +1 -1
  70. package/dist/core/plan-mode.js +49 -7
  71. package/dist/core/plan-mode.js.map +1 -1
  72. package/dist/core/settings-manager.d.ts +3 -0
  73. package/dist/core/settings-manager.d.ts.map +1 -1
  74. package/dist/core/settings-manager.js +8 -0
  75. package/dist/core/settings-manager.js.map +1 -1
  76. package/dist/core/swarm.d.ts +17 -4
  77. package/dist/core/swarm.d.ts.map +1 -1
  78. package/dist/core/swarm.js +36 -4
  79. package/dist/core/swarm.js.map +1 -1
  80. package/dist/core/tools/bash.d.ts.map +1 -1
  81. package/dist/core/tools/bash.js +2 -1
  82. package/dist/core/tools/bash.js.map +1 -1
  83. package/dist/core/tools/edit.d.ts.map +1 -1
  84. package/dist/core/tools/edit.js +9 -6
  85. package/dist/core/tools/edit.js.map +1 -1
  86. package/dist/core/tools/find.d.ts.map +1 -1
  87. package/dist/core/tools/find.js +2 -1
  88. package/dist/core/tools/find.js.map +1 -1
  89. package/dist/core/tools/grep.d.ts.map +1 -1
  90. package/dist/core/tools/grep.js +2 -1
  91. package/dist/core/tools/grep.js.map +1 -1
  92. package/dist/core/tools/ls.d.ts.map +1 -1
  93. package/dist/core/tools/ls.js +2 -1
  94. package/dist/core/tools/ls.js.map +1 -1
  95. package/dist/core/tools/read.d.ts.map +1 -1
  96. package/dist/core/tools/read.js +2 -1
  97. package/dist/core/tools/read.js.map +1 -1
  98. package/dist/core/tools/render-utils.d.ts +13 -3
  99. package/dist/core/tools/render-utils.d.ts.map +1 -1
  100. package/dist/core/tools/render-utils.js +10 -2
  101. package/dist/core/tools/render-utils.js.map +1 -1
  102. package/dist/core/tools/write.d.ts.map +1 -1
  103. package/dist/core/tools/write.js +5 -3
  104. package/dist/core/tools/write.js.map +1 -1
  105. package/dist/gateway/agent-bridge.d.ts +2 -1
  106. package/dist/gateway/agent-bridge.d.ts.map +1 -1
  107. package/dist/gateway/agent-bridge.js +11 -1
  108. package/dist/gateway/agent-bridge.js.map +1 -1
  109. package/dist/gateway/commands.d.ts.map +1 -1
  110. package/dist/gateway/commands.js +2 -1
  111. package/dist/gateway/commands.js.map +1 -1
  112. package/dist/gateway/cron.d.ts +4 -1
  113. package/dist/gateway/cron.d.ts.map +1 -1
  114. package/dist/gateway/cron.js +17 -8
  115. package/dist/gateway/cron.js.map +1 -1
  116. package/dist/gateway/router.d.ts +1 -1
  117. package/dist/gateway/router.d.ts.map +1 -1
  118. package/dist/gateway/router.js.map +1 -1
  119. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  120. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  121. package/dist/modes/interactive/components/settings-selector.js +11 -1
  122. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  123. package/dist/modes/interactive/components/thinking-tail.d.ts +1 -0
  124. package/dist/modes/interactive/components/thinking-tail.d.ts.map +1 -1
  125. package/dist/modes/interactive/components/thinking-tail.js +2 -6
  126. package/dist/modes/interactive/components/thinking-tail.js.map +1 -1
  127. package/dist/modes/interactive/components/tool-execution.d.ts +6 -0
  128. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  129. package/dist/modes/interactive/components/tool-execution.js +75 -16
  130. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  131. package/dist/modes/interactive/interactive-mode.d.ts +15 -1
  132. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  133. package/dist/modes/interactive/interactive-mode.js +139 -34
  134. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  135. package/docs/terminal-setup.md +2 -2
  136. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  137. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  138. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  139. package/examples/extensions/gondolin/package-lock.json +2 -2
  140. package/examples/extensions/gondolin/package.json +1 -1
  141. package/examples/extensions/sandbox/package-lock.json +2 -2
  142. package/examples/extensions/sandbox/package.json +1 -1
  143. package/examples/extensions/with-deps/package-lock.json +2 -2
  144. package/examples/extensions/with-deps/package.json +1 -1
  145. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"agent-bridge.js","sourceRoot":"","sources":["../../src/gateway/agent-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway agent bridge.\n *\n * Owns the sessionKey → AgentSession mapping: an LRU cache (cap 20) over\n * live sessions, backed by store.ts so conversations survive daemon restarts\n * (a stored sessionFile is reopened via SessionManager.open, which\n * createAgentSession restores history from; a missing file starts fresh with\n * a one-line warning).\n *\n * New sessions replicate main.ts's headless wiring: SettingsManager +\n * DefaultResourceLoader with the builtin extension factories + the three\n * lunr bridges, then bindExtensions({ mode: \"print\" }) like print-mode.\n *\n * Busy guard: one turn per session at a time. Messages arriving mid-turn are\n * queued (cap 5, oldest dropped with a notice) and drained FIFO as a single\n * concatenated follow-up turn once the running turn ends; runTurn returns\n * the QUEUED sentinel for those, and the follow-up turn's result is\n * delivered via callbacks.onFollowUpResult.\n *\n * Streaming: subscribes to session events and converts cumulative\n * message_update snapshots into deltas for callbacks.onDelta.\n *\n * isGatewayTurn()/runDepth: incremented around every prompt() so Phase 4\n * (cron↔gateway delivery) and extensions can detect they run inside a\n * gateway turn and refuse self-scheduling. Every prompt() also runs inside\n * runWithOrigin(source, ...) (core/cron/origin-context) so tools executing\n * during the turn — e.g. the `cron` tool's create action — can read the chat\n * the message came from and stamp it as the job's delivery origin.\n */\n\nimport { existsSync } from \"node:fs\";\nimport type { AgentMessage, ThinkingLevel } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage, ImageContent } from \"@ashx-j/lunr-ai\";\nimport type { Model } from \"@ashx-j/lunr-ai/compat\";\nimport type { AgentSession, AgentSessionEvent, SessionStats } from \"../core/agent-session.ts\";\nimport type { CompactionResult } from \"../core/compaction/index.ts\";\nimport { runWithOrigin } from \"../core/cron/origin-context.ts\";\nimport type { ContextUsage, ToolDefinition } from \"../core/extensions/types.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport { createPermissionContext, deletePermissionContext } from \"../core/permissions.ts\";\nimport type { ReadonlySessionManager, SessionManager, SessionMessageEntry } from \"../core/session-manager.ts\";\nimport { processImage } from \"../utils/image-process.ts\";\nimport { getSession, putSession, removeSession, touchSession } from \"./store.ts\";\nimport type { InboundAttachment, MessageEvent, SessionSource } from \"./types.ts\";\n\nfunction isUserMessageEntry(entry: { type: string }): entry is SessionMessageEntry {\n\treturn entry.type === \"message\";\n}\n\n/** runTurn resolves with this when the message was queued behind a running turn. */\nexport const QUEUED = \"__lunr_gateway_queued__\";\n\nconst CACHE_CAP = 20;\nconst QUEUE_CAP = 5;\n\nlet runDepth = 0;\n\n/** True while a gateway turn's prompt() is in flight. */\nexport function isGatewayTurn(): boolean {\n\treturn runDepth > 0;\n}\n\nexport function gatewayRunDepth(): number {\n\treturn runDepth;\n}\n\nexport interface TurnCallbacks {\n\t/** Assistant text delta during streaming (converted from cumulative snapshots). */\n\tonDelta?: (delta: string) => void;\n\t/** Final text of a drained-queue follow-up turn (deliver it like a normal reply). */\n\tonFollowUpResult?: (text: string) => void;\n\t/** Error from a drained-queue follow-up turn. */\n\tonError?: (message: string) => void;\n}\n\nexport interface BridgeSessionStatus {\n\tbusy: boolean;\n\tqueueDepth: number;\n\tcreatedAt?: string;\n}\n\n/**\n * Structural session shape the bridge needs — satisfied by AgentSession,\n * fakeable in tests. Expanded to support gateway slash commands.\n */\nexport interface BridgeSession {\n\tprompt(text: string, options?: { source?: \"extension\"; images?: ImageContent[] }): Promise<void>;\n\tabort(): Promise<void> | void;\n\tsubscribe(listener: (event: AgentSessionEvent) => void): () => void;\n\treadonly state: { messages: AgentMessage[] };\n\tdispose?(): void;\n\treadonly isStreaming: boolean;\n\treadonly isCompacting?: boolean;\n\treadonly model?: Model<any>;\n\treadonly modelRuntime: ModelRuntime;\n\treadonly thinkingLevel: ThinkingLevel;\n\treadonly messages: AgentMessage[];\n\treadonly systemPrompt: string;\n\treadonly sessionManager?: ReadonlySessionManager;\n\n\tgetActiveToolNames(): string[];\n\tgetToolDefinition(name: string): ToolDefinition | undefined;\n\tgetAvailableThinkingLevels(): ThinkingLevel[];\n\tsupportsThinking(): boolean;\n\tsetThinkingLevel(level: ThinkingLevel): void;\n\tsetModel(model: Model<any>): Promise<void>;\n\tcompact(customInstructions?: string): Promise<CompactionResult>;\n\tnavigateTree(\n\t\ttargetId: string,\n\t\toptions?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string },\n\t): Promise<{ editorText?: string; cancelled: boolean; aborted?: boolean }>;\n\tgetContextUsage(): ContextUsage | undefined;\n\tgetSessionStats(): SessionStats;\n\tsetSessionName(name: string): void;\n}\n\nexport type SessionFactory = (key: string, reopen: { sessionFile: string } | undefined) => Promise<BridgeSession>;\n\ninterface CacheEntry {\n\tsession: BridgeSession;\n\tbusy: boolean;\n\tqueue: MessageEvent[];\n\tdropped: number;\n\tunsubscribe?: () => void;\n}\n\n/**\n * Default factory: headless session with the builtin extensions, mirroring\n * main.ts (minus TUI) + print-mode's bindExtensions.\n *\n * The heavy wiring is imported LAZILY: a static import of\n * builtin-extensions/index.ts here would pull the whole extension + TUI\n * graph into every consumer of this module (router tests, `gateway status`)\n * and trip the known ashxj-tui ↔ package-barrel cycle (see AGENTS.md\n * \"Barrel-import guard\"). Loading it on first session creation keeps module\n * import light and matches how main.ts loads it in the real CLI.\n */\nasync function defaultSessionFactory(key: string, reopen: { sessionFile: string } | undefined): Promise<AgentSession> {\n\tconst [\n\t\t{ loadAllBuiltinExtensions },\n\t\t{ getAgentDir },\n\t\t{ registerCustomizeBridge },\n\t\t{ reconcileBehaviorPreset, registerBehaviorPresetBridge },\n\t\t{ registerMemoryCapBridge },\n\t\t{ getModelTiersBridge, registerModelTierBridge },\n\t\t{ createAgentSessionFromServices, createAgentSessionServices },\n\t\t{ SessionManager },\n\t\t{ SettingsManager },\n\t] = await Promise.all([\n\t\timport(\"../builtin-extensions/index.ts\"),\n\t\timport(\"../config.ts\"),\n\t\timport(\"../core/customize.ts\"),\n\t\timport(\"../core/behavior-preset.ts\"),\n\t\timport(\"../core/memory-cap.ts\"),\n\t\timport(\"../core/model-tiers.ts\"),\n\t\timport(\"../core/agent-session-services.ts\"),\n\t\timport(\"../core/session-manager.ts\"),\n\t\timport(\"../core/settings-manager.ts\"),\n\t]);\n\tconst cwd = process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });\n\t// Same bridges main.ts registers before extensions load.\n\tregisterModelTierBridge(settingsManager);\n\tregisterMemoryCapBridge(settingsManager);\n\tregisterBehaviorPresetBridge(settingsManager);\n\treconcileBehaviorPreset(settingsManager, agentDir);\n\tregisterCustomizeBridge(settingsManager);\n\n\tlet sessionManager: SessionManager;\n\tif (reopen && existsSync(reopen.sessionFile)) {\n\t\tsessionManager = SessionManager.open(reopen.sessionFile);\n\t} else {\n\t\tif (reopen) {\n\t\t\tconsole.warn(`[gateway] stored session file missing for ${key}; starting a fresh session`);\n\t\t}\n\t\tsessionManager = SessionManager.create(cwd);\n\t}\n\n\t// Services-first (mirrors main.ts): extension-registered providers (e.g.\n\t// ollama-cloud) must land in the shared ModelRuntime BEFORE session\n\t// creation — otherwise findInitialModel can't resolve the user's default\n\t// model and silently falls back to an arbitrary catalog provider (this\n\t// exact bug sent gateway turns to openrouter with no key → 401).\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager,\n\t\tresourceLoaderOptions: { extensionFactories: await loadAllBuiltinExtensions() },\n\t});\n\tconst { session } = await createAgentSessionFromServices({ services, sessionManager });\n\tgetModelTiersBridge()?.setParentThinkingProvider(() => session.thinkingLevel);\n\tawait session.bindExtensions({\n\t\tmode: \"print\",\n\t\tonError: (err) => console.error(`[gateway] extension error (${err.extensionPath}): ${err.error}`),\n\t});\n\treturn session;\n}\n\n/** Final assistant text, print-mode style; throws on error/aborted stop. */\nfunction extractFinalText(session: BridgeSession): string {\n\tconst messages = session.state.messages;\n\tconst last = messages[messages.length - 1];\n\tif (last?.role !== \"assistant\") return \"\";\n\tconst assistant = last as AssistantMessage;\n\tif (assistant.stopReason === \"error\" || assistant.stopReason === \"aborted\") {\n\t\tthrow new Error(assistant.errorMessage || `Request ${assistant.stopReason}`);\n\t}\n\tlet text = \"\";\n\tfor (const content of assistant.content) {\n\t\tif (content.type === \"text\") text += content.text;\n\t}\n\treturn text;\n}\n\nexport class AgentBridge {\n\tprivate cap: number;\n\tprivate readonly sessionFactory: SessionFactory;\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\tprivate readonly redoStack = new Map<string, string[]>();\n\tprivate readonly creating = new Map<string, Promise<CacheEntry>>();\n\n\tconstructor(options: { cacheCap?: number; sessionFactory?: SessionFactory } = {}) {\n\t\tthis.cap = options.cacheCap ?? CACHE_CAP;\n\t\tthis.sessionFactory = options.sessionFactory ?? defaultSessionFactory;\n\t}\n\n\t/**\n\t * Run one conversational turn. Resolves with the final assistant text, or\n\t * the QUEUED sentinel when the session was busy (router must not reply).\n\t */\n\tasync runTurn(key: string, event: MessageEvent, callbacks: TurnCallbacks = {}): Promise<string> {\n\t\tconst entry = await this.getOrCreate(key);\n\t\tif (entry.busy) {\n\t\t\tif (entry.queue.length >= QUEUE_CAP) {\n\t\t\t\tentry.queue.shift();\n\t\t\t\tentry.dropped++;\n\t\t\t}\n\t\t\tentry.queue.push(event);\n\t\t\treturn QUEUED;\n\t\t}\n\t\tentry.busy = true;\n\t\tlet result: string;\n\t\ttry {\n\t\t\tresult = await this.runPrompt(entry, event.text, callbacks, event.source, event.attachments);\n\t\t} catch (err) {\n\t\t\tentry.busy = false;\n\t\t\tthrow err;\n\t\t}\n\t\ttouchSession(key);\n\t\t// Drain queued messages as one follow-up turn, fire-and-forget. The busy\n\t\t// flag stays set until the queue is empty so turn ordering is strict;\n\t\t// the follow-up result arrives via callbacks.onFollowUpResult.\n\t\tvoid this.drainQueue(entry, callbacks).finally(() => {\n\t\t\tentry.busy = false;\n\t\t});\n\t\treturn result;\n\t}\n\n\tasync abort(key: string): Promise<void> {\n\t\tawait this.cache.get(key)?.session.abort();\n\t}\n\n\t/** Return the live session for a key, or null when none exists yet. */\n\tasync getSession(key: string): Promise<BridgeSession | null> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) return cached.session;\n\t\tif (!getSession(key)) return null;\n\t\treturn (await this.getOrCreate(key)).session;\n\t}\n\n\t/** Switch the chat to a different persisted session file. */\n\tasync switchSession(key: string, sessionFile: string): Promise<void> {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry?.busy) {\n\t\t\tthrow new Error(\"Session is busy — /stop first or wait\");\n\t\t}\n\t\tif (entry) {\n\t\t\tconst oldSessionId = entry.session.sessionManager?.getSessionId();\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t\tif (oldSessionId) deletePermissionContext(oldSessionId);\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\n\t\tconst session = await this.sessionFactory(key, { sessionFile });\n\t\tconst newFile = session.sessionManager?.getSessionFile();\n\t\tconst newSessionId = session.sessionManager?.getSessionId();\n\t\tif (newFile) {\n\t\t\tputSession(key, { sessionId: newSessionId ?? key, sessionFile: newFile });\n\t\t}\n\t\tif (newSessionId) {\n\t\t\tcreatePermissionContext(newSessionId);\n\t\t}\n\t\tconst newEntry: CacheEntry = { session, busy: false, queue: [], dropped: 0 };\n\t\tthis.cache.set(key, newEntry);\n\t\tthis._enforceCacheCap(key);\n\t\tnewEntry.unsubscribe = this._subscribeToSession(key, newEntry);\n\t}\n\n\t/** Undo the last user turn and push the previous leaf onto the redo stack. */\n\tasync undo(key: string): Promise<{ userText: string }> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst branch = session.sessionManager?.getBranch() ?? [];\n\t\tlet lastUserIndex = -1;\n\t\tfor (let i = branch.length - 1; i >= 0; i--) {\n\t\t\tconst entry = branch[i];\n\t\t\tif (isUserMessageEntry(entry) && entry.message.role === \"user\") {\n\t\t\t\tlastUserIndex = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (lastUserIndex === -1) throw new Error(\"Nothing to undo\");\n\t\tconst lastUser = branch[lastUserIndex];\n\t\tif (!isUserMessageEntry(lastUser)) throw new Error(\"Nothing to undo\");\n\n\t\tconst leafId = session.sessionManager?.getLeafId() ?? null;\n\t\tlet targetId: string;\n\t\tif (lastUser.id === leafId) {\n\t\t\tif (!lastUser.parentId) throw new Error(\"Nothing to undo\");\n\t\t\ttargetId = lastUser.parentId;\n\t\t} else {\n\t\t\ttargetId = lastUser.id;\n\t\t}\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) throw new Error(\"Navigation cancelled\");\n\n\t\tif (leafId) {\n\t\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\t\tstack.push(leafId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t}\n\n\t\treturn { userText: extractUserText(lastUser.message) };\n\t}\n\n\t/** Redo to the leaf saved by the most recent undo. */\n\tasync redo(key: string): Promise<void> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\tconst targetId = stack.pop();\n\t\tif (!targetId) throw new Error(\"Nothing to redo\");\n\t\tthis.redoStack.set(key, stack);\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) {\n\t\t\tstack.push(targetId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t\tthrow new Error(\"Navigation cancelled\");\n\t\t}\n\t}\n\n\t/** Test/debug hook: how many redo targets are saved for a key. */\n\tgetRedoStackLength(key: string): number {\n\t\treturn this.redoStack.get(key)?.length ?? 0;\n\t}\n\n\t/** Drop the cached session (disposing it) and forget the store entry (/new, /reset). */\n\treset(key: string): void {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry) {\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\t}\n\n\tgetStatus(key: string): BridgeSessionStatus {\n\t\tconst entry = this.cache.get(key);\n\t\treturn {\n\t\t\tbusy: entry?.busy ?? false,\n\t\t\tqueueDepth: entry?.queue.length ?? 0,\n\t\t\tcreatedAt: getSession(key)?.createdAt,\n\t\t};\n\t}\n\n\t/** One prompt → final text. Increments runDepth; streams deltas when asked. */\n\tprivate async runPrompt(\n\t\tentry: CacheEntry,\n\t\ttext: string,\n\t\tcallbacks: TurnCallbacks,\n\t\tsource: SessionSource,\n\t\tattachments?: InboundAttachment[],\n\t): Promise<string> {\n\t\tconst { session } = entry;\n\t\tlet unsubscribe: (() => void) | undefined;\n\t\tif (callbacks.onDelta) {\n\t\t\tconst onDelta = callbacks.onDelta;\n\t\t\tlet streamedLen = 0;\n\t\t\tunsubscribe = session.subscribe((event: AgentSessionEvent) => {\n\t\t\t\tif (event.type !== \"message_update\") return;\n\t\t\t\tconst assistantEvent = event.assistantMessageEvent;\n\t\t\t\tif (assistantEvent.type !== \"text_delta\") return;\n\t\t\t\tlet full = \"\";\n\t\t\t\tfor (const content of assistantEvent.partial.content) {\n\t\t\t\t\tif (content.type === \"text\") full += content.text;\n\t\t\t\t}\n\t\t\t\tif (full.length < streamedLen) streamedLen = 0; // new assistant message (tool-call boundary)\n\t\t\t\tif (full.length > streamedLen) {\n\t\t\t\t\tonDelta(full.slice(streamedLen));\n\t\t\t\t\tstreamedLen = full.length;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\trunDepth++;\n\t\ttry {\n\t\t\t// lunr: convert inbound image attachments to ImageContent for the model.\n\t\t\t// processImage normalizes + resizes under the inline 4.5MB base64 limit;\n\t\t\t// failures become bracketed text notes so the model explains the gap.\n\t\t\tconst images: ImageContent[] = [];\n\t\t\tconst notes: string[] = [];\n\t\t\tfor (const attachment of attachments ?? []) {\n\t\t\t\tconst bytes = Buffer.from(attachment.data, \"base64\");\n\t\t\t\tconst processed = await processImage(new Uint8Array(bytes), attachment.mimeType);\n\t\t\t\tif (processed.ok) {\n\t\t\t\t\timages.push({ type: \"image\", data: processed.data, mimeType: processed.mimeType });\n\t\t\t\t\tfor (const hint of processed.hints) notes.push(hint);\n\t\t\t\t} else {\n\t\t\t\t\tnotes.push(processed.message);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst promptText = notes.length > 0 ? `${text}\\n\\n${notes.join(\" \")}` : text;\n\t\t\t// AsyncLocalStorage origin: tools executing during this turn (e.g. the\n\t\t\t// `cron` tool) can read the chat the message came from. Propagates\n\t\t\t// through the awaited prompt() call tree.\n\t\t\tawait runWithOrigin(\n\t\t\t\t{\n\t\t\t\t\tplatform: source.platform,\n\t\t\t\t\tchatId: source.chatId,\n\t\t\t\t\tthreadId: source.threadId,\n\t\t\t\t\tchatType: source.chatType,\n\t\t\t\t},\n\t\t\t\t() => session.prompt(promptText, { source: \"extension\", ...(images.length ? { images } : {}) }),\n\t\t\t);\n\t\t} finally {\n\t\t\trunDepth--;\n\t\t\tunsubscribe?.();\n\t\t}\n\t\treturn extractFinalText(session);\n\t}\n\n\t/** Drain the queue FIFO: all queued messages become one concatenated turn. */\n\tprivate async drainQueue(entry: CacheEntry, callbacks: TurnCallbacks): Promise<void> {\n\t\twhile (entry.queue.length > 0) {\n\t\t\tconst items = entry.queue.splice(0);\n\t\t\tconst parts = items.map((item) => item.text);\n\t\t\tconst attachments = items.flatMap((item) => item.attachments ?? []);\n\t\t\tif (entry.dropped > 0) {\n\t\t\t\tparts.unshift(`[gateway: ${entry.dropped} earlier message(s) were dropped — the queue was full]`);\n\t\t\t\tentry.dropped = 0;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// Follow-up turns are final-only: no streaming callbacks. The origin\n\t\t\t\t// is the chat of the latest queued message.\n\t\t\t\tconst text = await this.runPrompt(\n\t\t\t\t\tentry,\n\t\t\t\t\tparts.join(\"\\n\\n\"),\n\t\t\t\t\t{},\n\t\t\t\t\titems[items.length - 1].source,\n\t\t\t\t\tattachments,\n\t\t\t\t);\n\t\t\t\tcallbacks.onFollowUpResult?.(text);\n\t\t\t} catch (err) {\n\t\t\t\tcallbacks.onError?.(err instanceof Error ? err.message : String(err));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async getOrCreate(key: string): Promise<CacheEntry> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) {\n\t\t\t// LRU refresh\n\t\t\tthis.cache.delete(key);\n\t\t\tthis.cache.set(key, cached);\n\t\t\treturn cached;\n\t\t}\n\t\t// Coalesce concurrent creation for the same key.\n\t\tlet pending = this.creating.get(key);\n\t\tif (!pending) {\n\t\t\tpending = this.createEntry(key);\n\t\t\tthis.creating.set(key, pending);\n\t\t\ttry {\n\t\t\t\tawait pending;\n\t\t\t} finally {\n\t\t\t\tthis.creating.delete(key);\n\t\t\t}\n\t\t}\n\t\treturn pending;\n\t}\n\n\tprivate async createEntry(key: string): Promise<CacheEntry> {\n\t\tconst stored = getSession(key);\n\t\tconst session = await this.sessionFactory(key, stored ? { sessionFile: stored.sessionFile } : undefined);\n\t\tconst sessionFile = session.sessionManager?.getSessionFile();\n\t\tconst sessionId = session.sessionManager?.getSessionId();\n\t\tif (sessionFile) {\n\t\t\tputSession(key, { sessionId: sessionId ?? key, sessionFile });\n\t\t}\n\t\tif (sessionId) {\n\t\t\tcreatePermissionContext(sessionId);\n\t\t}\n\t\tconst entry: CacheEntry = { session, busy: false, queue: [], dropped: 0, unsubscribe: undefined };\n\t\tthis.cache.set(key, entry);\n\t\tthis._enforceCacheCap(key);\n\t\tentry.unsubscribe = this._subscribeToSession(key, entry);\n\t\treturn entry;\n\t}\n\n\tprivate _enforceCacheCap(protectedKey?: string): void {\n\t\twhile (this.cache.size > this.cap) {\n\t\t\tlet oldestKey: string | undefined;\n\t\t\tlet oldest: CacheEntry | undefined;\n\t\t\tfor (const [key, entry] of this.cache) {\n\t\t\t\tif (key === protectedKey) continue;\n\t\t\t\tif (entry.busy) continue;\n\t\t\t\toldestKey = key;\n\t\t\t\toldest = entry;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!oldest || !oldestKey) {\n\t\t\t\t// Every cached session is busy (or is the protected new entry);\n\t\t\t\t// grow the cap temporarily rather than killing an in-flight turn.\n\t\t\t\tthis.cap = this.cache.size;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toldest.unsubscribe?.();\n\t\t\tthis.cache.delete(oldestKey);\n\t\t\toldest.session.dispose?.();\n\t\t\tconst evictedSessionId = oldest.session.sessionManager?.getSessionId();\n\t\t\tif (evictedSessionId) deletePermissionContext(evictedSessionId);\n\t\t}\n\t}\n\n\tprivate _subscribeToSession(key: string, entry: CacheEntry): () => void {\n\t\treturn entry.session.subscribe((event) => this._handleSessionEvent(key, event));\n\t}\n\n\tprivate _handleSessionEvent(key: string, event: AgentSessionEvent): void {\n\t\tif (event.type === \"message_start\" && event.message?.role === \"user\") {\n\t\t\tthis.redoStack.delete(key);\n\t\t}\n\t}\n}\n\nfunction extractUserText(message: AgentMessage): string {\n\tlet text = \"\";\n\tif (\"content\" in message && Array.isArray(message.content)) {\n\t\tfor (const block of message.content) {\n\t\t\tif (\n\t\t\t\tblock &&\n\t\t\t\ttypeof block === \"object\" &&\n\t\t\t\t\"type\" in block &&\n\t\t\t\tblock.type === \"text\" &&\n\t\t\t\t\"text\" in block &&\n\t\t\t\ttypeof block.text === \"string\"\n\t\t\t) {\n\t\t\t\ttext += block.text;\n\t\t\t}\n\t\t}\n\t}\n\treturn text;\n}\n"]}
1
+ {"version":3,"file":"agent-bridge.js","sourceRoot":"","sources":["../../src/gateway/agent-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAMrC,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAE1F,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGjF,SAAS,kBAAkB,CAAC,KAAuB,EAAgC;IAClF,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC;AAAA,CAChC;AAED,oFAAoF;AACpF,MAAM,CAAC,MAAM,MAAM,GAAG,yBAAyB,CAAC;AAEhD,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB,IAAI,QAAQ,GAAG,CAAC,CAAC;AAEjB,yDAAyD;AACzD,MAAM,UAAU,aAAa,GAAY;IACxC,OAAO,QAAQ,GAAG,CAAC,CAAC;AAAA,CACpB;AAED,MAAM,UAAU,eAAe,GAAW;IACzC,OAAO,QAAQ,CAAC;AAAA,CAChB;AA8DD;;;;;;;;;;GAUG;AACH,KAAK,UAAU,qBAAqB,CAAC,GAAW,EAAE,MAA2C,EAAyB;IACrH,MAAM,CACL,EAAE,wBAAwB,EAAE,EAC5B,EAAE,WAAW,EAAE,EACf,EAAE,uBAAuB,EAAE,EAC3B,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,EACzD,EAAE,uBAAuB,EAAE,EAC3B,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,EAChD,EAAE,8BAA8B,EAAE,0BAA0B,EAAE,EAC9D,EAAE,cAAc,EAAE,EAClB,EAAE,eAAe,EAAE,EACnB,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACrB,MAAM,CAAC,gCAAgC,CAAC;QACxC,MAAM,CAAC,cAAc,CAAC;QACtB,MAAM,CAAC,sBAAsB,CAAC;QAC9B,MAAM,CAAC,4BAA4B,CAAC;QACpC,MAAM,CAAC,uBAAuB,CAAC;QAC/B,MAAM,CAAC,wBAAwB,CAAC;QAChC,MAAM,CAAC,mCAAmC,CAAC;QAC3C,MAAM,CAAC,4BAA4B,CAAC;QACpC,MAAM,CAAC,6BAA6B,CAAC;KACrC,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;IACzF,yDAAyD;IACzD,uBAAuB,CAAC,eAAe,CAAC,CAAC;IACzC,uBAAuB,CAAC,eAAe,CAAC,CAAC;IACzC,4BAA4B,CAAC,eAAe,CAAC,CAAC;IAC9C,uBAAuB,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;IACnD,uBAAuB,CAAC,eAAe,CAAC,CAAC;IAEzC,IAAI,cAA8B,CAAC;IACnC,IAAI,MAAM,IAAI,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;QAC9C,cAAc,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC1D,CAAC;SAAM,CAAC;QACP,IAAI,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,6CAA6C,GAAG,4BAA4B,CAAC,CAAC;QAC5F,CAAC;QACD,cAAc,GAAG,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7C,CAAC;IAED,yEAAyE;IACzE,oEAAoE;IACpE,2EAAyE;IACzE,uEAAuE;IACvE,mEAAiE;IACjE,MAAM,QAAQ,GAAG,MAAM,0BAA0B,CAAC;QACjD,GAAG;QACH,QAAQ;QACR,eAAe;QACf,qBAAqB,EAAE,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,EAAE,EAAE;KAC/E,CAAC,CAAC;IACH,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,8BAA8B,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC;IACvF,mBAAmB,EAAE,EAAE,yBAAyB,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC9E,MAAM,OAAO,CAAC,cAAc,CAAC;QAC5B,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,8BAA8B,GAAG,CAAC,aAAa,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;KACjG,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AAAA,CACf;AAED,4EAA4E;AAC5E,SAAS,gBAAgB,CAAC,OAAsB,EAAU;IACzD,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC;IACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,IAAI,IAAI,EAAE,IAAI,KAAK,WAAW;QAAE,OAAO,EAAE,CAAC;IAC1C,MAAM,SAAS,GAAG,IAAwB,CAAC;IAC3C,IAAI,SAAS,CAAC,UAAU,KAAK,OAAO,IAAI,SAAS,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QAC5E,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,YAAY,IAAI,WAAW,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,KAAK,MAAM,OAAO,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM;YAAE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IACnD,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ;AAED,MAAM,OAAO,WAAW;IACf,GAAG,CAAS;IACH,cAAc,CAAiB;IAC/B,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IACtC,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;IACxC,QAAQ,GAAG,IAAI,GAAG,EAA+B,CAAC;IAEnE,YAAY,OAAO,GAA2D,EAAE,EAAE;QACjF,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,QAAQ,IAAI,SAAS,CAAC;QACzC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,qBAAqB,CAAC;IAAA,CACtE;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,KAAmB,EAAE,SAAS,GAAkB,EAAE,EAAmB;QAC/F,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAChB,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;gBACrC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBACpB,KAAK,CAAC,OAAO,EAAE,CAAC;YACjB,CAAC;YACD,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACxB,OAAO,MAAM,CAAC;QACf,CAAC;QACD,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;QAClB,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACJ,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;QAC9F,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;YACnB,MAAM,GAAG,CAAC;QACX,CAAC;QACD,YAAY,CAAC,GAAG,CAAC,CAAC;QAClB,yEAAyE;QACzE,sEAAsE;QACtE,+DAA+D;QAC/D,KAAK,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACpD,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;QAAA,CACnB,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAAA,CACd;IAED,KAAK,CAAC,KAAK,CAAC,GAAW,EAAiB;QACvC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;IAAA,CAC3C;IAED,uEAAuE;IACvE,KAAK,CAAC,UAAU,CAAC,GAAW,EAAiC;QAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC,OAAO,CAAC;QAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAClC,OAAO,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;IAAA,CAC7C;IAED,6DAA6D;IAC7D,KAAK,CAAC,aAAa,CAAC,GAAW,EAAE,WAAmB,EAAiB;QACpE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,KAAK,EAAE,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,yCAAuC,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,KAAK,EAAE,CAAC;YACX,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,YAAY,EAAE,CAAC;YAClE,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACtB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YAC1B,IAAI,YAAY;gBAAE,uBAAuB,CAAC,YAAY,CAAC,CAAC;QACzD,CAAC;QACD,aAAa,CAAC,GAAG,CAAC,CAAC;QACnB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAE3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,EAAE,cAAc,EAAE,CAAC;QACzD,MAAM,YAAY,GAAG,OAAO,CAAC,cAAc,EAAE,YAAY,EAAE,CAAC;QAC5D,IAAI,OAAO,EAAE,CAAC;YACb,UAAU,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,YAAY,IAAI,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3E,CAAC;QACD,IAAI,YAAY,EAAE,CAAC;YAClB,uBAAuB,CAAC,YAAY,CAAC,CAAC;QACvC,CAAC;QACD,MAAM,QAAQ,GAAe,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAC7E,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC9B,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC3B,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAAA,CAC/D;IAED,8EAA8E;IAC9E,KAAK,CAAC,IAAI,CAAC,GAAW,EAAiC;QACtD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QACpF,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACzD,IAAI,aAAa,GAAG,CAAC,CAAC,CAAC;QACvB,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,kBAAkB,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAChE,aAAa,GAAG,CAAC,CAAC;gBAClB,MAAM;YACP,CAAC;QACF,CAAC;QACD,IAAI,aAAa,KAAK,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC7D,MAAM,QAAQ,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;QACvC,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAEtE,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC;QAC3D,IAAI,QAAgB,CAAC;QACrB,IAAI,QAAQ,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;YAC5B,IAAI,CAAC,QAAQ,CAAC,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAC3D,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC;QAC9B,CAAC;aAAM,CAAC;YACP,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAC;QACxB,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACxD,IAAI,MAAM,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAE9D,IAAI,MAAM,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAC5C,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAChC,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;IAAA,CACvD;IAED,sDAAsD;IACtD,KAAK,CAAC,IAAI,CAAC,GAAW,EAAiB;QACtC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QACpF,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAC5C,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAClD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAE/B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACxD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACtB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACzC,CAAC;IAAA,CACD;IAED,kEAAkE;IAClE,kBAAkB,CAAC,GAAW,EAAU;QACvC,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;IAAA,CAC5C;IAED,wFAAwF;IACxF,KAAK,CAAC,KAAK,CAAC,GAAW,EAAiB;QACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,KAAK,EAAE,CAAC;YACX,2EAA2E;YAC3E,8EAA8E;YAC9E,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;YACvB,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;YAClB,IAAI,CAAC;gBACJ,MAAM,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC7B,CAAC;YAAC,MAAM,CAAC;gBACR,oDAAoD;YACrD,CAAC;YACD,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACtB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3B,CAAC;QACD,aAAa,CAAC,GAAG,CAAC,CAAC;QACnB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAAA,CAC3B;IAED,SAAS,CAAC,GAAW,EAAuB;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO;YACN,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,KAAK;YAC1B,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC;YACpC,SAAS,EAAE,UAAU,CAAC,GAAG,CAAC,EAAE,SAAS;SACrC,CAAC;IAAA,CACF;IAED,iFAA+E;IACvE,KAAK,CAAC,SAAS,CACtB,KAAiB,EACjB,IAAY,EACZ,SAAwB,EACxB,MAAqB,EACrB,WAAiC,EACf;QAClB,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;QAC1B,IAAI,WAAqC,CAAC;QAC1C,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;YAClC,IAAI,WAAW,GAAG,CAAC,CAAC;YACpB,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,KAAwB,EAAE,EAAE,CAAC;gBAC7D,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;oBAAE,OAAO;gBAC5C,MAAM,cAAc,GAAG,KAAK,CAAC,qBAAqB,CAAC;gBACnD,IAAI,cAAc,CAAC,IAAI,KAAK,YAAY;oBAAE,OAAO;gBACjD,IAAI,IAAI,GAAG,EAAE,CAAC;gBACd,KAAK,MAAM,OAAO,IAAI,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;oBACtD,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM;wBAAE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;gBACnD,CAAC;gBACD,IAAI,IAAI,CAAC,MAAM,GAAG,WAAW;oBAAE,WAAW,GAAG,CAAC,CAAC,CAAC,6CAA6C;gBAC7F,IAAI,IAAI,CAAC,MAAM,GAAG,WAAW,EAAE,CAAC;oBAC/B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;oBACjC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC;gBAC3B,CAAC;YAAA,CACD,CAAC,CAAC;QACJ,CAAC;QACD,QAAQ,EAAE,CAAC;QACX,IAAI,CAAC;YACJ,yEAAyE;YACzE,yEAAyE;YACzE,sEAAsE;YACtE,MAAM,MAAM,GAAmB,EAAE,CAAC;YAClC,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,KAAK,MAAM,UAAU,IAAI,WAAW,IAAI,EAAE,EAAE,CAAC;gBAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;gBACrD,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;gBACjF,IAAI,SAAS,CAAC,EAAE,EAAE,CAAC;oBAClB,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;oBACnF,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK;wBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACtD,CAAC;qBAAM,CAAC;oBACP,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;gBAC/B,CAAC;YACF,CAAC;YACD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7E,uEAAuE;YACvE,mEAAmE;YACnE,0CAA0C;YAC1C,MAAM,aAAa,CAClB;gBACC,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;aACzB,EACD,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAC/F,CAAC;QACH,CAAC;gBAAS,CAAC;YACV,QAAQ,EAAE,CAAC;YACX,WAAW,EAAE,EAAE,CAAC;QACjB,CAAC;QACD,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAAA,CACjC;IAED,8EAA8E;IACtE,KAAK,CAAC,UAAU,CAAC,KAAiB,EAAE,SAAwB,EAAiB;QACpF,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACpC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7C,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;YACpE,IAAI,KAAK,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;gBACvB,KAAK,CAAC,OAAO,CAAC,aAAa,KAAK,CAAC,OAAO,0DAAwD,CAAC,CAAC;gBAClG,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;YACnB,CAAC;YACD,IAAI,CAAC;gBACJ,qEAAqE;gBACrE,4CAA4C;gBAC5C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAChC,KAAK,EACL,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAClB,EAAE,EACF,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,EAC9B,WAAW,CACX,CAAC;gBACF,SAAS,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC,CAAC;YACpC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;gBACtE,OAAO;YACR,CAAC;QACF,CAAC;IAAA,CACD;IAEO,KAAK,CAAC,WAAW,CAAC,GAAW,EAAuB;QAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,MAAM,EAAE,CAAC;YACZ,cAAc;YACd,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC5B,OAAO,MAAM,CAAC;QACf,CAAC;QACD,iDAAiD;QACjD,IAAI,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAChC,IAAI,CAAC;gBACJ,MAAM,OAAO,CAAC;YACf,CAAC;oBAAS,CAAC;gBACV,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC3B,CAAC;QACF,CAAC;QACD,OAAO,OAAO,CAAC;IAAA,CACf;IAEO,KAAK,CAAC,WAAW,CAAC,GAAW,EAAuB;QAC3D,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACzG,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,EAAE,cAAc,EAAE,CAAC;QAC7D,MAAM,SAAS,GAAG,OAAO,CAAC,cAAc,EAAE,YAAY,EAAE,CAAC;QACzD,IAAI,WAAW,EAAE,CAAC;YACjB,UAAU,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,SAAS,IAAI,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,SAAS,EAAE,CAAC;YACf,uBAAuB,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,KAAK,GAAe,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;QAClG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC3B,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC3B,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACzD,OAAO,KAAK,CAAC;IAAA,CACb;IAEO,gBAAgB,CAAC,YAAqB,EAAQ;QACrD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACnC,IAAI,SAA6B,CAAC;YAClC,IAAI,MAA8B,CAAC;YACnC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACvC,IAAI,GAAG,KAAK,YAAY;oBAAE,SAAS;gBACnC,IAAI,KAAK,CAAC,IAAI;oBAAE,SAAS;gBACzB,SAAS,GAAG,GAAG,CAAC;gBAChB,MAAM,GAAG,KAAK,CAAC;gBACf,MAAM;YACP,CAAC;YACD,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC3B,gEAAgE;gBAChE,kEAAkE;gBAClE,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;gBAC3B,MAAM;YACP,CAAC;YACD,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC7B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YAC3B,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,YAAY,EAAE,CAAC;YACvE,IAAI,gBAAgB;gBAAE,uBAAuB,CAAC,gBAAgB,CAAC,CAAC;QACjE,CAAC;IAAA,CACD;IAEO,mBAAmB,CAAC,GAAW,EAAE,KAAiB,EAAc;QACvE,OAAO,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;IAAA,CAChF;IAEO,mBAAmB,CAAC,GAAW,EAAE,KAAwB,EAAQ;QACxE,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,IAAI,KAAK,CAAC,OAAO,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;YACtE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;IAAA,CACD;CACD;AAED,SAAS,eAAe,CAAC,OAAqB,EAAU;IACvD,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,SAAS,IAAI,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5D,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACrC,IACC,KAAK;gBACL,OAAO,KAAK,KAAK,QAAQ;gBACzB,MAAM,IAAI,KAAK;gBACf,KAAK,CAAC,IAAI,KAAK,MAAM;gBACrB,MAAM,IAAI,KAAK;gBACf,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAC7B,CAAC;gBACF,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC;YACpB,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ","sourcesContent":["/**\n * lunR: gateway agent bridge.\n *\n * Owns the sessionKey → AgentSession mapping: an LRU cache (cap 20) over\n * live sessions, backed by store.ts so conversations survive daemon restarts\n * (a stored sessionFile is reopened via SessionManager.open, which\n * createAgentSession restores history from; a missing file starts fresh with\n * a one-line warning).\n *\n * New sessions replicate main.ts's headless wiring: SettingsManager +\n * DefaultResourceLoader with the builtin extension factories + the three\n * lunr bridges, then bindExtensions({ mode: \"print\" }) like print-mode.\n *\n * Busy guard: one turn per session at a time. Messages arriving mid-turn are\n * queued (cap 5, oldest dropped with a notice) and drained FIFO as a single\n * concatenated follow-up turn once the running turn ends; runTurn returns\n * the QUEUED sentinel for those, and the follow-up turn's result is\n * delivered via callbacks.onFollowUpResult.\n *\n * Streaming: subscribes to session events and converts cumulative\n * message_update snapshots into deltas for callbacks.onDelta.\n *\n * isGatewayTurn()/runDepth: incremented around every prompt() so Phase 4\n * (cron↔gateway delivery) and extensions can detect they run inside a\n * gateway turn and refuse self-scheduling. Every prompt() also runs inside\n * runWithOrigin(source, ...) (core/cron/origin-context) so tools executing\n * during the turn — e.g. the `cron` tool's create action — can read the chat\n * the message came from and stamp it as the job's delivery origin.\n */\n\nimport { existsSync } from \"node:fs\";\nimport type { AgentMessage, ThinkingLevel } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage, ImageContent } from \"@ashx-j/lunr-ai\";\nimport type { Model } from \"@ashx-j/lunr-ai/compat\";\nimport type { AgentSession, AgentSessionEvent, SessionStats } from \"../core/agent-session.ts\";\nimport type { CompactionResult } from \"../core/compaction/index.ts\";\nimport { runWithOrigin } from \"../core/cron/origin-context.ts\";\nimport type { ContextUsage, ToolDefinition } from \"../core/extensions/types.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport { createPermissionContext, deletePermissionContext } from \"../core/permissions.ts\";\nimport type { ReadonlySessionManager, SessionManager, SessionMessageEntry } from \"../core/session-manager.ts\";\nimport { processImage } from \"../utils/image-process.ts\";\nimport { getSession, putSession, removeSession, touchSession } from \"./store.ts\";\nimport type { InboundAttachment, MessageEvent, SessionSource } from \"./types.ts\";\n\nfunction isUserMessageEntry(entry: { type: string }): entry is SessionMessageEntry {\n\treturn entry.type === \"message\";\n}\n\n/** runTurn resolves with this when the message was queued behind a running turn. */\nexport const QUEUED = \"__lunr_gateway_queued__\";\n\nconst CACHE_CAP = 20;\nconst QUEUE_CAP = 5;\n\nlet runDepth = 0;\n\n/** True while a gateway turn's prompt() is in flight. */\nexport function isGatewayTurn(): boolean {\n\treturn runDepth > 0;\n}\n\nexport function gatewayRunDepth(): number {\n\treturn runDepth;\n}\n\nexport interface TurnCallbacks {\n\t/** Assistant text delta during streaming (converted from cumulative snapshots). */\n\tonDelta?: (delta: string) => void;\n\t/** Final text of a drained-queue follow-up turn (deliver it like a normal reply). */\n\tonFollowUpResult?: (text: string) => void;\n\t/** Error from a drained-queue follow-up turn. */\n\tonError?: (message: string) => void;\n}\n\nexport interface BridgeSessionStatus {\n\tbusy: boolean;\n\tqueueDepth: number;\n\tcreatedAt?: string;\n}\n\n/**\n * Structural session shape the bridge needs — satisfied by AgentSession,\n * fakeable in tests. Expanded to support gateway slash commands.\n */\nexport interface BridgeSession {\n\tprompt(text: string, options?: { source?: \"extension\"; images?: ImageContent[] }): Promise<void>;\n\tabort(): Promise<void> | void;\n\tsubscribe(listener: (event: AgentSessionEvent) => void): () => void;\n\treadonly state: { messages: AgentMessage[] };\n\tdispose?(): void;\n\treadonly isStreaming: boolean;\n\treadonly isCompacting?: boolean;\n\treadonly model?: Model<any>;\n\treadonly modelRuntime: ModelRuntime;\n\treadonly thinkingLevel: ThinkingLevel;\n\treadonly messages: AgentMessage[];\n\treadonly systemPrompt: string;\n\treadonly sessionManager?: ReadonlySessionManager;\n\n\tgetActiveToolNames(): string[];\n\tgetToolDefinition(name: string): ToolDefinition | undefined;\n\tgetAvailableThinkingLevels(): ThinkingLevel[];\n\tsupportsThinking(): boolean;\n\tsetThinkingLevel(level: ThinkingLevel): void;\n\tsetModel(model: Model<any>): Promise<void>;\n\tcompact(customInstructions?: string): Promise<CompactionResult>;\n\tnavigateTree(\n\t\ttargetId: string,\n\t\toptions?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string },\n\t): Promise<{ editorText?: string; editorImages?: ImageContent[]; cancelled: boolean; aborted?: boolean }>;\n\tgetContextUsage(): ContextUsage | undefined;\n\tgetSessionStats(): SessionStats;\n\tsetSessionName(name: string): void;\n}\n\nexport type SessionFactory = (key: string, reopen: { sessionFile: string } | undefined) => Promise<BridgeSession>;\n\ninterface CacheEntry {\n\tsession: BridgeSession;\n\tbusy: boolean;\n\tqueue: MessageEvent[];\n\tdropped: number;\n\tunsubscribe?: () => void;\n}\n\n/**\n * Default factory: headless session with the builtin extensions, mirroring\n * main.ts (minus TUI) + print-mode's bindExtensions.\n *\n * The heavy wiring is imported LAZILY: a static import of\n * builtin-extensions/index.ts here would pull the whole extension + TUI\n * graph into every consumer of this module (router tests, `gateway status`)\n * and trip the known ashxj-tui ↔ package-barrel cycle (see AGENTS.md\n * \"Barrel-import guard\"). Loading it on first session creation keeps module\n * import light and matches how main.ts loads it in the real CLI.\n */\nasync function defaultSessionFactory(key: string, reopen: { sessionFile: string } | undefined): Promise<AgentSession> {\n\tconst [\n\t\t{ loadAllBuiltinExtensions },\n\t\t{ getAgentDir },\n\t\t{ registerCustomizeBridge },\n\t\t{ reconcileBehaviorPreset, registerBehaviorPresetBridge },\n\t\t{ registerMemoryCapBridge },\n\t\t{ getModelTiersBridge, registerModelTierBridge },\n\t\t{ createAgentSessionFromServices, createAgentSessionServices },\n\t\t{ SessionManager },\n\t\t{ SettingsManager },\n\t] = await Promise.all([\n\t\timport(\"../builtin-extensions/index.ts\"),\n\t\timport(\"../config.ts\"),\n\t\timport(\"../core/customize.ts\"),\n\t\timport(\"../core/behavior-preset.ts\"),\n\t\timport(\"../core/memory-cap.ts\"),\n\t\timport(\"../core/model-tiers.ts\"),\n\t\timport(\"../core/agent-session-services.ts\"),\n\t\timport(\"../core/session-manager.ts\"),\n\t\timport(\"../core/settings-manager.ts\"),\n\t]);\n\tconst cwd = process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });\n\t// Same bridges main.ts registers before extensions load.\n\tregisterModelTierBridge(settingsManager);\n\tregisterMemoryCapBridge(settingsManager);\n\tregisterBehaviorPresetBridge(settingsManager);\n\treconcileBehaviorPreset(settingsManager, agentDir);\n\tregisterCustomizeBridge(settingsManager);\n\n\tlet sessionManager: SessionManager;\n\tif (reopen && existsSync(reopen.sessionFile)) {\n\t\tsessionManager = SessionManager.open(reopen.sessionFile);\n\t} else {\n\t\tif (reopen) {\n\t\t\tconsole.warn(`[gateway] stored session file missing for ${key}; starting a fresh session`);\n\t\t}\n\t\tsessionManager = SessionManager.create(cwd);\n\t}\n\n\t// Services-first (mirrors main.ts): extension-registered providers (e.g.\n\t// ollama-cloud) must land in the shared ModelRuntime BEFORE session\n\t// creation — otherwise findInitialModel can't resolve the user's default\n\t// model and silently falls back to an arbitrary catalog provider (this\n\t// exact bug sent gateway turns to openrouter with no key → 401).\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager,\n\t\tresourceLoaderOptions: { extensionFactories: await loadAllBuiltinExtensions() },\n\t});\n\tconst { session } = await createAgentSessionFromServices({ services, sessionManager });\n\tgetModelTiersBridge()?.setParentThinkingProvider(() => session.thinkingLevel);\n\tawait session.bindExtensions({\n\t\tmode: \"print\",\n\t\tonError: (err) => console.error(`[gateway] extension error (${err.extensionPath}): ${err.error}`),\n\t});\n\treturn session;\n}\n\n/** Final assistant text, print-mode style; throws on error/aborted stop. */\nfunction extractFinalText(session: BridgeSession): string {\n\tconst messages = session.state.messages;\n\tconst last = messages[messages.length - 1];\n\tif (last?.role !== \"assistant\") return \"\";\n\tconst assistant = last as AssistantMessage;\n\tif (assistant.stopReason === \"error\" || assistant.stopReason === \"aborted\") {\n\t\tthrow new Error(assistant.errorMessage || `Request ${assistant.stopReason}`);\n\t}\n\tlet text = \"\";\n\tfor (const content of assistant.content) {\n\t\tif (content.type === \"text\") text += content.text;\n\t}\n\treturn text;\n}\n\nexport class AgentBridge {\n\tprivate cap: number;\n\tprivate readonly sessionFactory: SessionFactory;\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\tprivate readonly redoStack = new Map<string, string[]>();\n\tprivate readonly creating = new Map<string, Promise<CacheEntry>>();\n\n\tconstructor(options: { cacheCap?: number; sessionFactory?: SessionFactory } = {}) {\n\t\tthis.cap = options.cacheCap ?? CACHE_CAP;\n\t\tthis.sessionFactory = options.sessionFactory ?? defaultSessionFactory;\n\t}\n\n\t/**\n\t * Run one conversational turn. Resolves with the final assistant text, or\n\t * the QUEUED sentinel when the session was busy (router must not reply).\n\t */\n\tasync runTurn(key: string, event: MessageEvent, callbacks: TurnCallbacks = {}): Promise<string> {\n\t\tconst entry = await this.getOrCreate(key);\n\t\tif (entry.busy) {\n\t\t\tif (entry.queue.length >= QUEUE_CAP) {\n\t\t\t\tentry.queue.shift();\n\t\t\t\tentry.dropped++;\n\t\t\t}\n\t\t\tentry.queue.push(event);\n\t\t\treturn QUEUED;\n\t\t}\n\t\tentry.busy = true;\n\t\tlet result: string;\n\t\ttry {\n\t\t\tresult = await this.runPrompt(entry, event.text, callbacks, event.source, event.attachments);\n\t\t} catch (err) {\n\t\t\tentry.busy = false;\n\t\t\tthrow err;\n\t\t}\n\t\ttouchSession(key);\n\t\t// Drain queued messages as one follow-up turn, fire-and-forget. The busy\n\t\t// flag stays set until the queue is empty so turn ordering is strict;\n\t\t// the follow-up result arrives via callbacks.onFollowUpResult.\n\t\tvoid this.drainQueue(entry, callbacks).finally(() => {\n\t\t\tentry.busy = false;\n\t\t});\n\t\treturn result;\n\t}\n\n\tasync abort(key: string): Promise<void> {\n\t\tawait this.cache.get(key)?.session.abort();\n\t}\n\n\t/** Return the live session for a key, or null when none exists yet. */\n\tasync getSession(key: string): Promise<BridgeSession | null> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) return cached.session;\n\t\tif (!getSession(key)) return null;\n\t\treturn (await this.getOrCreate(key)).session;\n\t}\n\n\t/** Switch the chat to a different persisted session file. */\n\tasync switchSession(key: string, sessionFile: string): Promise<void> {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry?.busy) {\n\t\t\tthrow new Error(\"Session is busy — /stop first or wait\");\n\t\t}\n\t\tif (entry) {\n\t\t\tconst oldSessionId = entry.session.sessionManager?.getSessionId();\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t\tif (oldSessionId) deletePermissionContext(oldSessionId);\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\n\t\tconst session = await this.sessionFactory(key, { sessionFile });\n\t\tconst newFile = session.sessionManager?.getSessionFile();\n\t\tconst newSessionId = session.sessionManager?.getSessionId();\n\t\tif (newFile) {\n\t\t\tputSession(key, { sessionId: newSessionId ?? key, sessionFile: newFile });\n\t\t}\n\t\tif (newSessionId) {\n\t\t\tcreatePermissionContext(newSessionId);\n\t\t}\n\t\tconst newEntry: CacheEntry = { session, busy: false, queue: [], dropped: 0 };\n\t\tthis.cache.set(key, newEntry);\n\t\tthis._enforceCacheCap(key);\n\t\tnewEntry.unsubscribe = this._subscribeToSession(key, newEntry);\n\t}\n\n\t/** Undo the last user turn and push the previous leaf onto the redo stack. */\n\tasync undo(key: string): Promise<{ userText: string }> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst branch = session.sessionManager?.getBranch() ?? [];\n\t\tlet lastUserIndex = -1;\n\t\tfor (let i = branch.length - 1; i >= 0; i--) {\n\t\t\tconst entry = branch[i];\n\t\t\tif (isUserMessageEntry(entry) && entry.message.role === \"user\") {\n\t\t\t\tlastUserIndex = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (lastUserIndex === -1) throw new Error(\"Nothing to undo\");\n\t\tconst lastUser = branch[lastUserIndex];\n\t\tif (!isUserMessageEntry(lastUser)) throw new Error(\"Nothing to undo\");\n\n\t\tconst leafId = session.sessionManager?.getLeafId() ?? null;\n\t\tlet targetId: string;\n\t\tif (lastUser.id === leafId) {\n\t\t\tif (!lastUser.parentId) throw new Error(\"Nothing to undo\");\n\t\t\ttargetId = lastUser.parentId;\n\t\t} else {\n\t\t\ttargetId = lastUser.id;\n\t\t}\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) throw new Error(\"Navigation cancelled\");\n\n\t\tif (leafId) {\n\t\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\t\tstack.push(leafId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t}\n\n\t\treturn { userText: extractUserText(lastUser.message) };\n\t}\n\n\t/** Redo to the leaf saved by the most recent undo. */\n\tasync redo(key: string): Promise<void> {\n\t\tconst session = await this.getSession(key);\n\t\tif (!session) throw new Error(\"No session\");\n\t\tif (session.isStreaming) throw new Error(\"Wait for the current response to finish\");\n\t\tconst stack = this.redoStack.get(key) ?? [];\n\t\tconst targetId = stack.pop();\n\t\tif (!targetId) throw new Error(\"Nothing to redo\");\n\t\tthis.redoStack.set(key, stack);\n\n\t\tconst result = await session.navigateTree(targetId, {});\n\t\tif (result.cancelled) {\n\t\t\tstack.push(targetId);\n\t\t\tthis.redoStack.set(key, stack);\n\t\t\tthrow new Error(\"Navigation cancelled\");\n\t\t}\n\t}\n\n\t/** Test/debug hook: how many redo targets are saved for a key. */\n\tgetRedoStackLength(key: string): number {\n\t\treturn this.redoStack.get(key)?.length ?? 0;\n\t}\n\n\t/** Drop the cached session (disposing it) and forget the store entry (/new, /reset). */\n\tasync reset(key: string): Promise<void> {\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry) {\n\t\t\t// lunr: /new while busy must stop the live turn and drop queued follow-ups\n\t\t\t// before dispose, otherwise drainQueue can still fire on the torn-down entry.\n\t\t\tentry.queue.length = 0;\n\t\t\tentry.dropped = 0;\n\t\t\ttry {\n\t\t\t\tawait entry.session.abort();\n\t\t\t} catch {\n\t\t\t\t// best-effort; dispose still tears the session down\n\t\t\t}\n\t\t\tentry.unsubscribe?.();\n\t\t\tthis.cache.delete(key);\n\t\t\tentry.session.dispose?.();\n\t\t}\n\t\tremoveSession(key);\n\t\tthis.redoStack.delete(key);\n\t}\n\n\tgetStatus(key: string): BridgeSessionStatus {\n\t\tconst entry = this.cache.get(key);\n\t\treturn {\n\t\t\tbusy: entry?.busy ?? false,\n\t\t\tqueueDepth: entry?.queue.length ?? 0,\n\t\t\tcreatedAt: getSession(key)?.createdAt,\n\t\t};\n\t}\n\n\t/** One prompt → final text. Increments runDepth; streams deltas when asked. */\n\tprivate async runPrompt(\n\t\tentry: CacheEntry,\n\t\ttext: string,\n\t\tcallbacks: TurnCallbacks,\n\t\tsource: SessionSource,\n\t\tattachments?: InboundAttachment[],\n\t): Promise<string> {\n\t\tconst { session } = entry;\n\t\tlet unsubscribe: (() => void) | undefined;\n\t\tif (callbacks.onDelta) {\n\t\t\tconst onDelta = callbacks.onDelta;\n\t\t\tlet streamedLen = 0;\n\t\t\tunsubscribe = session.subscribe((event: AgentSessionEvent) => {\n\t\t\t\tif (event.type !== \"message_update\") return;\n\t\t\t\tconst assistantEvent = event.assistantMessageEvent;\n\t\t\t\tif (assistantEvent.type !== \"text_delta\") return;\n\t\t\t\tlet full = \"\";\n\t\t\t\tfor (const content of assistantEvent.partial.content) {\n\t\t\t\t\tif (content.type === \"text\") full += content.text;\n\t\t\t\t}\n\t\t\t\tif (full.length < streamedLen) streamedLen = 0; // new assistant message (tool-call boundary)\n\t\t\t\tif (full.length > streamedLen) {\n\t\t\t\t\tonDelta(full.slice(streamedLen));\n\t\t\t\t\tstreamedLen = full.length;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\trunDepth++;\n\t\ttry {\n\t\t\t// lunr: convert inbound image attachments to ImageContent for the model.\n\t\t\t// processImage normalizes + resizes under the inline 4.5MB base64 limit;\n\t\t\t// failures become bracketed text notes so the model explains the gap.\n\t\t\tconst images: ImageContent[] = [];\n\t\t\tconst notes: string[] = [];\n\t\t\tfor (const attachment of attachments ?? []) {\n\t\t\t\tconst bytes = Buffer.from(attachment.data, \"base64\");\n\t\t\t\tconst processed = await processImage(new Uint8Array(bytes), attachment.mimeType);\n\t\t\t\tif (processed.ok) {\n\t\t\t\t\timages.push({ type: \"image\", data: processed.data, mimeType: processed.mimeType });\n\t\t\t\t\tfor (const hint of processed.hints) notes.push(hint);\n\t\t\t\t} else {\n\t\t\t\t\tnotes.push(processed.message);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst promptText = notes.length > 0 ? `${text}\\n\\n${notes.join(\" \")}` : text;\n\t\t\t// AsyncLocalStorage origin: tools executing during this turn (e.g. the\n\t\t\t// `cron` tool) can read the chat the message came from. Propagates\n\t\t\t// through the awaited prompt() call tree.\n\t\t\tawait runWithOrigin(\n\t\t\t\t{\n\t\t\t\t\tplatform: source.platform,\n\t\t\t\t\tchatId: source.chatId,\n\t\t\t\t\tthreadId: source.threadId,\n\t\t\t\t\tchatType: source.chatType,\n\t\t\t\t},\n\t\t\t\t() => session.prompt(promptText, { source: \"extension\", ...(images.length ? { images } : {}) }),\n\t\t\t);\n\t\t} finally {\n\t\t\trunDepth--;\n\t\t\tunsubscribe?.();\n\t\t}\n\t\treturn extractFinalText(session);\n\t}\n\n\t/** Drain the queue FIFO: all queued messages become one concatenated turn. */\n\tprivate async drainQueue(entry: CacheEntry, callbacks: TurnCallbacks): Promise<void> {\n\t\twhile (entry.queue.length > 0) {\n\t\t\tconst items = entry.queue.splice(0);\n\t\t\tconst parts = items.map((item) => item.text);\n\t\t\tconst attachments = items.flatMap((item) => item.attachments ?? []);\n\t\t\tif (entry.dropped > 0) {\n\t\t\t\tparts.unshift(`[gateway: ${entry.dropped} earlier message(s) were dropped — the queue was full]`);\n\t\t\t\tentry.dropped = 0;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// Follow-up turns are final-only: no streaming callbacks. The origin\n\t\t\t\t// is the chat of the latest queued message.\n\t\t\t\tconst text = await this.runPrompt(\n\t\t\t\t\tentry,\n\t\t\t\t\tparts.join(\"\\n\\n\"),\n\t\t\t\t\t{},\n\t\t\t\t\titems[items.length - 1].source,\n\t\t\t\t\tattachments,\n\t\t\t\t);\n\t\t\t\tcallbacks.onFollowUpResult?.(text);\n\t\t\t} catch (err) {\n\t\t\t\tcallbacks.onError?.(err instanceof Error ? err.message : String(err));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async getOrCreate(key: string): Promise<CacheEntry> {\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) {\n\t\t\t// LRU refresh\n\t\t\tthis.cache.delete(key);\n\t\t\tthis.cache.set(key, cached);\n\t\t\treturn cached;\n\t\t}\n\t\t// Coalesce concurrent creation for the same key.\n\t\tlet pending = this.creating.get(key);\n\t\tif (!pending) {\n\t\t\tpending = this.createEntry(key);\n\t\t\tthis.creating.set(key, pending);\n\t\t\ttry {\n\t\t\t\tawait pending;\n\t\t\t} finally {\n\t\t\t\tthis.creating.delete(key);\n\t\t\t}\n\t\t}\n\t\treturn pending;\n\t}\n\n\tprivate async createEntry(key: string): Promise<CacheEntry> {\n\t\tconst stored = getSession(key);\n\t\tconst session = await this.sessionFactory(key, stored ? { sessionFile: stored.sessionFile } : undefined);\n\t\tconst sessionFile = session.sessionManager?.getSessionFile();\n\t\tconst sessionId = session.sessionManager?.getSessionId();\n\t\tif (sessionFile) {\n\t\t\tputSession(key, { sessionId: sessionId ?? key, sessionFile });\n\t\t}\n\t\tif (sessionId) {\n\t\t\tcreatePermissionContext(sessionId);\n\t\t}\n\t\tconst entry: CacheEntry = { session, busy: false, queue: [], dropped: 0, unsubscribe: undefined };\n\t\tthis.cache.set(key, entry);\n\t\tthis._enforceCacheCap(key);\n\t\tentry.unsubscribe = this._subscribeToSession(key, entry);\n\t\treturn entry;\n\t}\n\n\tprivate _enforceCacheCap(protectedKey?: string): void {\n\t\twhile (this.cache.size > this.cap) {\n\t\t\tlet oldestKey: string | undefined;\n\t\t\tlet oldest: CacheEntry | undefined;\n\t\t\tfor (const [key, entry] of this.cache) {\n\t\t\t\tif (key === protectedKey) continue;\n\t\t\t\tif (entry.busy) continue;\n\t\t\t\toldestKey = key;\n\t\t\t\toldest = entry;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!oldest || !oldestKey) {\n\t\t\t\t// Every cached session is busy (or is the protected new entry);\n\t\t\t\t// grow the cap temporarily rather than killing an in-flight turn.\n\t\t\t\tthis.cap = this.cache.size;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toldest.unsubscribe?.();\n\t\t\tthis.cache.delete(oldestKey);\n\t\t\toldest.session.dispose?.();\n\t\t\tconst evictedSessionId = oldest.session.sessionManager?.getSessionId();\n\t\t\tif (evictedSessionId) deletePermissionContext(evictedSessionId);\n\t\t}\n\t}\n\n\tprivate _subscribeToSession(key: string, entry: CacheEntry): () => void {\n\t\treturn entry.session.subscribe((event) => this._handleSessionEvent(key, event));\n\t}\n\n\tprivate _handleSessionEvent(key: string, event: AgentSessionEvent): void {\n\t\tif (event.type === \"message_start\" && event.message?.role === \"user\") {\n\t\t\tthis.redoStack.delete(key);\n\t\t}\n\t}\n}\n\nfunction extractUserText(message: AgentMessage): string {\n\tlet text = \"\";\n\tif (\"content\" in message && Array.isArray(message.content)) {\n\t\tfor (const block of message.content) {\n\t\t\tif (\n\t\t\t\tblock &&\n\t\t\t\ttypeof block === \"object\" &&\n\t\t\t\t\"type\" in block &&\n\t\t\t\tblock.type === \"text\" &&\n\t\t\t\t\"text\" in block &&\n\t\t\t\ttypeof block.text === \"string\"\n\t\t\t) {\n\t\t\t\ttext += block.text;\n\t\t\t}\n\t\t}\n\t}\n\treturn text;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/gateway/commands.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAmEhE,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,YAAY,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IAEtB,OAAO,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CAC1D;AA4jBD,eAAO,MAAM,aAAa,EAAE,WAAW,EAetC,CAAC;AAEF,gGAAgG;AAChG,wBAAgB,eAAe,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAAE,CAEzE;AAED,wBAAgB,cAAc,IAAI,MAAM,CASvC;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAwBhG;AAED,uEAAuE;AACvE,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKjH","sourcesContent":["/**\n * lunR: gateway chat command registry.\n *\n * Every command is a small headless handler over AgentSession. Router.ts keeps\n * platform gating/authorization and delegates to runChatCommand for busy/session\n * guards, error wrapping, and /help generation.\n */\n\nimport type { ThinkingLevel } from \"@ashx-j/lunr-agent\";\nimport type { Model } from \"@ashx-j/lunr-ai/compat\";\nimport { computeContextBreakdown } from \"../core/context-breakdown.ts\";\nimport type { ToolDefinition } from \"../core/extensions/types.ts\";\nimport { findExactModelReferenceMatch } from \"../core/model-resolver.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport type { SessionInfo } from \"../core/session-manager.ts\";\nimport { SessionManager } from \"../core/session-manager.ts\";\nimport { buildSwarmPrompt } from \"../core/swarm.ts\";\nimport type { BridgeSession } from \"./agent-bridge.ts\";\nimport { createPicker, type PickerItem } from \"./buttons.ts\";\nimport type { BridgeLike } from \"./router.ts\";\nimport type { MessageEvent, PlatformAdapter } from \"./types.ts\";\n\nconst MODEL_CACHE_TTL_MS = 5 * 60 * 1000;\nconst SESSIONS_CACHE_TTL_MS = 5 * 60 * 1000;\nconst MAX_MODEL_LIST_LINES = 15;\nconst PROVIDER_PER_PAGE = 10;\nconst _MODEL_PER_PAGE = 8;\nconst MAX_MODELS_PER_PROVIDER = 50;\nconst MODEL_ID_MAX = 38;\nconst MODEL_CACHE_CAP = 100;\nconst SESSIONS_CACHE_CAP = 100;\n\ninterface ModelCacheEntry {\n\tmodels: Model<any>[];\n\texpires: number;\n}\n\ninterface SessionsCacheEntry {\n\tsessions: SessionInfo[];\n\texpires: number;\n}\n\nconst modelCache = new Map<string, ModelCacheEntry>();\nconst sessionsCache = new Map<string, SessionsCacheEntry>();\n\nfunction evictLRU<K, V>(map: Map<K, V>, cap: number): void {\n\twhile (map.size > cap) {\n\t\tconst first = map.keys().next().value;\n\t\tif (first !== undefined) map.delete(first);\n\t}\n}\n\nfunction getCachedModels(key: string): Model<any>[] | undefined {\n\tconst cached = modelCache.get(key);\n\tif (!cached) return undefined;\n\tif (Date.now() > cached.expires) {\n\t\tmodelCache.delete(key);\n\t\treturn undefined;\n\t}\n\t// LRU refresh.\n\tmodelCache.delete(key);\n\tmodelCache.set(key, cached);\n\treturn cached.models;\n}\n\nfunction setCachedModels(key: string, models: Model<any>[]): void {\n\tmodelCache.set(key, { models, expires: Date.now() + MODEL_CACHE_TTL_MS });\n\tevictLRU(modelCache, MODEL_CACHE_CAP);\n}\n\nfunction getCachedSessions(key: string): SessionInfo[] | undefined {\n\tconst cached = sessionsCache.get(key);\n\tif (!cached) return undefined;\n\tif (Date.now() > cached.expires) {\n\t\tsessionsCache.delete(key);\n\t\treturn undefined;\n\t}\n\tsessionsCache.delete(key);\n\tsessionsCache.set(key, cached);\n\treturn cached.sessions;\n}\n\nfunction setCachedSessions(key: string, sessions: SessionInfo[]): void {\n\tsessionsCache.set(key, { sessions, expires: Date.now() + SESSIONS_CACHE_TTL_MS });\n\tevictLRU(sessionsCache, SESSIONS_CACHE_CAP);\n}\n\nexport interface ChatCommandContext {\n\tevent: MessageEvent;\n\tkey: string;\n\tadapter: PlatformAdapter;\n\tbridge: BridgeLike;\n\tsession?: BridgeSession;\n\treply(text: string): Promise<void>;\n\targs: string;\n}\n\nexport interface ChatCommand {\n\tname: string;\n\taliases?: string[];\n\tdescription: string;\n\tbypassBusy: boolean;\n\tneedsSession: boolean;\n\t// biome-ignore lint/suspicious/noConfusingVoidType: handlers may return nothing; runChatCommand treats undefined as consumed.\n\thandler(ctx: ChatCommandContext): Promise<boolean | void>;\n}\n\nfunction oneLine(text: string): string {\n\treturn text.replace(/\\s+/g, \" \").trim().slice(0, 200);\n}\n\nfunction prefixLines(text: string, prefix: string): string {\n\treturn text\n\t\t.split(\"\\n\")\n\t\t.map((line, index) => (index === 0 ? prefix : \" \") + line)\n\t\t.join(\"\\n\");\n}\n\nfunction truncate(text: string, max: number): string {\n\tif (text.length <= max) return text;\n\treturn `${text.slice(0, max - 1)}…`;\n}\n\nfunction formatRelativeTime(date: Date): string {\n\tconst seconds = Math.max(0, Math.floor((Date.now() - date.getTime()) / 1000));\n\tif (seconds < 60) return `${seconds}s ago`;\n\tconst minutes = Math.floor(seconds / 60);\n\tif (minutes < 60) return `${minutes}m ago`;\n\tconst hours = Math.floor(minutes / 60);\n\tif (hours < 24) return `${hours}h ago`;\n\tconst days = Math.floor(hours / 24);\n\treturn `${days}d ago`;\n}\n\nfunction formatModelList(models: Model<any>[], maxLines = MAX_MODEL_LIST_LINES): string[] {\n\tif (models.length === 0) return [\"No models available.\"];\n\tconst byProvider = new Map<string, Model<any>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\tconst providers = [...byProvider.keys()].sort();\n\tconst lines: string[] = [];\n\tlet index = 1;\n\tfor (const provider of providers) {\n\t\tconst list = (byProvider.get(provider) ?? []).sort((a, b) => a.id.localeCompare(b.id));\n\t\tfor (const model of list) {\n\t\t\tlines.push(`${index}) ${provider}/${model.id}`);\n\t\t\tindex++;\n\t\t}\n\t}\n\tif (lines.length <= maxLines) return lines;\n\treturn [\n\t\t`${models.length} models across ${providers.length} providers.`,\n\t\t\"Use /model <provider/id> or /model <query>.\",\n\t\t...providers.map((p) => `- ${p}`),\n\t];\n}\n\nfunction groupModelsByProvider(models: Model<any>[]): Map<string, Model<any>[]> {\n\tconst byProvider = new Map<string, Model<any>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\tfor (const list of byProvider.values()) {\n\t\tlist.sort((a, b) => a.id.localeCompare(b.id));\n\t}\n\treturn byProvider;\n}\n\nfunction truncateModelId(id: string): string {\n\tconst lastSlash = id.lastIndexOf(\"/\");\n\tconst name = lastSlash >= 0 ? id.slice(lastSlash + 1) : id;\n\treturn truncate(name, MODEL_ID_MAX);\n}\n\nasync function refreshAndCacheModels(session: BridgeSession, key: string): Promise<Model<any>[]> {\n\tconst runtime: ModelRuntime = session.modelRuntime;\n\tawait runtime.refresh();\n\tconst models = [...(await runtime.getAvailable())].sort(\n\t\t(a, b) => a.provider.localeCompare(b.provider) || a.id.localeCompare(b.id),\n\t);\n\tsetCachedModels(key, models);\n\treturn models;\n}\n\nasync function setModelAndReply(ctx: ChatCommandContext, model: Model<any>): Promise<void> {\n\tconst session = ctx.session!;\n\ttry {\n\t\tawait session.setModel(model);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"No API key\")) {\n\t\t\tawait ctx.reply(`No API key for ${model.provider}.`);\n\t\t\treturn;\n\t\t}\n\t\tthrow error;\n\t}\n\tawait ctx.reply(`Model → ${model.provider}/${model.id} (thinking level re-clamped to ${session.thinkingLevel})`);\n}\n\nconst newCommand: ChatCommand = {\n\tname: \"new\",\n\taliases: [\"reset\"],\n\tdescription: \"start a fresh session for this chat\",\n\tbypassBusy: false,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.reset(ctx.key);\n\t\tawait ctx.reply(\"Session reset — next message starts a fresh session.\");\n\t},\n};\n\nconst undoCommand: ChatCommand = {\n\tname: \"undo\",\n\tdescription: \"rewind the session tree before the last user turn\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst { userText } = await ctx.bridge.undo(ctx.key);\n\t\tconst text = userText ? ` \"${truncate(userText, 60)}\"` : \"\";\n\t\tawait ctx.reply(`Undone — rewound before${text}. /redo to restore.`);\n\t},\n};\n\nconst redoCommand: ChatCommand = {\n\tname: \"redo\",\n\tdescription: \"restore the last undone turn\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.redo(ctx.key);\n\t\tawait ctx.reply(\"Redone.\");\n\t},\n};\n\nconst stopCommand: ChatCommand = {\n\tname: \"stop\",\n\tdescription: \"abort the running turn\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.abort(ctx.key);\n\t\tawait ctx.reply(\"Stopped.\");\n\t},\n};\n\nconst statusCommand: ChatCommand = {\n\tname: \"status\",\n\tdescription: \"session status\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tconst status = ctx.bridge.getStatus(ctx.key);\n\t\tconst age = status.createdAt\n\t\t\t? `${Math.max(0, Math.round((Date.now() - Date.parse(status.createdAt)) / 1000))}s`\n\t\t\t: \"no session yet\";\n\t\tawait ctx.reply(\n\t\t\t`${ctx.event.source.platform} · session age ${age} · ${status.busy ? \"busy\" : \"idle\"} · queue ${status.queueDepth}`,\n\t\t);\n\t},\n};\n\nconst whoamiCommand: ChatCommand = {\n\tname: \"whoami\",\n\tdescription: \"your ids and session key\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tconst { userId, chatId } = ctx.event.source;\n\t\tawait ctx.reply(`userId: ${userId}\\nchatId: ${chatId}\\nkey: ${ctx.key}`);\n\t},\n};\n\nconst helpCommand: ChatCommand = {\n\tname: \"help\",\n\tdescription: \"this message\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.reply(formatHelpText());\n\t},\n};\n\nconst modelCommand: ChatCommand = {\n\tname: \"model\",\n\tdescription: \"list or switch models\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst arg = ctx.args.trim();\n\t\tif (!arg) {\n\t\t\tconst cached = getCachedModels(ctx.key);\n\t\t\tconst models = cached ?? (await refreshAndCacheModels(session, ctx.key));\n\t\t\tconst byProvider = groupModelsByProvider(models);\n\t\t\tconst current = session.model;\n\t\t\tconst currentRef = current ? `${current.provider}/${current.id}` : \"\";\n\t\t\tconst providers = [...byProvider.keys()].sort();\n\t\t\tconst providerItems: PickerItem[] = providers.map((provider) => ({\n\t\t\t\tlabel: `${current?.provider === provider ? \"✓ \" : \"\"}${provider} (${byProvider.get(provider)?.length ?? 0})`,\n\t\t\t\tvalue: provider,\n\t\t\t}));\n\n\t\t\tconst pickerOpts = {\n\t\t\t\treplyTo: ctx.event.messageId,\n\t\t\t\tthreadId: ctx.event.source.threadId,\n\t\t\t};\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"model\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems: providerItems,\n\t\t\t\t\tperPage: PROVIDER_PER_PAGE,\n\t\t\t\t\ttitle: \"Pick a model provider\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tif (item.value === \"__back__\") {\n\t\t\t\t\t\t\treturn { done: false, items: providerItems, title: \"Pick a model provider\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!item.value.includes(\"/\")) {\n\t\t\t\t\t\t\tconst provider = item.value;\n\t\t\t\t\t\t\tconst list = byProvider.get(provider) ?? [];\n\t\t\t\t\t\t\tconst capped = list.slice(0, MAX_MODELS_PER_PROVIDER);\n\t\t\t\t\t\t\tconst note =\n\t\t\t\t\t\t\t\tlist.length > MAX_MODELS_PER_PROVIDER\n\t\t\t\t\t\t\t\t\t? ` — ${list.length - MAX_MODELS_PER_PROVIDER} more available`\n\t\t\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\t\tconst modelItems: PickerItem[] = [];\n\t\t\t\t\t\t\tfor (const m of capped) {\n\t\t\t\t\t\t\t\tconst ref = `${provider}/${m.id}`;\n\t\t\t\t\t\t\t\tmodelItems.push({\n\t\t\t\t\t\t\t\t\tlabel: `${currentRef === ref ? \"✓ \" : \"\"}${truncateModelId(m.id)}`,\n\t\t\t\t\t\t\t\t\tvalue: ref,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmodelItems.unshift({ label: \"◀ Back\", value: \"__back__\" });\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tdone: false,\n\t\t\t\t\t\t\t\titems: modelItems,\n\t\t\t\t\t\t\t\ttitle: `${provider} (${list.length})${note}`,\n\t\t\t\t\t\t\t\tbreadcrumbs: provider,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst exact = findExactModelReferenceMatch(item.value, models);\n\t\t\t\t\t\tif (!exact) {\n\t\t\t\t\t\t\treturn { done: true, text: \"That model is no longer available.\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait session.setModel(exact);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\t\tif (message.includes(\"No API key\")) {\n\t\t\t\t\t\t\t\treturn { done: true, text: `No API key for ${exact.provider}.` };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tdone: true,\n\t\t\t\t\t\t\ttext: `☾ Model → ${exact.provider}/${exact.id} (thinking level re-clamped to ${session.thinkingLevel})`,\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpickerOpts,\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tconst currentText = `${current?.provider ?? \"none\"}/${current?.id ?? \"none\"}`;\n\t\t\t\tconst lines = formatModelList(models);\n\t\t\t\tawait ctx.reply([`Current: ${currentText}`, ...lines, \"Use /model <n> or /model <provider/id>\"].join(\"\\n\"));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst numeric = Number.parseInt(arg, 10);\n\t\tif (!Number.isNaN(numeric) && /^\\d+$/.test(arg)) {\n\t\t\tconst cached = getCachedModels(ctx.key);\n\t\t\tif (!cached || numeric < 1 || numeric > cached.length) {\n\t\t\t\tawait ctx.reply(\"Invalid selection — run /model to see the current list.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait setModelAndReply(ctx, cached[numeric - 1]);\n\t\t\treturn;\n\t\t}\n\n\t\tconst models = getCachedModels(ctx.key) ?? (await refreshAndCacheModels(session, ctx.key));\n\t\tconst exact = findExactModelReferenceMatch(arg, models);\n\t\tif (exact) {\n\t\t\tawait setModelAndReply(ctx, exact);\n\t\t\treturn;\n\t\t}\n\n\t\tconst query = arg.toLowerCase();\n\t\tconst matches = models.filter(\n\t\t\t(m) =>\n\t\t\t\tm.id.toLowerCase().includes(query) ||\n\t\t\t\t`${m.provider}/${m.id}`.toLowerCase().includes(query) ||\n\t\t\t\t(m.name?.toLowerCase().includes(query) ?? false),\n\t\t);\n\t\tif (matches.length === 0) {\n\t\t\tawait ctx.reply(`No model matches \"${arg}\".`);\n\t\t\treturn;\n\t\t}\n\t\tif (matches.length > 1) {\n\t\t\tawait ctx.reply(formatModelList(matches).join(\"\\n\"));\n\t\t\treturn;\n\t\t}\n\t\tawait setModelAndReply(ctx, matches[0]);\n\t},\n};\n\nconst sessionsCommand: ChatCommand = {\n\tname: \"sessions\",\n\tdescription: \"list or switch sessions\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst arg = ctx.args.trim();\n\t\tconst currentFile = session.sessionManager?.getSessionFile();\n\n\t\tif (!arg) {\n\t\t\tconst all = await SessionManager.list(process.cwd());\n\t\t\tall.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\t\tconst sessions = all.slice(0, 10);\n\t\t\tsetCachedSessions(ctx.key, sessions);\n\t\t\tconst items: PickerItem[] = sessions.map((s) => ({\n\t\t\t\tlabel: `${s.path === currentFile ? \"☾ \" : \"\"}${s.name ? truncate(s.name, 30) : truncate(s.firstMessage, 30) || \"(empty)\"}`,\n\t\t\t\tvalue: s.path,\n\t\t\t}));\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"sessions\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems,\n\t\t\t\t\tperPage: 8,\n\t\t\t\t\ttitle: \"Pick a session\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tawait ctx.bridge.switchSession(ctx.key, item.value);\n\t\t\t\t\t\tconst switched = await ctx.bridge.getSession(ctx.key);\n\t\t\t\t\t\tconst name = switched?.sessionManager?.getSessionName() ?? \"unnamed\";\n\t\t\t\t\t\tconst count = switched?.sessionManager?.getEntries().length ?? 0;\n\t\t\t\t\t\treturn { done: true, text: `☾ Switched to \"${name}\" (${count} msgs). History continues here.` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tconst lines = sessions.map((s, i) => {\n\t\t\t\t\tconst marker = s.path === currentFile ? \" ☾\" : \"\";\n\t\t\t\t\tconst label = s.name ? truncate(s.name, 40) : truncate(s.firstMessage, 40) || \"(empty)\";\n\t\t\t\t\treturn `${i + 1}) ${label}${marker} · ${formatRelativeTime(s.modified)} · ${s.messageCount} msgs`;\n\t\t\t\t});\n\t\t\t\tawait ctx.reply(\n\t\t\t\t\t[`Sessions for ${process.cwd()}`, ...lines, \"Use /sessions <n> or /sessions <id-prefix>\"].join(\"\\n\"),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tlet path: string | undefined;\n\t\tconst numeric = Number.parseInt(arg, 10);\n\t\tconst cached = getCachedSessions(ctx.key);\n\t\tif (!Number.isNaN(numeric) && /^\\d+$/.test(arg)) {\n\t\t\tif (!cached || numeric < 1 || numeric > cached.length) {\n\t\t\t\tawait ctx.reply(\"Invalid selection — run /sessions to see the current list.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpath = cached[numeric - 1].path;\n\t\t} else {\n\t\t\tconst pool = cached ?? (await SessionManager.list(process.cwd()));\n\t\t\tconst matches = pool.filter((s) => s.id.startsWith(arg) || s.path.startsWith(arg));\n\t\t\tif (matches.length === 0) {\n\t\t\t\tawait ctx.reply(`No session matches \"${arg}\".`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (matches.length > 1) {\n\t\t\t\tawait ctx.reply(`Multiple sessions match \"${arg}\" — use a longer prefix.`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpath = matches[0].path;\n\t\t}\n\n\t\tif (!path) {\n\t\t\tawait ctx.reply(\"No session selected.\");\n\t\t\treturn;\n\t\t}\n\n\t\tawait ctx.bridge.switchSession(ctx.key, path);\n\t\tconst switched = await ctx.bridge.getSession(ctx.key);\n\t\tconst name = switched?.sessionManager?.getSessionName() ?? \"unnamed\";\n\t\tconst count = switched?.sessionManager?.getEntries().length ?? 0;\n\t\tawait ctx.reply(`Switched to \"${name}\" (${count} msgs). History continues here.`);\n\t},\n};\n\nconst titleCommand: ChatCommand = {\n\tname: \"title\",\n\taliases: [\"name\"],\n\tdescription: \"name the current session\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst name = ctx.args.trim();\n\t\tif (!name) {\n\t\t\tconst current = session.sessionManager?.getSessionName();\n\t\t\tawait ctx.reply(current ? `Session title: ${current}` : \"(unnamed)\");\n\t\t\treturn;\n\t\t}\n\t\tsession.setSessionName(name);\n\t\tconst readBack = session.sessionManager?.getSessionName() ?? name;\n\t\tawait ctx.reply(`Session titled \"${readBack}\".`);\n\t},\n};\n\nconst contextCommand: ChatCommand = {\n\tname: \"context\",\n\tdescription: \"estimated context window breakdown\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst model = session.model;\n\t\tif (!model) {\n\t\t\tawait ctx.reply(\"No model selected.\");\n\t\t\treturn;\n\t\t}\n\t\tconst contextWindow = model.contextWindow ?? 0;\n\t\tif (contextWindow <= 0) {\n\t\t\tawait ctx.reply(\"Context window unknown for this model.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst tools: ToolDefinition[] = session\n\t\t\t.getActiveToolNames()\n\t\t\t.map((name) => session.getToolDefinition(name))\n\t\t\t.filter((d): d is ToolDefinition => d !== undefined);\n\n\t\tconst breakdown = computeContextBreakdown({\n\t\t\tsystemPrompt: session.systemPrompt,\n\t\t\ttools,\n\t\t\tmessages: session.messages,\n\t\t\tcontextWindow,\n\t\t});\n\n\t\tconst usage = session.getContextUsage();\n\t\tconst tokens = usage?.tokens ?? breakdown.total;\n\t\tconst percent = usage?.percent ?? (contextWindow > 0 ? (tokens / contextWindow) * 100 : 0);\n\n\t\tconst messageTokens =\n\t\t\tbreakdown.user +\n\t\t\tbreakdown.assistantText +\n\t\t\tbreakdown.thinking +\n\t\t\tbreakdown.toolCalls +\n\t\t\tbreakdown.toolResults +\n\t\t\tbreakdown.summaries;\n\n\t\tconst stats = session.getSessionStats();\n\t\tawait ctx.reply(\n\t\t\t[\n\t\t\t\t`Context: ${tokens.toLocaleString(\"en-US\")} / ${contextWindow.toLocaleString(\"en-US\")} tokens (${Math.round(percent)}%)`,\n\t\t\t\t` system ${breakdown.systemPrompt.toLocaleString(\"en-US\")} · tools ${breakdown.toolDefinitions.toLocaleString(\"en-US\")} · messages ${messageTokens.toLocaleString(\"en-US\")}`,\n\t\t\t\t`Session: ${stats.totalMessages} messages · ${stats.userMessages} turns · model ${model.provider}/${model.id}`,\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t},\n};\n\nconst swarmCommand: ChatCommand = {\n\tname: \"swarm\",\n\tdescription: \"decompose a task across parallel subagents\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst task = ctx.args.trim();\n\t\tif (!task) {\n\t\t\tawait ctx.reply(\"Usage: /swarm <task> — decomposes the task across parallel subagents.\");\n\t\t\treturn true;\n\t\t}\n\t\tctx.event.text = buildSwarmPrompt(task);\n\t\treturn false;\n\t},\n};\n\nconst compactCommand: ChatCommand = {\n\tname: \"compact\",\n\tdescription: \"summarize and compress session context\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst instructions = ctx.args.trim() || undefined;\n\t\ttry {\n\t\t\tconst result = await session.compact(instructions);\n\t\t\tconst before = result.tokensBefore.toLocaleString(\"en-US\");\n\t\t\tconst after =\n\t\t\t\tresult.estimatedTokensAfter !== undefined\n\t\t\t\t\t? `~${result.estimatedTokensAfter.toLocaleString(\"en-US\")}`\n\t\t\t\t\t: \"~?\";\n\t\t\tawait ctx.reply(`Compacted: ${before} → ${after} tokens.`);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tif (\n\t\t\t\tmessage.includes(\"Nothing to compact\") ||\n\t\t\t\tmessage.includes(\"Already compacted\") ||\n\t\t\t\tmessage.includes(\"No model selected\")\n\t\t\t) {\n\t\t\t\tawait ctx.reply(message);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t},\n};\n\nconst thinkingCommand: ChatCommand = {\n\tname: \"thinking\",\n\taliases: [\"effort\", \"reasoning\"],\n\tdescription: \"get or set the thinking level\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst levels = session.getAvailableThinkingLevels();\n\t\tif (!session.supportsThinking()) {\n\t\t\tawait ctx.reply(\n\t\t\t\t`Model ${session.model?.provider ?? \"none\"}/${session.model?.id ?? \"none\"} doesn't support thinking.`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst arg = ctx.args.trim().toLowerCase() as ThinkingLevel;\n\t\tif (!arg) {\n\t\t\tconst current = session.thinkingLevel;\n\t\t\tconst items: PickerItem[] = levels.map((level) => ({\n\t\t\t\tlabel: `${level === current ? \"✓ \" : \"\"}${level}`,\n\t\t\t\tvalue: level,\n\t\t\t}));\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"thinking\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems,\n\t\t\t\t\tperPage: levels.length,\n\t\t\t\t\ttitle: \"Pick a thinking level\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tconst level = item.value as ThinkingLevel;\n\t\t\t\t\t\tsession.setThinkingLevel(level);\n\t\t\t\t\t\treturn { done: true, text: `☾ Thinking → ${level}` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tawait ctx.reply(`Level: ${current} — available: ${levels.join(\", \")}`);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!levels.includes(arg)) {\n\t\t\tawait ctx.reply(`Invalid level. Available: ${levels.join(\", \")}`);\n\t\t\treturn;\n\t\t}\n\t\tsession.setThinkingLevel(arg);\n\t\tawait ctx.reply(`Thinking → ${arg}`);\n\t},\n};\n\nexport const CHAT_COMMANDS: ChatCommand[] = [\n\tnewCommand,\n\tundoCommand,\n\tredoCommand,\n\tstopCommand,\n\tstatusCommand,\n\twhoamiCommand,\n\thelpCommand,\n\tmodelCommand,\n\tsessionsCommand,\n\ttitleCommand,\n\tcontextCommand,\n\tswarmCommand,\n\tcompactCommand,\n\tthinkingCommand,\n];\n\n/** Platform menu specs (e.g. Telegram setMyCommands): canonical names only, aliases skipped. */\nexport function botCommandSpecs(): { name: string; description: string }[] {\n\treturn CHAT_COMMANDS.map((c) => ({ name: c.name, description: c.description }));\n}\n\nexport function formatHelpText(): string {\n\tconst lines = [\"lunR gateway commands:\"];\n\tfor (const cmd of CHAT_COMMANDS) {\n\t\tconst names = [cmd.name, ...(cmd.aliases ?? [])].map((a) => `/${a}`).join(\" | \");\n\t\tlines.push(`${names} — ${cmd.description}`);\n\t}\n\tlines.push(\"\");\n\tlines.push(\"Tap to pick: /model, /thinking, /sessions (run without args to see buttons).\");\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Run a chat command with busy/session guards and error → \"⚠ one-line\".\n * Returns true when the event was consumed by the command, false when the\n * router should continue (e.g. /swarm mutates the event and wants a normal turn).\n */\nexport async function runChatCommand(cmd: ChatCommand, ctx: ChatCommandContext): Promise<boolean> {\n\tif (!cmd.bypassBusy && ctx.bridge.getStatus(ctx.key).busy) {\n\t\tawait ctx.reply(\"⏳ busy — /stop first or wait\");\n\t\treturn true;\n\t}\n\tif (cmd.needsSession) {\n\t\tconst session = await ctx.bridge.getSession(ctx.key);\n\t\tif (!session) {\n\t\t\tawait ctx.reply(\"No session yet — send a message first.\");\n\t\t\treturn true;\n\t\t}\n\t\tctx.session = session;\n\t}\n\ttry {\n\t\tconst consumed = await cmd.handler(ctx);\n\t\treturn consumed ?? true;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tawait ctx.adapter.send(ctx.event.source.chatId, `⚠ ${oneLine(message)}`, {\n\t\t\treplyTo: ctx.event.messageId,\n\t\t\tthreadId: ctx.event.source.threadId,\n\t\t});\n\t\treturn true;\n\t}\n}\n\n/** Reply helper used by the router to build the ChatCommandContext. */\nexport async function sendCommandReply(adapter: PlatformAdapter, event: MessageEvent, text: string): Promise<void> {\n\tawait adapter.send(event.source.chatId, prefixLines(text, \"☾ \"), {\n\t\treplyTo: event.messageId,\n\t\tthreadId: event.source.threadId,\n\t});\n}\n"]}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/gateway/commands.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAmEhE,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,YAAY,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IAEtB,OAAO,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CAC1D;AA6jBD,eAAO,MAAM,aAAa,EAAE,WAAW,EAetC,CAAC;AAEF,gGAAgG;AAChG,wBAAgB,eAAe,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAAE,CAEzE;AAED,wBAAgB,cAAc,IAAI,MAAM,CASvC;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAwBhG;AAED,uEAAuE;AACvE,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKjH","sourcesContent":["/**\n * lunR: gateway chat command registry.\n *\n * Every command is a small headless handler over AgentSession. Router.ts keeps\n * platform gating/authorization and delegates to runChatCommand for busy/session\n * guards, error wrapping, and /help generation.\n */\n\nimport type { ThinkingLevel } from \"@ashx-j/lunr-agent\";\nimport type { Model } from \"@ashx-j/lunr-ai/compat\";\nimport { computeContextBreakdown } from \"../core/context-breakdown.ts\";\nimport type { ToolDefinition } from \"../core/extensions/types.ts\";\nimport { findExactModelReferenceMatch } from \"../core/model-resolver.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport type { SessionInfo } from \"../core/session-manager.ts\";\nimport { SessionManager } from \"../core/session-manager.ts\";\nimport { buildSwarmPrompt } from \"../core/swarm.ts\";\nimport type { BridgeSession } from \"./agent-bridge.ts\";\nimport { createPicker, type PickerItem } from \"./buttons.ts\";\nimport type { BridgeLike } from \"./router.ts\";\nimport type { MessageEvent, PlatformAdapter } from \"./types.ts\";\n\nconst MODEL_CACHE_TTL_MS = 5 * 60 * 1000;\nconst SESSIONS_CACHE_TTL_MS = 5 * 60 * 1000;\nconst MAX_MODEL_LIST_LINES = 15;\nconst PROVIDER_PER_PAGE = 10;\nconst _MODEL_PER_PAGE = 8;\nconst MAX_MODELS_PER_PROVIDER = 50;\nconst MODEL_ID_MAX = 38;\nconst MODEL_CACHE_CAP = 100;\nconst SESSIONS_CACHE_CAP = 100;\n\ninterface ModelCacheEntry {\n\tmodels: Model<any>[];\n\texpires: number;\n}\n\ninterface SessionsCacheEntry {\n\tsessions: SessionInfo[];\n\texpires: number;\n}\n\nconst modelCache = new Map<string, ModelCacheEntry>();\nconst sessionsCache = new Map<string, SessionsCacheEntry>();\n\nfunction evictLRU<K, V>(map: Map<K, V>, cap: number): void {\n\twhile (map.size > cap) {\n\t\tconst first = map.keys().next().value;\n\t\tif (first !== undefined) map.delete(first);\n\t}\n}\n\nfunction getCachedModels(key: string): Model<any>[] | undefined {\n\tconst cached = modelCache.get(key);\n\tif (!cached) return undefined;\n\tif (Date.now() > cached.expires) {\n\t\tmodelCache.delete(key);\n\t\treturn undefined;\n\t}\n\t// LRU refresh.\n\tmodelCache.delete(key);\n\tmodelCache.set(key, cached);\n\treturn cached.models;\n}\n\nfunction setCachedModels(key: string, models: Model<any>[]): void {\n\tmodelCache.set(key, { models, expires: Date.now() + MODEL_CACHE_TTL_MS });\n\tevictLRU(modelCache, MODEL_CACHE_CAP);\n}\n\nfunction getCachedSessions(key: string): SessionInfo[] | undefined {\n\tconst cached = sessionsCache.get(key);\n\tif (!cached) return undefined;\n\tif (Date.now() > cached.expires) {\n\t\tsessionsCache.delete(key);\n\t\treturn undefined;\n\t}\n\tsessionsCache.delete(key);\n\tsessionsCache.set(key, cached);\n\treturn cached.sessions;\n}\n\nfunction setCachedSessions(key: string, sessions: SessionInfo[]): void {\n\tsessionsCache.set(key, { sessions, expires: Date.now() + SESSIONS_CACHE_TTL_MS });\n\tevictLRU(sessionsCache, SESSIONS_CACHE_CAP);\n}\n\nexport interface ChatCommandContext {\n\tevent: MessageEvent;\n\tkey: string;\n\tadapter: PlatformAdapter;\n\tbridge: BridgeLike;\n\tsession?: BridgeSession;\n\treply(text: string): Promise<void>;\n\targs: string;\n}\n\nexport interface ChatCommand {\n\tname: string;\n\taliases?: string[];\n\tdescription: string;\n\tbypassBusy: boolean;\n\tneedsSession: boolean;\n\t// biome-ignore lint/suspicious/noConfusingVoidType: handlers may return nothing; runChatCommand treats undefined as consumed.\n\thandler(ctx: ChatCommandContext): Promise<boolean | void>;\n}\n\nfunction oneLine(text: string): string {\n\treturn text.replace(/\\s+/g, \" \").trim().slice(0, 200);\n}\n\nfunction prefixLines(text: string, prefix: string): string {\n\treturn text\n\t\t.split(\"\\n\")\n\t\t.map((line, index) => (index === 0 ? prefix : \" \") + line)\n\t\t.join(\"\\n\");\n}\n\nfunction truncate(text: string, max: number): string {\n\tif (text.length <= max) return text;\n\treturn `${text.slice(0, max - 1)}…`;\n}\n\nfunction formatRelativeTime(date: Date): string {\n\tconst seconds = Math.max(0, Math.floor((Date.now() - date.getTime()) / 1000));\n\tif (seconds < 60) return `${seconds}s ago`;\n\tconst minutes = Math.floor(seconds / 60);\n\tif (minutes < 60) return `${minutes}m ago`;\n\tconst hours = Math.floor(minutes / 60);\n\tif (hours < 24) return `${hours}h ago`;\n\tconst days = Math.floor(hours / 24);\n\treturn `${days}d ago`;\n}\n\nfunction formatModelList(models: Model<any>[], maxLines = MAX_MODEL_LIST_LINES): string[] {\n\tif (models.length === 0) return [\"No models available.\"];\n\tconst byProvider = new Map<string, Model<any>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\tconst providers = [...byProvider.keys()].sort();\n\tconst lines: string[] = [];\n\tlet index = 1;\n\tfor (const provider of providers) {\n\t\tconst list = (byProvider.get(provider) ?? []).sort((a, b) => a.id.localeCompare(b.id));\n\t\tfor (const model of list) {\n\t\t\tlines.push(`${index}) ${provider}/${model.id}`);\n\t\t\tindex++;\n\t\t}\n\t}\n\tif (lines.length <= maxLines) return lines;\n\treturn [\n\t\t`${models.length} models across ${providers.length} providers.`,\n\t\t\"Use /model <provider/id> or /model <query>.\",\n\t\t...providers.map((p) => `- ${p}`),\n\t];\n}\n\nfunction groupModelsByProvider(models: Model<any>[]): Map<string, Model<any>[]> {\n\tconst byProvider = new Map<string, Model<any>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\tfor (const list of byProvider.values()) {\n\t\tlist.sort((a, b) => a.id.localeCompare(b.id));\n\t}\n\treturn byProvider;\n}\n\nfunction truncateModelId(id: string): string {\n\tconst lastSlash = id.lastIndexOf(\"/\");\n\tconst name = lastSlash >= 0 ? id.slice(lastSlash + 1) : id;\n\treturn truncate(name, MODEL_ID_MAX);\n}\n\nasync function refreshAndCacheModels(session: BridgeSession, key: string): Promise<Model<any>[]> {\n\tconst runtime: ModelRuntime = session.modelRuntime;\n\tawait runtime.refresh();\n\tconst models = [...(await runtime.getAvailable())].sort(\n\t\t(a, b) => a.provider.localeCompare(b.provider) || a.id.localeCompare(b.id),\n\t);\n\tsetCachedModels(key, models);\n\treturn models;\n}\n\nasync function setModelAndReply(ctx: ChatCommandContext, model: Model<any>): Promise<void> {\n\tconst session = ctx.session!;\n\ttry {\n\t\tawait session.setModel(model);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"No API key\")) {\n\t\t\tawait ctx.reply(`No API key for ${model.provider}.`);\n\t\t\treturn;\n\t\t}\n\t\tthrow error;\n\t}\n\tawait ctx.reply(`Model → ${model.provider}/${model.id} (thinking level re-clamped to ${session.thinkingLevel})`);\n}\n\nconst newCommand: ChatCommand = {\n\tname: \"new\",\n\taliases: [\"reset\"],\n\tdescription: \"start a fresh session for this chat\",\n\t// lunr: busy-session /new must bypass the guard (same as /stop).\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.reset(ctx.key);\n\t\tawait ctx.reply(\"Session reset — next message starts a fresh session.\");\n\t},\n};\n\nconst undoCommand: ChatCommand = {\n\tname: \"undo\",\n\tdescription: \"rewind the session tree before the last user turn\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst { userText } = await ctx.bridge.undo(ctx.key);\n\t\tconst text = userText ? ` \"${truncate(userText, 60)}\"` : \"\";\n\t\tawait ctx.reply(`Undone — rewound before${text}. /redo to restore.`);\n\t},\n};\n\nconst redoCommand: ChatCommand = {\n\tname: \"redo\",\n\tdescription: \"restore the last undone turn\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.redo(ctx.key);\n\t\tawait ctx.reply(\"Redone.\");\n\t},\n};\n\nconst stopCommand: ChatCommand = {\n\tname: \"stop\",\n\tdescription: \"abort the running turn\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.abort(ctx.key);\n\t\tawait ctx.reply(\"Stopped.\");\n\t},\n};\n\nconst statusCommand: ChatCommand = {\n\tname: \"status\",\n\tdescription: \"session status\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tconst status = ctx.bridge.getStatus(ctx.key);\n\t\tconst age = status.createdAt\n\t\t\t? `${Math.max(0, Math.round((Date.now() - Date.parse(status.createdAt)) / 1000))}s`\n\t\t\t: \"no session yet\";\n\t\tawait ctx.reply(\n\t\t\t`${ctx.event.source.platform} · session age ${age} · ${status.busy ? \"busy\" : \"idle\"} · queue ${status.queueDepth}`,\n\t\t);\n\t},\n};\n\nconst whoamiCommand: ChatCommand = {\n\tname: \"whoami\",\n\tdescription: \"your ids and session key\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tconst { userId, chatId } = ctx.event.source;\n\t\tawait ctx.reply(`userId: ${userId}\\nchatId: ${chatId}\\nkey: ${ctx.key}`);\n\t},\n};\n\nconst helpCommand: ChatCommand = {\n\tname: \"help\",\n\tdescription: \"this message\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.reply(formatHelpText());\n\t},\n};\n\nconst modelCommand: ChatCommand = {\n\tname: \"model\",\n\tdescription: \"list or switch models\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst arg = ctx.args.trim();\n\t\tif (!arg) {\n\t\t\tconst cached = getCachedModels(ctx.key);\n\t\t\tconst models = cached ?? (await refreshAndCacheModels(session, ctx.key));\n\t\t\tconst byProvider = groupModelsByProvider(models);\n\t\t\tconst current = session.model;\n\t\t\tconst currentRef = current ? `${current.provider}/${current.id}` : \"\";\n\t\t\tconst providers = [...byProvider.keys()].sort();\n\t\t\tconst providerItems: PickerItem[] = providers.map((provider) => ({\n\t\t\t\tlabel: `${current?.provider === provider ? \"✓ \" : \"\"}${provider} (${byProvider.get(provider)?.length ?? 0})`,\n\t\t\t\tvalue: provider,\n\t\t\t}));\n\n\t\t\tconst pickerOpts = {\n\t\t\t\treplyTo: ctx.event.messageId,\n\t\t\t\tthreadId: ctx.event.source.threadId,\n\t\t\t};\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"model\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems: providerItems,\n\t\t\t\t\tperPage: PROVIDER_PER_PAGE,\n\t\t\t\t\ttitle: \"Pick a model provider\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tif (item.value === \"__back__\") {\n\t\t\t\t\t\t\treturn { done: false, items: providerItems, title: \"Pick a model provider\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!item.value.includes(\"/\")) {\n\t\t\t\t\t\t\tconst provider = item.value;\n\t\t\t\t\t\t\tconst list = byProvider.get(provider) ?? [];\n\t\t\t\t\t\t\tconst capped = list.slice(0, MAX_MODELS_PER_PROVIDER);\n\t\t\t\t\t\t\tconst note =\n\t\t\t\t\t\t\t\tlist.length > MAX_MODELS_PER_PROVIDER\n\t\t\t\t\t\t\t\t\t? ` — ${list.length - MAX_MODELS_PER_PROVIDER} more available`\n\t\t\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\t\tconst modelItems: PickerItem[] = [];\n\t\t\t\t\t\t\tfor (const m of capped) {\n\t\t\t\t\t\t\t\tconst ref = `${provider}/${m.id}`;\n\t\t\t\t\t\t\t\tmodelItems.push({\n\t\t\t\t\t\t\t\t\tlabel: `${currentRef === ref ? \"✓ \" : \"\"}${truncateModelId(m.id)}`,\n\t\t\t\t\t\t\t\t\tvalue: ref,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmodelItems.unshift({ label: \"◀ Back\", value: \"__back__\" });\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tdone: false,\n\t\t\t\t\t\t\t\titems: modelItems,\n\t\t\t\t\t\t\t\ttitle: `${provider} (${list.length})${note}`,\n\t\t\t\t\t\t\t\tbreadcrumbs: provider,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst exact = findExactModelReferenceMatch(item.value, models);\n\t\t\t\t\t\tif (!exact) {\n\t\t\t\t\t\t\treturn { done: true, text: \"That model is no longer available.\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait session.setModel(exact);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\t\tif (message.includes(\"No API key\")) {\n\t\t\t\t\t\t\t\treturn { done: true, text: `No API key for ${exact.provider}.` };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tdone: true,\n\t\t\t\t\t\t\ttext: `☾ Model → ${exact.provider}/${exact.id} (thinking level re-clamped to ${session.thinkingLevel})`,\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpickerOpts,\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tconst currentText = `${current?.provider ?? \"none\"}/${current?.id ?? \"none\"}`;\n\t\t\t\tconst lines = formatModelList(models);\n\t\t\t\tawait ctx.reply([`Current: ${currentText}`, ...lines, \"Use /model <n> or /model <provider/id>\"].join(\"\\n\"));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst numeric = Number.parseInt(arg, 10);\n\t\tif (!Number.isNaN(numeric) && /^\\d+$/.test(arg)) {\n\t\t\tconst cached = getCachedModels(ctx.key);\n\t\t\tif (!cached || numeric < 1 || numeric > cached.length) {\n\t\t\t\tawait ctx.reply(\"Invalid selection — run /model to see the current list.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait setModelAndReply(ctx, cached[numeric - 1]);\n\t\t\treturn;\n\t\t}\n\n\t\tconst models = getCachedModels(ctx.key) ?? (await refreshAndCacheModels(session, ctx.key));\n\t\tconst exact = findExactModelReferenceMatch(arg, models);\n\t\tif (exact) {\n\t\t\tawait setModelAndReply(ctx, exact);\n\t\t\treturn;\n\t\t}\n\n\t\tconst query = arg.toLowerCase();\n\t\tconst matches = models.filter(\n\t\t\t(m) =>\n\t\t\t\tm.id.toLowerCase().includes(query) ||\n\t\t\t\t`${m.provider}/${m.id}`.toLowerCase().includes(query) ||\n\t\t\t\t(m.name?.toLowerCase().includes(query) ?? false),\n\t\t);\n\t\tif (matches.length === 0) {\n\t\t\tawait ctx.reply(`No model matches \"${arg}\".`);\n\t\t\treturn;\n\t\t}\n\t\tif (matches.length > 1) {\n\t\t\tawait ctx.reply(formatModelList(matches).join(\"\\n\"));\n\t\t\treturn;\n\t\t}\n\t\tawait setModelAndReply(ctx, matches[0]);\n\t},\n};\n\nconst sessionsCommand: ChatCommand = {\n\tname: \"sessions\",\n\tdescription: \"list or switch sessions\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst arg = ctx.args.trim();\n\t\tconst currentFile = session.sessionManager?.getSessionFile();\n\n\t\tif (!arg) {\n\t\t\tconst all = await SessionManager.list(process.cwd());\n\t\t\tall.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\t\tconst sessions = all.slice(0, 10);\n\t\t\tsetCachedSessions(ctx.key, sessions);\n\t\t\tconst items: PickerItem[] = sessions.map((s) => ({\n\t\t\t\tlabel: `${s.path === currentFile ? \"☾ \" : \"\"}${s.name ? truncate(s.name, 30) : truncate(s.firstMessage, 30) || \"(empty)\"}`,\n\t\t\t\tvalue: s.path,\n\t\t\t}));\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"sessions\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems,\n\t\t\t\t\tperPage: 8,\n\t\t\t\t\ttitle: \"Pick a session\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tawait ctx.bridge.switchSession(ctx.key, item.value);\n\t\t\t\t\t\tconst switched = await ctx.bridge.getSession(ctx.key);\n\t\t\t\t\t\tconst name = switched?.sessionManager?.getSessionName() ?? \"unnamed\";\n\t\t\t\t\t\tconst count = switched?.sessionManager?.getEntries().length ?? 0;\n\t\t\t\t\t\treturn { done: true, text: `☾ Switched to \"${name}\" (${count} msgs). History continues here.` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tconst lines = sessions.map((s, i) => {\n\t\t\t\t\tconst marker = s.path === currentFile ? \" ☾\" : \"\";\n\t\t\t\t\tconst label = s.name ? truncate(s.name, 40) : truncate(s.firstMessage, 40) || \"(empty)\";\n\t\t\t\t\treturn `${i + 1}) ${label}${marker} · ${formatRelativeTime(s.modified)} · ${s.messageCount} msgs`;\n\t\t\t\t});\n\t\t\t\tawait ctx.reply(\n\t\t\t\t\t[`Sessions for ${process.cwd()}`, ...lines, \"Use /sessions <n> or /sessions <id-prefix>\"].join(\"\\n\"),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tlet path: string | undefined;\n\t\tconst numeric = Number.parseInt(arg, 10);\n\t\tconst cached = getCachedSessions(ctx.key);\n\t\tif (!Number.isNaN(numeric) && /^\\d+$/.test(arg)) {\n\t\t\tif (!cached || numeric < 1 || numeric > cached.length) {\n\t\t\t\tawait ctx.reply(\"Invalid selection — run /sessions to see the current list.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpath = cached[numeric - 1].path;\n\t\t} else {\n\t\t\tconst pool = cached ?? (await SessionManager.list(process.cwd()));\n\t\t\tconst matches = pool.filter((s) => s.id.startsWith(arg) || s.path.startsWith(arg));\n\t\t\tif (matches.length === 0) {\n\t\t\t\tawait ctx.reply(`No session matches \"${arg}\".`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (matches.length > 1) {\n\t\t\t\tawait ctx.reply(`Multiple sessions match \"${arg}\" — use a longer prefix.`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpath = matches[0].path;\n\t\t}\n\n\t\tif (!path) {\n\t\t\tawait ctx.reply(\"No session selected.\");\n\t\t\treturn;\n\t\t}\n\n\t\tawait ctx.bridge.switchSession(ctx.key, path);\n\t\tconst switched = await ctx.bridge.getSession(ctx.key);\n\t\tconst name = switched?.sessionManager?.getSessionName() ?? \"unnamed\";\n\t\tconst count = switched?.sessionManager?.getEntries().length ?? 0;\n\t\tawait ctx.reply(`Switched to \"${name}\" (${count} msgs). History continues here.`);\n\t},\n};\n\nconst titleCommand: ChatCommand = {\n\tname: \"title\",\n\taliases: [\"name\"],\n\tdescription: \"name the current session\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst name = ctx.args.trim();\n\t\tif (!name) {\n\t\t\tconst current = session.sessionManager?.getSessionName();\n\t\t\tawait ctx.reply(current ? `Session title: ${current}` : \"(unnamed)\");\n\t\t\treturn;\n\t\t}\n\t\tsession.setSessionName(name);\n\t\tconst readBack = session.sessionManager?.getSessionName() ?? name;\n\t\tawait ctx.reply(`Session titled \"${readBack}\".`);\n\t},\n};\n\nconst contextCommand: ChatCommand = {\n\tname: \"context\",\n\tdescription: \"estimated context window breakdown\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst model = session.model;\n\t\tif (!model) {\n\t\t\tawait ctx.reply(\"No model selected.\");\n\t\t\treturn;\n\t\t}\n\t\tconst contextWindow = model.contextWindow ?? 0;\n\t\tif (contextWindow <= 0) {\n\t\t\tawait ctx.reply(\"Context window unknown for this model.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst tools: ToolDefinition[] = session\n\t\t\t.getActiveToolNames()\n\t\t\t.map((name) => session.getToolDefinition(name))\n\t\t\t.filter((d): d is ToolDefinition => d !== undefined);\n\n\t\tconst breakdown = computeContextBreakdown({\n\t\t\tsystemPrompt: session.systemPrompt,\n\t\t\ttools,\n\t\t\tmessages: session.messages,\n\t\t\tcontextWindow,\n\t\t});\n\n\t\tconst usage = session.getContextUsage();\n\t\tconst tokens = usage?.tokens ?? breakdown.total;\n\t\tconst percent = usage?.percent ?? (contextWindow > 0 ? (tokens / contextWindow) * 100 : 0);\n\n\t\tconst messageTokens =\n\t\t\tbreakdown.user +\n\t\t\tbreakdown.assistantText +\n\t\t\tbreakdown.thinking +\n\t\t\tbreakdown.toolCalls +\n\t\t\tbreakdown.toolResults +\n\t\t\tbreakdown.summaries;\n\n\t\tconst stats = session.getSessionStats();\n\t\tawait ctx.reply(\n\t\t\t[\n\t\t\t\t`Context: ${tokens.toLocaleString(\"en-US\")} / ${contextWindow.toLocaleString(\"en-US\")} tokens (${Math.round(percent)}%)`,\n\t\t\t\t` system ${breakdown.systemPrompt.toLocaleString(\"en-US\")} · tools ${breakdown.toolDefinitions.toLocaleString(\"en-US\")} · messages ${messageTokens.toLocaleString(\"en-US\")}`,\n\t\t\t\t`Session: ${stats.totalMessages} messages · ${stats.userMessages} turns · model ${model.provider}/${model.id}`,\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t},\n};\n\nconst swarmCommand: ChatCommand = {\n\tname: \"swarm\",\n\tdescription: \"decompose a task across parallel subagents\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst task = ctx.args.trim();\n\t\tif (!task) {\n\t\t\tawait ctx.reply(\"Usage: /swarm <task> — decomposes the task across parallel subagents.\");\n\t\t\treturn true;\n\t\t}\n\t\tctx.event.text = buildSwarmPrompt(task);\n\t\treturn false;\n\t},\n};\n\nconst compactCommand: ChatCommand = {\n\tname: \"compact\",\n\tdescription: \"summarize and compress session context\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst instructions = ctx.args.trim() || undefined;\n\t\ttry {\n\t\t\tconst result = await session.compact(instructions);\n\t\t\tconst before = result.tokensBefore.toLocaleString(\"en-US\");\n\t\t\tconst after =\n\t\t\t\tresult.estimatedTokensAfter !== undefined\n\t\t\t\t\t? `~${result.estimatedTokensAfter.toLocaleString(\"en-US\")}`\n\t\t\t\t\t: \"~?\";\n\t\t\tawait ctx.reply(`Compacted: ${before} → ${after} tokens.`);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tif (\n\t\t\t\tmessage.includes(\"Nothing to compact\") ||\n\t\t\t\tmessage.includes(\"Already compacted\") ||\n\t\t\t\tmessage.includes(\"No model selected\")\n\t\t\t) {\n\t\t\t\tawait ctx.reply(message);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t},\n};\n\nconst thinkingCommand: ChatCommand = {\n\tname: \"thinking\",\n\taliases: [\"effort\", \"reasoning\"],\n\tdescription: \"get or set the thinking level\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst levels = session.getAvailableThinkingLevels();\n\t\tif (!session.supportsThinking()) {\n\t\t\tawait ctx.reply(\n\t\t\t\t`Model ${session.model?.provider ?? \"none\"}/${session.model?.id ?? \"none\"} doesn't support thinking.`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst arg = ctx.args.trim().toLowerCase() as ThinkingLevel;\n\t\tif (!arg) {\n\t\t\tconst current = session.thinkingLevel;\n\t\t\tconst items: PickerItem[] = levels.map((level) => ({\n\t\t\t\tlabel: `${level === current ? \"✓ \" : \"\"}${level}`,\n\t\t\t\tvalue: level,\n\t\t\t}));\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"thinking\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems,\n\t\t\t\t\tperPage: levels.length,\n\t\t\t\t\ttitle: \"Pick a thinking level\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tconst level = item.value as ThinkingLevel;\n\t\t\t\t\t\tsession.setThinkingLevel(level);\n\t\t\t\t\t\treturn { done: true, text: `☾ Thinking → ${level}` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tawait ctx.reply(`Level: ${current} — available: ${levels.join(\", \")}`);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!levels.includes(arg)) {\n\t\t\tawait ctx.reply(`Invalid level. Available: ${levels.join(\", \")}`);\n\t\t\treturn;\n\t\t}\n\t\tsession.setThinkingLevel(arg);\n\t\tawait ctx.reply(`Thinking → ${arg}`);\n\t},\n};\n\nexport const CHAT_COMMANDS: ChatCommand[] = [\n\tnewCommand,\n\tundoCommand,\n\tredoCommand,\n\tstopCommand,\n\tstatusCommand,\n\twhoamiCommand,\n\thelpCommand,\n\tmodelCommand,\n\tsessionsCommand,\n\ttitleCommand,\n\tcontextCommand,\n\tswarmCommand,\n\tcompactCommand,\n\tthinkingCommand,\n];\n\n/** Platform menu specs (e.g. Telegram setMyCommands): canonical names only, aliases skipped. */\nexport function botCommandSpecs(): { name: string; description: string }[] {\n\treturn CHAT_COMMANDS.map((c) => ({ name: c.name, description: c.description }));\n}\n\nexport function formatHelpText(): string {\n\tconst lines = [\"lunR gateway commands:\"];\n\tfor (const cmd of CHAT_COMMANDS) {\n\t\tconst names = [cmd.name, ...(cmd.aliases ?? [])].map((a) => `/${a}`).join(\" | \");\n\t\tlines.push(`${names} — ${cmd.description}`);\n\t}\n\tlines.push(\"\");\n\tlines.push(\"Tap to pick: /model, /thinking, /sessions (run without args to see buttons).\");\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Run a chat command with busy/session guards and error → \"⚠ one-line\".\n * Returns true when the event was consumed by the command, false when the\n * router should continue (e.g. /swarm mutates the event and wants a normal turn).\n */\nexport async function runChatCommand(cmd: ChatCommand, ctx: ChatCommandContext): Promise<boolean> {\n\tif (!cmd.bypassBusy && ctx.bridge.getStatus(ctx.key).busy) {\n\t\tawait ctx.reply(\"⏳ busy — /stop first or wait\");\n\t\treturn true;\n\t}\n\tif (cmd.needsSession) {\n\t\tconst session = await ctx.bridge.getSession(ctx.key);\n\t\tif (!session) {\n\t\t\tawait ctx.reply(\"No session yet — send a message first.\");\n\t\t\treturn true;\n\t\t}\n\t\tctx.session = session;\n\t}\n\ttry {\n\t\tconst consumed = await cmd.handler(ctx);\n\t\treturn consumed ?? true;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tawait ctx.adapter.send(ctx.event.source.chatId, `⚠ ${oneLine(message)}`, {\n\t\t\treplyTo: ctx.event.messageId,\n\t\t\tthreadId: ctx.event.source.threadId,\n\t\t});\n\t\treturn true;\n\t}\n}\n\n/** Reply helper used by the router to build the ChatCommandContext. */\nexport async function sendCommandReply(adapter: PlatformAdapter, event: MessageEvent, text: string): Promise<void> {\n\tawait adapter.send(event.source.chatId, prefixLines(text, \"☾ \"), {\n\t\treplyTo: event.messageId,\n\t\tthreadId: event.source.threadId,\n\t});\n}\n"]}
@@ -158,7 +158,8 @@ const newCommand = {
158
158
  name: "new",
159
159
  aliases: ["reset"],
160
160
  description: "start a fresh session for this chat",
161
- bypassBusy: false,
161
+ // lunr: busy-session /new must bypass the guard (same as /stop).
162
+ bypassBusy: true,
162
163
  needsSession: false,
163
164
  async handler(ctx) {
164
165
  await ctx.bridge.reset(ctx.key);
@@ -1 +1 @@
1
- {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/gateway/commands.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway chat command registry.\n *\n * Every command is a small headless handler over AgentSession. Router.ts keeps\n * platform gating/authorization and delegates to runChatCommand for busy/session\n * guards, error wrapping, and /help generation.\n */\n\nimport type { ThinkingLevel } from \"@ashx-j/lunr-agent\";\nimport type { Model } from \"@ashx-j/lunr-ai/compat\";\nimport { computeContextBreakdown } from \"../core/context-breakdown.ts\";\nimport type { ToolDefinition } from \"../core/extensions/types.ts\";\nimport { findExactModelReferenceMatch } from \"../core/model-resolver.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport type { SessionInfo } from \"../core/session-manager.ts\";\nimport { SessionManager } from \"../core/session-manager.ts\";\nimport { buildSwarmPrompt } from \"../core/swarm.ts\";\nimport type { BridgeSession } from \"./agent-bridge.ts\";\nimport { createPicker, type PickerItem } from \"./buttons.ts\";\nimport type { BridgeLike } from \"./router.ts\";\nimport type { MessageEvent, PlatformAdapter } from \"./types.ts\";\n\nconst MODEL_CACHE_TTL_MS = 5 * 60 * 1000;\nconst SESSIONS_CACHE_TTL_MS = 5 * 60 * 1000;\nconst MAX_MODEL_LIST_LINES = 15;\nconst PROVIDER_PER_PAGE = 10;\nconst _MODEL_PER_PAGE = 8;\nconst MAX_MODELS_PER_PROVIDER = 50;\nconst MODEL_ID_MAX = 38;\nconst MODEL_CACHE_CAP = 100;\nconst SESSIONS_CACHE_CAP = 100;\n\ninterface ModelCacheEntry {\n\tmodels: Model<any>[];\n\texpires: number;\n}\n\ninterface SessionsCacheEntry {\n\tsessions: SessionInfo[];\n\texpires: number;\n}\n\nconst modelCache = new Map<string, ModelCacheEntry>();\nconst sessionsCache = new Map<string, SessionsCacheEntry>();\n\nfunction evictLRU<K, V>(map: Map<K, V>, cap: number): void {\n\twhile (map.size > cap) {\n\t\tconst first = map.keys().next().value;\n\t\tif (first !== undefined) map.delete(first);\n\t}\n}\n\nfunction getCachedModels(key: string): Model<any>[] | undefined {\n\tconst cached = modelCache.get(key);\n\tif (!cached) return undefined;\n\tif (Date.now() > cached.expires) {\n\t\tmodelCache.delete(key);\n\t\treturn undefined;\n\t}\n\t// LRU refresh.\n\tmodelCache.delete(key);\n\tmodelCache.set(key, cached);\n\treturn cached.models;\n}\n\nfunction setCachedModels(key: string, models: Model<any>[]): void {\n\tmodelCache.set(key, { models, expires: Date.now() + MODEL_CACHE_TTL_MS });\n\tevictLRU(modelCache, MODEL_CACHE_CAP);\n}\n\nfunction getCachedSessions(key: string): SessionInfo[] | undefined {\n\tconst cached = sessionsCache.get(key);\n\tif (!cached) return undefined;\n\tif (Date.now() > cached.expires) {\n\t\tsessionsCache.delete(key);\n\t\treturn undefined;\n\t}\n\tsessionsCache.delete(key);\n\tsessionsCache.set(key, cached);\n\treturn cached.sessions;\n}\n\nfunction setCachedSessions(key: string, sessions: SessionInfo[]): void {\n\tsessionsCache.set(key, { sessions, expires: Date.now() + SESSIONS_CACHE_TTL_MS });\n\tevictLRU(sessionsCache, SESSIONS_CACHE_CAP);\n}\n\nexport interface ChatCommandContext {\n\tevent: MessageEvent;\n\tkey: string;\n\tadapter: PlatformAdapter;\n\tbridge: BridgeLike;\n\tsession?: BridgeSession;\n\treply(text: string): Promise<void>;\n\targs: string;\n}\n\nexport interface ChatCommand {\n\tname: string;\n\taliases?: string[];\n\tdescription: string;\n\tbypassBusy: boolean;\n\tneedsSession: boolean;\n\t// biome-ignore lint/suspicious/noConfusingVoidType: handlers may return nothing; runChatCommand treats undefined as consumed.\n\thandler(ctx: ChatCommandContext): Promise<boolean | void>;\n}\n\nfunction oneLine(text: string): string {\n\treturn text.replace(/\\s+/g, \" \").trim().slice(0, 200);\n}\n\nfunction prefixLines(text: string, prefix: string): string {\n\treturn text\n\t\t.split(\"\\n\")\n\t\t.map((line, index) => (index === 0 ? prefix : \" \") + line)\n\t\t.join(\"\\n\");\n}\n\nfunction truncate(text: string, max: number): string {\n\tif (text.length <= max) return text;\n\treturn `${text.slice(0, max - 1)}…`;\n}\n\nfunction formatRelativeTime(date: Date): string {\n\tconst seconds = Math.max(0, Math.floor((Date.now() - date.getTime()) / 1000));\n\tif (seconds < 60) return `${seconds}s ago`;\n\tconst minutes = Math.floor(seconds / 60);\n\tif (minutes < 60) return `${minutes}m ago`;\n\tconst hours = Math.floor(minutes / 60);\n\tif (hours < 24) return `${hours}h ago`;\n\tconst days = Math.floor(hours / 24);\n\treturn `${days}d ago`;\n}\n\nfunction formatModelList(models: Model<any>[], maxLines = MAX_MODEL_LIST_LINES): string[] {\n\tif (models.length === 0) return [\"No models available.\"];\n\tconst byProvider = new Map<string, Model<any>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\tconst providers = [...byProvider.keys()].sort();\n\tconst lines: string[] = [];\n\tlet index = 1;\n\tfor (const provider of providers) {\n\t\tconst list = (byProvider.get(provider) ?? []).sort((a, b) => a.id.localeCompare(b.id));\n\t\tfor (const model of list) {\n\t\t\tlines.push(`${index}) ${provider}/${model.id}`);\n\t\t\tindex++;\n\t\t}\n\t}\n\tif (lines.length <= maxLines) return lines;\n\treturn [\n\t\t`${models.length} models across ${providers.length} providers.`,\n\t\t\"Use /model <provider/id> or /model <query>.\",\n\t\t...providers.map((p) => `- ${p}`),\n\t];\n}\n\nfunction groupModelsByProvider(models: Model<any>[]): Map<string, Model<any>[]> {\n\tconst byProvider = new Map<string, Model<any>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\tfor (const list of byProvider.values()) {\n\t\tlist.sort((a, b) => a.id.localeCompare(b.id));\n\t}\n\treturn byProvider;\n}\n\nfunction truncateModelId(id: string): string {\n\tconst lastSlash = id.lastIndexOf(\"/\");\n\tconst name = lastSlash >= 0 ? id.slice(lastSlash + 1) : id;\n\treturn truncate(name, MODEL_ID_MAX);\n}\n\nasync function refreshAndCacheModels(session: BridgeSession, key: string): Promise<Model<any>[]> {\n\tconst runtime: ModelRuntime = session.modelRuntime;\n\tawait runtime.refresh();\n\tconst models = [...(await runtime.getAvailable())].sort(\n\t\t(a, b) => a.provider.localeCompare(b.provider) || a.id.localeCompare(b.id),\n\t);\n\tsetCachedModels(key, models);\n\treturn models;\n}\n\nasync function setModelAndReply(ctx: ChatCommandContext, model: Model<any>): Promise<void> {\n\tconst session = ctx.session!;\n\ttry {\n\t\tawait session.setModel(model);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"No API key\")) {\n\t\t\tawait ctx.reply(`No API key for ${model.provider}.`);\n\t\t\treturn;\n\t\t}\n\t\tthrow error;\n\t}\n\tawait ctx.reply(`Model → ${model.provider}/${model.id} (thinking level re-clamped to ${session.thinkingLevel})`);\n}\n\nconst newCommand: ChatCommand = {\n\tname: \"new\",\n\taliases: [\"reset\"],\n\tdescription: \"start a fresh session for this chat\",\n\tbypassBusy: false,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.reset(ctx.key);\n\t\tawait ctx.reply(\"Session reset — next message starts a fresh session.\");\n\t},\n};\n\nconst undoCommand: ChatCommand = {\n\tname: \"undo\",\n\tdescription: \"rewind the session tree before the last user turn\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst { userText } = await ctx.bridge.undo(ctx.key);\n\t\tconst text = userText ? ` \"${truncate(userText, 60)}\"` : \"\";\n\t\tawait ctx.reply(`Undone — rewound before${text}. /redo to restore.`);\n\t},\n};\n\nconst redoCommand: ChatCommand = {\n\tname: \"redo\",\n\tdescription: \"restore the last undone turn\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.redo(ctx.key);\n\t\tawait ctx.reply(\"Redone.\");\n\t},\n};\n\nconst stopCommand: ChatCommand = {\n\tname: \"stop\",\n\tdescription: \"abort the running turn\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.abort(ctx.key);\n\t\tawait ctx.reply(\"Stopped.\");\n\t},\n};\n\nconst statusCommand: ChatCommand = {\n\tname: \"status\",\n\tdescription: \"session status\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tconst status = ctx.bridge.getStatus(ctx.key);\n\t\tconst age = status.createdAt\n\t\t\t? `${Math.max(0, Math.round((Date.now() - Date.parse(status.createdAt)) / 1000))}s`\n\t\t\t: \"no session yet\";\n\t\tawait ctx.reply(\n\t\t\t`${ctx.event.source.platform} · session age ${age} · ${status.busy ? \"busy\" : \"idle\"} · queue ${status.queueDepth}`,\n\t\t);\n\t},\n};\n\nconst whoamiCommand: ChatCommand = {\n\tname: \"whoami\",\n\tdescription: \"your ids and session key\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tconst { userId, chatId } = ctx.event.source;\n\t\tawait ctx.reply(`userId: ${userId}\\nchatId: ${chatId}\\nkey: ${ctx.key}`);\n\t},\n};\n\nconst helpCommand: ChatCommand = {\n\tname: \"help\",\n\tdescription: \"this message\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.reply(formatHelpText());\n\t},\n};\n\nconst modelCommand: ChatCommand = {\n\tname: \"model\",\n\tdescription: \"list or switch models\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst arg = ctx.args.trim();\n\t\tif (!arg) {\n\t\t\tconst cached = getCachedModels(ctx.key);\n\t\t\tconst models = cached ?? (await refreshAndCacheModels(session, ctx.key));\n\t\t\tconst byProvider = groupModelsByProvider(models);\n\t\t\tconst current = session.model;\n\t\t\tconst currentRef = current ? `${current.provider}/${current.id}` : \"\";\n\t\t\tconst providers = [...byProvider.keys()].sort();\n\t\t\tconst providerItems: PickerItem[] = providers.map((provider) => ({\n\t\t\t\tlabel: `${current?.provider === provider ? \"✓ \" : \"\"}${provider} (${byProvider.get(provider)?.length ?? 0})`,\n\t\t\t\tvalue: provider,\n\t\t\t}));\n\n\t\t\tconst pickerOpts = {\n\t\t\t\treplyTo: ctx.event.messageId,\n\t\t\t\tthreadId: ctx.event.source.threadId,\n\t\t\t};\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"model\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems: providerItems,\n\t\t\t\t\tperPage: PROVIDER_PER_PAGE,\n\t\t\t\t\ttitle: \"Pick a model provider\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tif (item.value === \"__back__\") {\n\t\t\t\t\t\t\treturn { done: false, items: providerItems, title: \"Pick a model provider\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!item.value.includes(\"/\")) {\n\t\t\t\t\t\t\tconst provider = item.value;\n\t\t\t\t\t\t\tconst list = byProvider.get(provider) ?? [];\n\t\t\t\t\t\t\tconst capped = list.slice(0, MAX_MODELS_PER_PROVIDER);\n\t\t\t\t\t\t\tconst note =\n\t\t\t\t\t\t\t\tlist.length > MAX_MODELS_PER_PROVIDER\n\t\t\t\t\t\t\t\t\t? ` — ${list.length - MAX_MODELS_PER_PROVIDER} more available`\n\t\t\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\t\tconst modelItems: PickerItem[] = [];\n\t\t\t\t\t\t\tfor (const m of capped) {\n\t\t\t\t\t\t\t\tconst ref = `${provider}/${m.id}`;\n\t\t\t\t\t\t\t\tmodelItems.push({\n\t\t\t\t\t\t\t\t\tlabel: `${currentRef === ref ? \"✓ \" : \"\"}${truncateModelId(m.id)}`,\n\t\t\t\t\t\t\t\t\tvalue: ref,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmodelItems.unshift({ label: \"◀ Back\", value: \"__back__\" });\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tdone: false,\n\t\t\t\t\t\t\t\titems: modelItems,\n\t\t\t\t\t\t\t\ttitle: `${provider} (${list.length})${note}`,\n\t\t\t\t\t\t\t\tbreadcrumbs: provider,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst exact = findExactModelReferenceMatch(item.value, models);\n\t\t\t\t\t\tif (!exact) {\n\t\t\t\t\t\t\treturn { done: true, text: \"That model is no longer available.\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait session.setModel(exact);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\t\tif (message.includes(\"No API key\")) {\n\t\t\t\t\t\t\t\treturn { done: true, text: `No API key for ${exact.provider}.` };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tdone: true,\n\t\t\t\t\t\t\ttext: `☾ Model → ${exact.provider}/${exact.id} (thinking level re-clamped to ${session.thinkingLevel})`,\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpickerOpts,\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tconst currentText = `${current?.provider ?? \"none\"}/${current?.id ?? \"none\"}`;\n\t\t\t\tconst lines = formatModelList(models);\n\t\t\t\tawait ctx.reply([`Current: ${currentText}`, ...lines, \"Use /model <n> or /model <provider/id>\"].join(\"\\n\"));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst numeric = Number.parseInt(arg, 10);\n\t\tif (!Number.isNaN(numeric) && /^\\d+$/.test(arg)) {\n\t\t\tconst cached = getCachedModels(ctx.key);\n\t\t\tif (!cached || numeric < 1 || numeric > cached.length) {\n\t\t\t\tawait ctx.reply(\"Invalid selection — run /model to see the current list.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait setModelAndReply(ctx, cached[numeric - 1]);\n\t\t\treturn;\n\t\t}\n\n\t\tconst models = getCachedModels(ctx.key) ?? (await refreshAndCacheModels(session, ctx.key));\n\t\tconst exact = findExactModelReferenceMatch(arg, models);\n\t\tif (exact) {\n\t\t\tawait setModelAndReply(ctx, exact);\n\t\t\treturn;\n\t\t}\n\n\t\tconst query = arg.toLowerCase();\n\t\tconst matches = models.filter(\n\t\t\t(m) =>\n\t\t\t\tm.id.toLowerCase().includes(query) ||\n\t\t\t\t`${m.provider}/${m.id}`.toLowerCase().includes(query) ||\n\t\t\t\t(m.name?.toLowerCase().includes(query) ?? false),\n\t\t);\n\t\tif (matches.length === 0) {\n\t\t\tawait ctx.reply(`No model matches \"${arg}\".`);\n\t\t\treturn;\n\t\t}\n\t\tif (matches.length > 1) {\n\t\t\tawait ctx.reply(formatModelList(matches).join(\"\\n\"));\n\t\t\treturn;\n\t\t}\n\t\tawait setModelAndReply(ctx, matches[0]);\n\t},\n};\n\nconst sessionsCommand: ChatCommand = {\n\tname: \"sessions\",\n\tdescription: \"list or switch sessions\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst arg = ctx.args.trim();\n\t\tconst currentFile = session.sessionManager?.getSessionFile();\n\n\t\tif (!arg) {\n\t\t\tconst all = await SessionManager.list(process.cwd());\n\t\t\tall.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\t\tconst sessions = all.slice(0, 10);\n\t\t\tsetCachedSessions(ctx.key, sessions);\n\t\t\tconst items: PickerItem[] = sessions.map((s) => ({\n\t\t\t\tlabel: `${s.path === currentFile ? \"☾ \" : \"\"}${s.name ? truncate(s.name, 30) : truncate(s.firstMessage, 30) || \"(empty)\"}`,\n\t\t\t\tvalue: s.path,\n\t\t\t}));\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"sessions\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems,\n\t\t\t\t\tperPage: 8,\n\t\t\t\t\ttitle: \"Pick a session\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tawait ctx.bridge.switchSession(ctx.key, item.value);\n\t\t\t\t\t\tconst switched = await ctx.bridge.getSession(ctx.key);\n\t\t\t\t\t\tconst name = switched?.sessionManager?.getSessionName() ?? \"unnamed\";\n\t\t\t\t\t\tconst count = switched?.sessionManager?.getEntries().length ?? 0;\n\t\t\t\t\t\treturn { done: true, text: `☾ Switched to \"${name}\" (${count} msgs). History continues here.` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tconst lines = sessions.map((s, i) => {\n\t\t\t\t\tconst marker = s.path === currentFile ? \" ☾\" : \"\";\n\t\t\t\t\tconst label = s.name ? truncate(s.name, 40) : truncate(s.firstMessage, 40) || \"(empty)\";\n\t\t\t\t\treturn `${i + 1}) ${label}${marker} · ${formatRelativeTime(s.modified)} · ${s.messageCount} msgs`;\n\t\t\t\t});\n\t\t\t\tawait ctx.reply(\n\t\t\t\t\t[`Sessions for ${process.cwd()}`, ...lines, \"Use /sessions <n> or /sessions <id-prefix>\"].join(\"\\n\"),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tlet path: string | undefined;\n\t\tconst numeric = Number.parseInt(arg, 10);\n\t\tconst cached = getCachedSessions(ctx.key);\n\t\tif (!Number.isNaN(numeric) && /^\\d+$/.test(arg)) {\n\t\t\tif (!cached || numeric < 1 || numeric > cached.length) {\n\t\t\t\tawait ctx.reply(\"Invalid selection — run /sessions to see the current list.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpath = cached[numeric - 1].path;\n\t\t} else {\n\t\t\tconst pool = cached ?? (await SessionManager.list(process.cwd()));\n\t\t\tconst matches = pool.filter((s) => s.id.startsWith(arg) || s.path.startsWith(arg));\n\t\t\tif (matches.length === 0) {\n\t\t\t\tawait ctx.reply(`No session matches \"${arg}\".`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (matches.length > 1) {\n\t\t\t\tawait ctx.reply(`Multiple sessions match \"${arg}\" — use a longer prefix.`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpath = matches[0].path;\n\t\t}\n\n\t\tif (!path) {\n\t\t\tawait ctx.reply(\"No session selected.\");\n\t\t\treturn;\n\t\t}\n\n\t\tawait ctx.bridge.switchSession(ctx.key, path);\n\t\tconst switched = await ctx.bridge.getSession(ctx.key);\n\t\tconst name = switched?.sessionManager?.getSessionName() ?? \"unnamed\";\n\t\tconst count = switched?.sessionManager?.getEntries().length ?? 0;\n\t\tawait ctx.reply(`Switched to \"${name}\" (${count} msgs). History continues here.`);\n\t},\n};\n\nconst titleCommand: ChatCommand = {\n\tname: \"title\",\n\taliases: [\"name\"],\n\tdescription: \"name the current session\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst name = ctx.args.trim();\n\t\tif (!name) {\n\t\t\tconst current = session.sessionManager?.getSessionName();\n\t\t\tawait ctx.reply(current ? `Session title: ${current}` : \"(unnamed)\");\n\t\t\treturn;\n\t\t}\n\t\tsession.setSessionName(name);\n\t\tconst readBack = session.sessionManager?.getSessionName() ?? name;\n\t\tawait ctx.reply(`Session titled \"${readBack}\".`);\n\t},\n};\n\nconst contextCommand: ChatCommand = {\n\tname: \"context\",\n\tdescription: \"estimated context window breakdown\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst model = session.model;\n\t\tif (!model) {\n\t\t\tawait ctx.reply(\"No model selected.\");\n\t\t\treturn;\n\t\t}\n\t\tconst contextWindow = model.contextWindow ?? 0;\n\t\tif (contextWindow <= 0) {\n\t\t\tawait ctx.reply(\"Context window unknown for this model.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst tools: ToolDefinition[] = session\n\t\t\t.getActiveToolNames()\n\t\t\t.map((name) => session.getToolDefinition(name))\n\t\t\t.filter((d): d is ToolDefinition => d !== undefined);\n\n\t\tconst breakdown = computeContextBreakdown({\n\t\t\tsystemPrompt: session.systemPrompt,\n\t\t\ttools,\n\t\t\tmessages: session.messages,\n\t\t\tcontextWindow,\n\t\t});\n\n\t\tconst usage = session.getContextUsage();\n\t\tconst tokens = usage?.tokens ?? breakdown.total;\n\t\tconst percent = usage?.percent ?? (contextWindow > 0 ? (tokens / contextWindow) * 100 : 0);\n\n\t\tconst messageTokens =\n\t\t\tbreakdown.user +\n\t\t\tbreakdown.assistantText +\n\t\t\tbreakdown.thinking +\n\t\t\tbreakdown.toolCalls +\n\t\t\tbreakdown.toolResults +\n\t\t\tbreakdown.summaries;\n\n\t\tconst stats = session.getSessionStats();\n\t\tawait ctx.reply(\n\t\t\t[\n\t\t\t\t`Context: ${tokens.toLocaleString(\"en-US\")} / ${contextWindow.toLocaleString(\"en-US\")} tokens (${Math.round(percent)}%)`,\n\t\t\t\t` system ${breakdown.systemPrompt.toLocaleString(\"en-US\")} · tools ${breakdown.toolDefinitions.toLocaleString(\"en-US\")} · messages ${messageTokens.toLocaleString(\"en-US\")}`,\n\t\t\t\t`Session: ${stats.totalMessages} messages · ${stats.userMessages} turns · model ${model.provider}/${model.id}`,\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t},\n};\n\nconst swarmCommand: ChatCommand = {\n\tname: \"swarm\",\n\tdescription: \"decompose a task across parallel subagents\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst task = ctx.args.trim();\n\t\tif (!task) {\n\t\t\tawait ctx.reply(\"Usage: /swarm <task> — decomposes the task across parallel subagents.\");\n\t\t\treturn true;\n\t\t}\n\t\tctx.event.text = buildSwarmPrompt(task);\n\t\treturn false;\n\t},\n};\n\nconst compactCommand: ChatCommand = {\n\tname: \"compact\",\n\tdescription: \"summarize and compress session context\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst instructions = ctx.args.trim() || undefined;\n\t\ttry {\n\t\t\tconst result = await session.compact(instructions);\n\t\t\tconst before = result.tokensBefore.toLocaleString(\"en-US\");\n\t\t\tconst after =\n\t\t\t\tresult.estimatedTokensAfter !== undefined\n\t\t\t\t\t? `~${result.estimatedTokensAfter.toLocaleString(\"en-US\")}`\n\t\t\t\t\t: \"~?\";\n\t\t\tawait ctx.reply(`Compacted: ${before} → ${after} tokens.`);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tif (\n\t\t\t\tmessage.includes(\"Nothing to compact\") ||\n\t\t\t\tmessage.includes(\"Already compacted\") ||\n\t\t\t\tmessage.includes(\"No model selected\")\n\t\t\t) {\n\t\t\t\tawait ctx.reply(message);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t},\n};\n\nconst thinkingCommand: ChatCommand = {\n\tname: \"thinking\",\n\taliases: [\"effort\", \"reasoning\"],\n\tdescription: \"get or set the thinking level\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst levels = session.getAvailableThinkingLevels();\n\t\tif (!session.supportsThinking()) {\n\t\t\tawait ctx.reply(\n\t\t\t\t`Model ${session.model?.provider ?? \"none\"}/${session.model?.id ?? \"none\"} doesn't support thinking.`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst arg = ctx.args.trim().toLowerCase() as ThinkingLevel;\n\t\tif (!arg) {\n\t\t\tconst current = session.thinkingLevel;\n\t\t\tconst items: PickerItem[] = levels.map((level) => ({\n\t\t\t\tlabel: `${level === current ? \"✓ \" : \"\"}${level}`,\n\t\t\t\tvalue: level,\n\t\t\t}));\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"thinking\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems,\n\t\t\t\t\tperPage: levels.length,\n\t\t\t\t\ttitle: \"Pick a thinking level\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tconst level = item.value as ThinkingLevel;\n\t\t\t\t\t\tsession.setThinkingLevel(level);\n\t\t\t\t\t\treturn { done: true, text: `☾ Thinking → ${level}` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tawait ctx.reply(`Level: ${current} — available: ${levels.join(\", \")}`);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!levels.includes(arg)) {\n\t\t\tawait ctx.reply(`Invalid level. Available: ${levels.join(\", \")}`);\n\t\t\treturn;\n\t\t}\n\t\tsession.setThinkingLevel(arg);\n\t\tawait ctx.reply(`Thinking → ${arg}`);\n\t},\n};\n\nexport const CHAT_COMMANDS: ChatCommand[] = [\n\tnewCommand,\n\tundoCommand,\n\tredoCommand,\n\tstopCommand,\n\tstatusCommand,\n\twhoamiCommand,\n\thelpCommand,\n\tmodelCommand,\n\tsessionsCommand,\n\ttitleCommand,\n\tcontextCommand,\n\tswarmCommand,\n\tcompactCommand,\n\tthinkingCommand,\n];\n\n/** Platform menu specs (e.g. Telegram setMyCommands): canonical names only, aliases skipped. */\nexport function botCommandSpecs(): { name: string; description: string }[] {\n\treturn CHAT_COMMANDS.map((c) => ({ name: c.name, description: c.description }));\n}\n\nexport function formatHelpText(): string {\n\tconst lines = [\"lunR gateway commands:\"];\n\tfor (const cmd of CHAT_COMMANDS) {\n\t\tconst names = [cmd.name, ...(cmd.aliases ?? [])].map((a) => `/${a}`).join(\" | \");\n\t\tlines.push(`${names} — ${cmd.description}`);\n\t}\n\tlines.push(\"\");\n\tlines.push(\"Tap to pick: /model, /thinking, /sessions (run without args to see buttons).\");\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Run a chat command with busy/session guards and error → \"⚠ one-line\".\n * Returns true when the event was consumed by the command, false when the\n * router should continue (e.g. /swarm mutates the event and wants a normal turn).\n */\nexport async function runChatCommand(cmd: ChatCommand, ctx: ChatCommandContext): Promise<boolean> {\n\tif (!cmd.bypassBusy && ctx.bridge.getStatus(ctx.key).busy) {\n\t\tawait ctx.reply(\"⏳ busy — /stop first or wait\");\n\t\treturn true;\n\t}\n\tif (cmd.needsSession) {\n\t\tconst session = await ctx.bridge.getSession(ctx.key);\n\t\tif (!session) {\n\t\t\tawait ctx.reply(\"No session yet — send a message first.\");\n\t\t\treturn true;\n\t\t}\n\t\tctx.session = session;\n\t}\n\ttry {\n\t\tconst consumed = await cmd.handler(ctx);\n\t\treturn consumed ?? true;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tawait ctx.adapter.send(ctx.event.source.chatId, `⚠ ${oneLine(message)}`, {\n\t\t\treplyTo: ctx.event.messageId,\n\t\t\tthreadId: ctx.event.source.threadId,\n\t\t});\n\t\treturn true;\n\t}\n}\n\n/** Reply helper used by the router to build the ChatCommandContext. */\nexport async function sendCommandReply(adapter: PlatformAdapter, event: MessageEvent, text: string): Promise<void> {\n\tawait adapter.send(event.source.chatId, prefixLines(text, \"☾ \"), {\n\t\treplyTo: event.messageId,\n\t\tthreadId: event.source.threadId,\n\t});\n}\n"]}
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/gateway/commands.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: gateway chat command registry.\n *\n * Every command is a small headless handler over AgentSession. Router.ts keeps\n * platform gating/authorization and delegates to runChatCommand for busy/session\n * guards, error wrapping, and /help generation.\n */\n\nimport type { ThinkingLevel } from \"@ashx-j/lunr-agent\";\nimport type { Model } from \"@ashx-j/lunr-ai/compat\";\nimport { computeContextBreakdown } from \"../core/context-breakdown.ts\";\nimport type { ToolDefinition } from \"../core/extensions/types.ts\";\nimport { findExactModelReferenceMatch } from \"../core/model-resolver.ts\";\nimport type { ModelRuntime } from \"../core/model-runtime.ts\";\nimport type { SessionInfo } from \"../core/session-manager.ts\";\nimport { SessionManager } from \"../core/session-manager.ts\";\nimport { buildSwarmPrompt } from \"../core/swarm.ts\";\nimport type { BridgeSession } from \"./agent-bridge.ts\";\nimport { createPicker, type PickerItem } from \"./buttons.ts\";\nimport type { BridgeLike } from \"./router.ts\";\nimport type { MessageEvent, PlatformAdapter } from \"./types.ts\";\n\nconst MODEL_CACHE_TTL_MS = 5 * 60 * 1000;\nconst SESSIONS_CACHE_TTL_MS = 5 * 60 * 1000;\nconst MAX_MODEL_LIST_LINES = 15;\nconst PROVIDER_PER_PAGE = 10;\nconst _MODEL_PER_PAGE = 8;\nconst MAX_MODELS_PER_PROVIDER = 50;\nconst MODEL_ID_MAX = 38;\nconst MODEL_CACHE_CAP = 100;\nconst SESSIONS_CACHE_CAP = 100;\n\ninterface ModelCacheEntry {\n\tmodels: Model<any>[];\n\texpires: number;\n}\n\ninterface SessionsCacheEntry {\n\tsessions: SessionInfo[];\n\texpires: number;\n}\n\nconst modelCache = new Map<string, ModelCacheEntry>();\nconst sessionsCache = new Map<string, SessionsCacheEntry>();\n\nfunction evictLRU<K, V>(map: Map<K, V>, cap: number): void {\n\twhile (map.size > cap) {\n\t\tconst first = map.keys().next().value;\n\t\tif (first !== undefined) map.delete(first);\n\t}\n}\n\nfunction getCachedModels(key: string): Model<any>[] | undefined {\n\tconst cached = modelCache.get(key);\n\tif (!cached) return undefined;\n\tif (Date.now() > cached.expires) {\n\t\tmodelCache.delete(key);\n\t\treturn undefined;\n\t}\n\t// LRU refresh.\n\tmodelCache.delete(key);\n\tmodelCache.set(key, cached);\n\treturn cached.models;\n}\n\nfunction setCachedModels(key: string, models: Model<any>[]): void {\n\tmodelCache.set(key, { models, expires: Date.now() + MODEL_CACHE_TTL_MS });\n\tevictLRU(modelCache, MODEL_CACHE_CAP);\n}\n\nfunction getCachedSessions(key: string): SessionInfo[] | undefined {\n\tconst cached = sessionsCache.get(key);\n\tif (!cached) return undefined;\n\tif (Date.now() > cached.expires) {\n\t\tsessionsCache.delete(key);\n\t\treturn undefined;\n\t}\n\tsessionsCache.delete(key);\n\tsessionsCache.set(key, cached);\n\treturn cached.sessions;\n}\n\nfunction setCachedSessions(key: string, sessions: SessionInfo[]): void {\n\tsessionsCache.set(key, { sessions, expires: Date.now() + SESSIONS_CACHE_TTL_MS });\n\tevictLRU(sessionsCache, SESSIONS_CACHE_CAP);\n}\n\nexport interface ChatCommandContext {\n\tevent: MessageEvent;\n\tkey: string;\n\tadapter: PlatformAdapter;\n\tbridge: BridgeLike;\n\tsession?: BridgeSession;\n\treply(text: string): Promise<void>;\n\targs: string;\n}\n\nexport interface ChatCommand {\n\tname: string;\n\taliases?: string[];\n\tdescription: string;\n\tbypassBusy: boolean;\n\tneedsSession: boolean;\n\t// biome-ignore lint/suspicious/noConfusingVoidType: handlers may return nothing; runChatCommand treats undefined as consumed.\n\thandler(ctx: ChatCommandContext): Promise<boolean | void>;\n}\n\nfunction oneLine(text: string): string {\n\treturn text.replace(/\\s+/g, \" \").trim().slice(0, 200);\n}\n\nfunction prefixLines(text: string, prefix: string): string {\n\treturn text\n\t\t.split(\"\\n\")\n\t\t.map((line, index) => (index === 0 ? prefix : \" \") + line)\n\t\t.join(\"\\n\");\n}\n\nfunction truncate(text: string, max: number): string {\n\tif (text.length <= max) return text;\n\treturn `${text.slice(0, max - 1)}…`;\n}\n\nfunction formatRelativeTime(date: Date): string {\n\tconst seconds = Math.max(0, Math.floor((Date.now() - date.getTime()) / 1000));\n\tif (seconds < 60) return `${seconds}s ago`;\n\tconst minutes = Math.floor(seconds / 60);\n\tif (minutes < 60) return `${minutes}m ago`;\n\tconst hours = Math.floor(minutes / 60);\n\tif (hours < 24) return `${hours}h ago`;\n\tconst days = Math.floor(hours / 24);\n\treturn `${days}d ago`;\n}\n\nfunction formatModelList(models: Model<any>[], maxLines = MAX_MODEL_LIST_LINES): string[] {\n\tif (models.length === 0) return [\"No models available.\"];\n\tconst byProvider = new Map<string, Model<any>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\tconst providers = [...byProvider.keys()].sort();\n\tconst lines: string[] = [];\n\tlet index = 1;\n\tfor (const provider of providers) {\n\t\tconst list = (byProvider.get(provider) ?? []).sort((a, b) => a.id.localeCompare(b.id));\n\t\tfor (const model of list) {\n\t\t\tlines.push(`${index}) ${provider}/${model.id}`);\n\t\t\tindex++;\n\t\t}\n\t}\n\tif (lines.length <= maxLines) return lines;\n\treturn [\n\t\t`${models.length} models across ${providers.length} providers.`,\n\t\t\"Use /model <provider/id> or /model <query>.\",\n\t\t...providers.map((p) => `- ${p}`),\n\t];\n}\n\nfunction groupModelsByProvider(models: Model<any>[]): Map<string, Model<any>[]> {\n\tconst byProvider = new Map<string, Model<any>[]>();\n\tfor (const model of models) {\n\t\tconst list = byProvider.get(model.provider) ?? [];\n\t\tlist.push(model);\n\t\tbyProvider.set(model.provider, list);\n\t}\n\tfor (const list of byProvider.values()) {\n\t\tlist.sort((a, b) => a.id.localeCompare(b.id));\n\t}\n\treturn byProvider;\n}\n\nfunction truncateModelId(id: string): string {\n\tconst lastSlash = id.lastIndexOf(\"/\");\n\tconst name = lastSlash >= 0 ? id.slice(lastSlash + 1) : id;\n\treturn truncate(name, MODEL_ID_MAX);\n}\n\nasync function refreshAndCacheModels(session: BridgeSession, key: string): Promise<Model<any>[]> {\n\tconst runtime: ModelRuntime = session.modelRuntime;\n\tawait runtime.refresh();\n\tconst models = [...(await runtime.getAvailable())].sort(\n\t\t(a, b) => a.provider.localeCompare(b.provider) || a.id.localeCompare(b.id),\n\t);\n\tsetCachedModels(key, models);\n\treturn models;\n}\n\nasync function setModelAndReply(ctx: ChatCommandContext, model: Model<any>): Promise<void> {\n\tconst session = ctx.session!;\n\ttry {\n\t\tawait session.setModel(model);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"No API key\")) {\n\t\t\tawait ctx.reply(`No API key for ${model.provider}.`);\n\t\t\treturn;\n\t\t}\n\t\tthrow error;\n\t}\n\tawait ctx.reply(`Model → ${model.provider}/${model.id} (thinking level re-clamped to ${session.thinkingLevel})`);\n}\n\nconst newCommand: ChatCommand = {\n\tname: \"new\",\n\taliases: [\"reset\"],\n\tdescription: \"start a fresh session for this chat\",\n\t// lunr: busy-session /new must bypass the guard (same as /stop).\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.reset(ctx.key);\n\t\tawait ctx.reply(\"Session reset — next message starts a fresh session.\");\n\t},\n};\n\nconst undoCommand: ChatCommand = {\n\tname: \"undo\",\n\tdescription: \"rewind the session tree before the last user turn\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst { userText } = await ctx.bridge.undo(ctx.key);\n\t\tconst text = userText ? ` \"${truncate(userText, 60)}\"` : \"\";\n\t\tawait ctx.reply(`Undone — rewound before${text}. /redo to restore.`);\n\t},\n};\n\nconst redoCommand: ChatCommand = {\n\tname: \"redo\",\n\tdescription: \"restore the last undone turn\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.redo(ctx.key);\n\t\tawait ctx.reply(\"Redone.\");\n\t},\n};\n\nconst stopCommand: ChatCommand = {\n\tname: \"stop\",\n\tdescription: \"abort the running turn\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.bridge.abort(ctx.key);\n\t\tawait ctx.reply(\"Stopped.\");\n\t},\n};\n\nconst statusCommand: ChatCommand = {\n\tname: \"status\",\n\tdescription: \"session status\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tconst status = ctx.bridge.getStatus(ctx.key);\n\t\tconst age = status.createdAt\n\t\t\t? `${Math.max(0, Math.round((Date.now() - Date.parse(status.createdAt)) / 1000))}s`\n\t\t\t: \"no session yet\";\n\t\tawait ctx.reply(\n\t\t\t`${ctx.event.source.platform} · session age ${age} · ${status.busy ? \"busy\" : \"idle\"} · queue ${status.queueDepth}`,\n\t\t);\n\t},\n};\n\nconst whoamiCommand: ChatCommand = {\n\tname: \"whoami\",\n\tdescription: \"your ids and session key\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tconst { userId, chatId } = ctx.event.source;\n\t\tawait ctx.reply(`userId: ${userId}\\nchatId: ${chatId}\\nkey: ${ctx.key}`);\n\t},\n};\n\nconst helpCommand: ChatCommand = {\n\tname: \"help\",\n\tdescription: \"this message\",\n\tbypassBusy: true,\n\tneedsSession: false,\n\tasync handler(ctx) {\n\t\tawait ctx.reply(formatHelpText());\n\t},\n};\n\nconst modelCommand: ChatCommand = {\n\tname: \"model\",\n\tdescription: \"list or switch models\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst arg = ctx.args.trim();\n\t\tif (!arg) {\n\t\t\tconst cached = getCachedModels(ctx.key);\n\t\t\tconst models = cached ?? (await refreshAndCacheModels(session, ctx.key));\n\t\t\tconst byProvider = groupModelsByProvider(models);\n\t\t\tconst current = session.model;\n\t\t\tconst currentRef = current ? `${current.provider}/${current.id}` : \"\";\n\t\t\tconst providers = [...byProvider.keys()].sort();\n\t\t\tconst providerItems: PickerItem[] = providers.map((provider) => ({\n\t\t\t\tlabel: `${current?.provider === provider ? \"✓ \" : \"\"}${provider} (${byProvider.get(provider)?.length ?? 0})`,\n\t\t\t\tvalue: provider,\n\t\t\t}));\n\n\t\t\tconst pickerOpts = {\n\t\t\t\treplyTo: ctx.event.messageId,\n\t\t\t\tthreadId: ctx.event.source.threadId,\n\t\t\t};\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"model\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems: providerItems,\n\t\t\t\t\tperPage: PROVIDER_PER_PAGE,\n\t\t\t\t\ttitle: \"Pick a model provider\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tif (item.value === \"__back__\") {\n\t\t\t\t\t\t\treturn { done: false, items: providerItems, title: \"Pick a model provider\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!item.value.includes(\"/\")) {\n\t\t\t\t\t\t\tconst provider = item.value;\n\t\t\t\t\t\t\tconst list = byProvider.get(provider) ?? [];\n\t\t\t\t\t\t\tconst capped = list.slice(0, MAX_MODELS_PER_PROVIDER);\n\t\t\t\t\t\t\tconst note =\n\t\t\t\t\t\t\t\tlist.length > MAX_MODELS_PER_PROVIDER\n\t\t\t\t\t\t\t\t\t? ` — ${list.length - MAX_MODELS_PER_PROVIDER} more available`\n\t\t\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\t\tconst modelItems: PickerItem[] = [];\n\t\t\t\t\t\t\tfor (const m of capped) {\n\t\t\t\t\t\t\t\tconst ref = `${provider}/${m.id}`;\n\t\t\t\t\t\t\t\tmodelItems.push({\n\t\t\t\t\t\t\t\t\tlabel: `${currentRef === ref ? \"✓ \" : \"\"}${truncateModelId(m.id)}`,\n\t\t\t\t\t\t\t\t\tvalue: ref,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmodelItems.unshift({ label: \"◀ Back\", value: \"__back__\" });\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tdone: false,\n\t\t\t\t\t\t\t\titems: modelItems,\n\t\t\t\t\t\t\t\ttitle: `${provider} (${list.length})${note}`,\n\t\t\t\t\t\t\t\tbreadcrumbs: provider,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst exact = findExactModelReferenceMatch(item.value, models);\n\t\t\t\t\t\tif (!exact) {\n\t\t\t\t\t\t\treturn { done: true, text: \"That model is no longer available.\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait session.setModel(exact);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\t\tif (message.includes(\"No API key\")) {\n\t\t\t\t\t\t\t\treturn { done: true, text: `No API key for ${exact.provider}.` };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tdone: true,\n\t\t\t\t\t\t\ttext: `☾ Model → ${exact.provider}/${exact.id} (thinking level re-clamped to ${session.thinkingLevel})`,\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpickerOpts,\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tconst currentText = `${current?.provider ?? \"none\"}/${current?.id ?? \"none\"}`;\n\t\t\t\tconst lines = formatModelList(models);\n\t\t\t\tawait ctx.reply([`Current: ${currentText}`, ...lines, \"Use /model <n> or /model <provider/id>\"].join(\"\\n\"));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst numeric = Number.parseInt(arg, 10);\n\t\tif (!Number.isNaN(numeric) && /^\\d+$/.test(arg)) {\n\t\t\tconst cached = getCachedModels(ctx.key);\n\t\t\tif (!cached || numeric < 1 || numeric > cached.length) {\n\t\t\t\tawait ctx.reply(\"Invalid selection — run /model to see the current list.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait setModelAndReply(ctx, cached[numeric - 1]);\n\t\t\treturn;\n\t\t}\n\n\t\tconst models = getCachedModels(ctx.key) ?? (await refreshAndCacheModels(session, ctx.key));\n\t\tconst exact = findExactModelReferenceMatch(arg, models);\n\t\tif (exact) {\n\t\t\tawait setModelAndReply(ctx, exact);\n\t\t\treturn;\n\t\t}\n\n\t\tconst query = arg.toLowerCase();\n\t\tconst matches = models.filter(\n\t\t\t(m) =>\n\t\t\t\tm.id.toLowerCase().includes(query) ||\n\t\t\t\t`${m.provider}/${m.id}`.toLowerCase().includes(query) ||\n\t\t\t\t(m.name?.toLowerCase().includes(query) ?? false),\n\t\t);\n\t\tif (matches.length === 0) {\n\t\t\tawait ctx.reply(`No model matches \"${arg}\".`);\n\t\t\treturn;\n\t\t}\n\t\tif (matches.length > 1) {\n\t\t\tawait ctx.reply(formatModelList(matches).join(\"\\n\"));\n\t\t\treturn;\n\t\t}\n\t\tawait setModelAndReply(ctx, matches[0]);\n\t},\n};\n\nconst sessionsCommand: ChatCommand = {\n\tname: \"sessions\",\n\tdescription: \"list or switch sessions\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst arg = ctx.args.trim();\n\t\tconst currentFile = session.sessionManager?.getSessionFile();\n\n\t\tif (!arg) {\n\t\t\tconst all = await SessionManager.list(process.cwd());\n\t\t\tall.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\t\tconst sessions = all.slice(0, 10);\n\t\t\tsetCachedSessions(ctx.key, sessions);\n\t\t\tconst items: PickerItem[] = sessions.map((s) => ({\n\t\t\t\tlabel: `${s.path === currentFile ? \"☾ \" : \"\"}${s.name ? truncate(s.name, 30) : truncate(s.firstMessage, 30) || \"(empty)\"}`,\n\t\t\t\tvalue: s.path,\n\t\t\t}));\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"sessions\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems,\n\t\t\t\t\tperPage: 8,\n\t\t\t\t\ttitle: \"Pick a session\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tawait ctx.bridge.switchSession(ctx.key, item.value);\n\t\t\t\t\t\tconst switched = await ctx.bridge.getSession(ctx.key);\n\t\t\t\t\t\tconst name = switched?.sessionManager?.getSessionName() ?? \"unnamed\";\n\t\t\t\t\t\tconst count = switched?.sessionManager?.getEntries().length ?? 0;\n\t\t\t\t\t\treturn { done: true, text: `☾ Switched to \"${name}\" (${count} msgs). History continues here.` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tconst lines = sessions.map((s, i) => {\n\t\t\t\t\tconst marker = s.path === currentFile ? \" ☾\" : \"\";\n\t\t\t\t\tconst label = s.name ? truncate(s.name, 40) : truncate(s.firstMessage, 40) || \"(empty)\";\n\t\t\t\t\treturn `${i + 1}) ${label}${marker} · ${formatRelativeTime(s.modified)} · ${s.messageCount} msgs`;\n\t\t\t\t});\n\t\t\t\tawait ctx.reply(\n\t\t\t\t\t[`Sessions for ${process.cwd()}`, ...lines, \"Use /sessions <n> or /sessions <id-prefix>\"].join(\"\\n\"),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tlet path: string | undefined;\n\t\tconst numeric = Number.parseInt(arg, 10);\n\t\tconst cached = getCachedSessions(ctx.key);\n\t\tif (!Number.isNaN(numeric) && /^\\d+$/.test(arg)) {\n\t\t\tif (!cached || numeric < 1 || numeric > cached.length) {\n\t\t\t\tawait ctx.reply(\"Invalid selection — run /sessions to see the current list.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpath = cached[numeric - 1].path;\n\t\t} else {\n\t\t\tconst pool = cached ?? (await SessionManager.list(process.cwd()));\n\t\t\tconst matches = pool.filter((s) => s.id.startsWith(arg) || s.path.startsWith(arg));\n\t\t\tif (matches.length === 0) {\n\t\t\t\tawait ctx.reply(`No session matches \"${arg}\".`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (matches.length > 1) {\n\t\t\t\tawait ctx.reply(`Multiple sessions match \"${arg}\" — use a longer prefix.`);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpath = matches[0].path;\n\t\t}\n\n\t\tif (!path) {\n\t\t\tawait ctx.reply(\"No session selected.\");\n\t\t\treturn;\n\t\t}\n\n\t\tawait ctx.bridge.switchSession(ctx.key, path);\n\t\tconst switched = await ctx.bridge.getSession(ctx.key);\n\t\tconst name = switched?.sessionManager?.getSessionName() ?? \"unnamed\";\n\t\tconst count = switched?.sessionManager?.getEntries().length ?? 0;\n\t\tawait ctx.reply(`Switched to \"${name}\" (${count} msgs). History continues here.`);\n\t},\n};\n\nconst titleCommand: ChatCommand = {\n\tname: \"title\",\n\taliases: [\"name\"],\n\tdescription: \"name the current session\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst name = ctx.args.trim();\n\t\tif (!name) {\n\t\t\tconst current = session.sessionManager?.getSessionName();\n\t\t\tawait ctx.reply(current ? `Session title: ${current}` : \"(unnamed)\");\n\t\t\treturn;\n\t\t}\n\t\tsession.setSessionName(name);\n\t\tconst readBack = session.sessionManager?.getSessionName() ?? name;\n\t\tawait ctx.reply(`Session titled \"${readBack}\".`);\n\t},\n};\n\nconst contextCommand: ChatCommand = {\n\tname: \"context\",\n\tdescription: \"estimated context window breakdown\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst model = session.model;\n\t\tif (!model) {\n\t\t\tawait ctx.reply(\"No model selected.\");\n\t\t\treturn;\n\t\t}\n\t\tconst contextWindow = model.contextWindow ?? 0;\n\t\tif (contextWindow <= 0) {\n\t\t\tawait ctx.reply(\"Context window unknown for this model.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst tools: ToolDefinition[] = session\n\t\t\t.getActiveToolNames()\n\t\t\t.map((name) => session.getToolDefinition(name))\n\t\t\t.filter((d): d is ToolDefinition => d !== undefined);\n\n\t\tconst breakdown = computeContextBreakdown({\n\t\t\tsystemPrompt: session.systemPrompt,\n\t\t\ttools,\n\t\t\tmessages: session.messages,\n\t\t\tcontextWindow,\n\t\t});\n\n\t\tconst usage = session.getContextUsage();\n\t\tconst tokens = usage?.tokens ?? breakdown.total;\n\t\tconst percent = usage?.percent ?? (contextWindow > 0 ? (tokens / contextWindow) * 100 : 0);\n\n\t\tconst messageTokens =\n\t\t\tbreakdown.user +\n\t\t\tbreakdown.assistantText +\n\t\t\tbreakdown.thinking +\n\t\t\tbreakdown.toolCalls +\n\t\t\tbreakdown.toolResults +\n\t\t\tbreakdown.summaries;\n\n\t\tconst stats = session.getSessionStats();\n\t\tawait ctx.reply(\n\t\t\t[\n\t\t\t\t`Context: ${tokens.toLocaleString(\"en-US\")} / ${contextWindow.toLocaleString(\"en-US\")} tokens (${Math.round(percent)}%)`,\n\t\t\t\t` system ${breakdown.systemPrompt.toLocaleString(\"en-US\")} · tools ${breakdown.toolDefinitions.toLocaleString(\"en-US\")} · messages ${messageTokens.toLocaleString(\"en-US\")}`,\n\t\t\t\t`Session: ${stats.totalMessages} messages · ${stats.userMessages} turns · model ${model.provider}/${model.id}`,\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t},\n};\n\nconst swarmCommand: ChatCommand = {\n\tname: \"swarm\",\n\tdescription: \"decompose a task across parallel subagents\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst task = ctx.args.trim();\n\t\tif (!task) {\n\t\t\tawait ctx.reply(\"Usage: /swarm <task> — decomposes the task across parallel subagents.\");\n\t\t\treturn true;\n\t\t}\n\t\tctx.event.text = buildSwarmPrompt(task);\n\t\treturn false;\n\t},\n};\n\nconst compactCommand: ChatCommand = {\n\tname: \"compact\",\n\tdescription: \"summarize and compress session context\",\n\tbypassBusy: true,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst instructions = ctx.args.trim() || undefined;\n\t\ttry {\n\t\t\tconst result = await session.compact(instructions);\n\t\t\tconst before = result.tokensBefore.toLocaleString(\"en-US\");\n\t\t\tconst after =\n\t\t\t\tresult.estimatedTokensAfter !== undefined\n\t\t\t\t\t? `~${result.estimatedTokensAfter.toLocaleString(\"en-US\")}`\n\t\t\t\t\t: \"~?\";\n\t\t\tawait ctx.reply(`Compacted: ${before} → ${after} tokens.`);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tif (\n\t\t\t\tmessage.includes(\"Nothing to compact\") ||\n\t\t\t\tmessage.includes(\"Already compacted\") ||\n\t\t\t\tmessage.includes(\"No model selected\")\n\t\t\t) {\n\t\t\t\tawait ctx.reply(message);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t},\n};\n\nconst thinkingCommand: ChatCommand = {\n\tname: \"thinking\",\n\taliases: [\"effort\", \"reasoning\"],\n\tdescription: \"get or set the thinking level\",\n\tbypassBusy: false,\n\tneedsSession: true,\n\tasync handler(ctx) {\n\t\tconst session = ctx.session!;\n\t\tconst levels = session.getAvailableThinkingLevels();\n\t\tif (!session.supportsThinking()) {\n\t\t\tawait ctx.reply(\n\t\t\t\t`Model ${session.model?.provider ?? \"none\"}/${session.model?.id ?? \"none\"} doesn't support thinking.`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst arg = ctx.args.trim().toLowerCase() as ThinkingLevel;\n\t\tif (!arg) {\n\t\t\tconst current = session.thinkingLevel;\n\t\t\tconst items: PickerItem[] = levels.map((level) => ({\n\t\t\t\tlabel: `${level === current ? \"✓ \" : \"\"}${level}`,\n\t\t\t\tvalue: level,\n\t\t\t}));\n\t\t\tconst result = await createPicker(\n\t\t\t\tctx.adapter,\n\t\t\t\tctx.event.source,\n\t\t\t\t{\n\t\t\t\t\tkind: \"thinking\",\n\t\t\t\t\tsessionKey: ctx.key,\n\t\t\t\t\tinvokerId: ctx.event.source.userId,\n\t\t\t\t\titems,\n\t\t\t\t\tperPage: levels.length,\n\t\t\t\t\ttitle: \"Pick a thinking level\",\n\t\t\t\t\tasync resolve(item) {\n\t\t\t\t\t\tconst level = item.value as ThinkingLevel;\n\t\t\t\t\t\tsession.setThinkingLevel(level);\n\t\t\t\t\t\treturn { done: true, text: `☾ Thinking → ${level}` };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ replyTo: ctx.event.messageId, threadId: ctx.event.source.threadId },\n\t\t\t);\n\t\t\tif (!result.success) {\n\t\t\t\tawait ctx.reply(`Level: ${current} — available: ${levels.join(\", \")}`);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!levels.includes(arg)) {\n\t\t\tawait ctx.reply(`Invalid level. Available: ${levels.join(\", \")}`);\n\t\t\treturn;\n\t\t}\n\t\tsession.setThinkingLevel(arg);\n\t\tawait ctx.reply(`Thinking → ${arg}`);\n\t},\n};\n\nexport const CHAT_COMMANDS: ChatCommand[] = [\n\tnewCommand,\n\tundoCommand,\n\tredoCommand,\n\tstopCommand,\n\tstatusCommand,\n\twhoamiCommand,\n\thelpCommand,\n\tmodelCommand,\n\tsessionsCommand,\n\ttitleCommand,\n\tcontextCommand,\n\tswarmCommand,\n\tcompactCommand,\n\tthinkingCommand,\n];\n\n/** Platform menu specs (e.g. Telegram setMyCommands): canonical names only, aliases skipped. */\nexport function botCommandSpecs(): { name: string; description: string }[] {\n\treturn CHAT_COMMANDS.map((c) => ({ name: c.name, description: c.description }));\n}\n\nexport function formatHelpText(): string {\n\tconst lines = [\"lunR gateway commands:\"];\n\tfor (const cmd of CHAT_COMMANDS) {\n\t\tconst names = [cmd.name, ...(cmd.aliases ?? [])].map((a) => `/${a}`).join(\" | \");\n\t\tlines.push(`${names} — ${cmd.description}`);\n\t}\n\tlines.push(\"\");\n\tlines.push(\"Tap to pick: /model, /thinking, /sessions (run without args to see buttons).\");\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Run a chat command with busy/session guards and error → \"⚠ one-line\".\n * Returns true when the event was consumed by the command, false when the\n * router should continue (e.g. /swarm mutates the event and wants a normal turn).\n */\nexport async function runChatCommand(cmd: ChatCommand, ctx: ChatCommandContext): Promise<boolean> {\n\tif (!cmd.bypassBusy && ctx.bridge.getStatus(ctx.key).busy) {\n\t\tawait ctx.reply(\"⏳ busy — /stop first or wait\");\n\t\treturn true;\n\t}\n\tif (cmd.needsSession) {\n\t\tconst session = await ctx.bridge.getSession(ctx.key);\n\t\tif (!session) {\n\t\t\tawait ctx.reply(\"No session yet — send a message first.\");\n\t\t\treturn true;\n\t\t}\n\t\tctx.session = session;\n\t}\n\ttry {\n\t\tconst consumed = await cmd.handler(ctx);\n\t\treturn consumed ?? true;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tawait ctx.adapter.send(ctx.event.source.chatId, `⚠ ${oneLine(message)}`, {\n\t\t\treplyTo: ctx.event.messageId,\n\t\t\tthreadId: ctx.event.source.threadId,\n\t\t});\n\t\treturn true;\n\t}\n}\n\n/** Reply helper used by the router to build the ChatCommandContext. */\nexport async function sendCommandReply(adapter: PlatformAdapter, event: MessageEvent, text: string): Promise<void> {\n\tawait adapter.send(event.source.chatId, prefixLines(text, \"☾ \"), {\n\t\treplyTo: event.messageId,\n\t\tthreadId: event.source.threadId,\n\t});\n}\n"]}