@xopcai/xopc 0.0.23 → 0.0.25

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 (170) hide show
  1. package/dist/extensions/feishu/xopc.extension.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/agents-C_bPhtBs.js +216 -0
  4. package/dist/gateway/static/root/assets/agents-C_bPhtBs.js.map +1 -0
  5. package/dist/gateway/static/root/assets/apps-page-DbzO48lg.js +2 -0
  6. package/dist/gateway/static/root/assets/apps-page-DbzO48lg.js.map +1 -0
  7. package/dist/gateway/static/root/assets/{attachment-preview-renderer-CebH7fCz.js → attachment-preview-renderer-CxMJMbD2.js} +4 -4
  8. package/dist/gateway/static/root/assets/{attachment-preview-renderer-CebH7fCz.js.map → attachment-preview-renderer-CxMJMbD2.js.map} +1 -1
  9. package/dist/gateway/static/root/assets/{attachment-process-heavy-Dbf1--O6.js → attachment-process-heavy-EFXPGfWk.js} +6 -6
  10. package/dist/gateway/static/root/assets/{attachment-process-heavy-Dbf1--O6.js.map → attachment-process-heavy-EFXPGfWk.js.map} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-utils-core-Dt6UxMPV.js → attachment-utils-core-ECbeoa9H.js} +1 -1
  12. package/dist/gateway/static/root/assets/attachment-utils-core-ECbeoa9H.js.map +1 -0
  13. package/dist/gateway/static/root/assets/channels-settings-CeGoU9v8.js +9 -0
  14. package/dist/gateway/static/root/assets/{channels-settings-CGzrrBlT.js.map → channels-settings-CeGoU9v8.js.map} +1 -1
  15. package/dist/gateway/static/root/assets/cn-BMCV0OMB.js +2 -0
  16. package/dist/gateway/static/root/assets/cn-BMCV0OMB.js.map +1 -0
  17. package/dist/gateway/static/root/assets/cron-page-DpEYUvxB.js +2 -0
  18. package/dist/gateway/static/root/assets/{cron-page-BGCdDf2w.js.map → cron-page-DpEYUvxB.js.map} +1 -1
  19. package/dist/gateway/static/root/assets/cron-utils-Cvv0F3pa.js +3 -0
  20. package/dist/gateway/static/root/assets/{cron-utils-Dnks4wWv.js.map → cron-utils-Cvv0F3pa.js.map} +1 -1
  21. package/dist/gateway/static/root/assets/dist-C41N3YrO.js +2 -0
  22. package/dist/gateway/static/root/assets/{dist-BG1ChbY9.js.map → dist-C41N3YrO.js.map} +1 -1
  23. package/dist/gateway/static/root/assets/{docx-preview-DxcHM3sR.js → docx-preview-F-jKDMNv.js} +2 -2
  24. package/dist/gateway/static/root/assets/{docx-preview-DxcHM3sR.js.map → docx-preview-F-jKDMNv.js.map} +1 -1
  25. package/dist/gateway/static/root/assets/{excel-worksheet-utils-Dk66snKA.js → excel-worksheet-utils-DPfAinZn.js} +1 -1
  26. package/dist/gateway/static/root/assets/{excel-worksheet-utils-Dk66snKA.js.map → excel-worksheet-utils-DPfAinZn.js.map} +1 -1
  27. package/dist/gateway/static/root/assets/extension-debug-page-CkkYZjNP.js +2 -0
  28. package/dist/gateway/static/root/assets/{extension-debug-page-CRC16AbL.js.map → extension-debug-page-CkkYZjNP.js.map} +1 -1
  29. package/dist/gateway/static/root/assets/extension-page-BjUIPVNG.js +2 -0
  30. package/dist/gateway/static/root/assets/{extension-page-BagrJnbm.js.map → extension-page-BjUIPVNG.js.map} +1 -1
  31. package/dist/gateway/static/root/assets/extension-settings-page-CwuFDOdk.js +2 -0
  32. package/dist/gateway/static/root/assets/extension-settings-page-CwuFDOdk.js.map +1 -0
  33. package/dist/gateway/static/root/assets/index-DwzwDCjW.js +150 -0
  34. package/dist/gateway/static/root/assets/index-DwzwDCjW.js.map +1 -0
  35. package/dist/gateway/static/root/assets/index-dhtHG1nU.css +1 -0
  36. package/dist/gateway/static/root/assets/{jszip.min-DVUfmhpE.js → jszip.min-CL3dfyxs.js} +1 -1
  37. package/dist/gateway/static/root/assets/{jszip.min-DVUfmhpE.js.map → jszip.min-CL3dfyxs.js.map} +1 -1
  38. package/dist/gateway/static/root/assets/logs-page-BtwGPuw2.js +2 -0
  39. package/dist/gateway/static/root/assets/{logs-page-Bo9EsE_D.js.map → logs-page-BtwGPuw2.js.map} +1 -1
  40. package/dist/gateway/static/root/assets/{pdf--jE7rvON.js → pdf-CX6ji-QC.js} +1 -1
  41. package/dist/gateway/static/root/assets/{pdf--jE7rvON.js.map → pdf-CX6ji-QC.js.map} +1 -1
  42. package/dist/gateway/static/root/assets/sessions-page-4rKFDn2k.js +2 -0
  43. package/dist/gateway/static/root/assets/{sessions-page-CDgXq8qp.js.map → sessions-page-4rKFDn2k.js.map} +1 -1
  44. package/dist/gateway/static/root/assets/settings-page-iYLSxQYc.js +2 -0
  45. package/dist/gateway/static/root/assets/settings-page-iYLSxQYc.js.map +1 -0
  46. package/dist/gateway/static/root/assets/skills-page-_siDuHeF.js +3 -0
  47. package/dist/gateway/static/root/assets/{skills-page-BRS5qYTw.js.map → skills-page-_siDuHeF.js.map} +1 -1
  48. package/dist/gateway/static/root/assets/vendor-swr-B5fPo7KK.js +2 -0
  49. package/dist/gateway/static/root/assets/{vendor-swr-Dp4nzp5h.js.map → vendor-swr-B5fPo7KK.js.map} +1 -1
  50. package/dist/gateway/static/root/assets/{xlsx-DVk38js7.js → xlsx-CPtvfmVF.js} +1 -1
  51. package/dist/gateway/static/root/assets/{xlsx-DVk38js7.js.map → xlsx-CPtvfmVF.js.map} +1 -1
  52. package/dist/gateway/static/root/index.html +5 -4
  53. package/dist/package.js +1 -1
  54. package/dist/src/agent/image/tool-model-config.js +2 -2
  55. package/dist/src/agent/image/tool-model-config.js.map +1 -1
  56. package/dist/src/agent/memory/dreaming/config.d.ts +20 -0
  57. package/dist/src/agent/memory/dreaming/config.js +44 -0
  58. package/dist/src/agent/memory/dreaming/config.js.map +1 -0
  59. package/dist/src/agent/memory/dreaming/constants.d.ts +8 -0
  60. package/dist/src/agent/memory/dreaming/constants.js +14 -0
  61. package/dist/src/agent/memory/dreaming/constants.js.map +1 -0
  62. package/dist/src/agent/memory/dreaming/deep-promotion.d.ts +22 -0
  63. package/dist/src/agent/memory/dreaming/deep-promotion.js +337 -0
  64. package/dist/src/agent/memory/dreaming/deep-promotion.js.map +1 -0
  65. package/dist/src/agent/memory/dreaming/preview.d.ts +26 -0
  66. package/dist/src/agent/memory/dreaming/preview.js +176 -0
  67. package/dist/src/agent/memory/dreaming/preview.js.map +1 -0
  68. package/dist/src/agent/memory/dreaming/short-term-store.d.ts +45 -0
  69. package/dist/src/agent/memory/dreaming/short-term-store.js +187 -0
  70. package/dist/src/agent/memory/dreaming/short-term-store.js.map +1 -0
  71. package/dist/src/agent/orchestration/agent-orchestrator.js +17 -0
  72. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  73. package/dist/src/agent/service.d.ts +6 -0
  74. package/dist/src/agent/service.js +52 -0
  75. package/dist/src/agent/service.js.map +1 -1
  76. package/dist/src/agent/tools/dreaming-tool.d.ts +7 -0
  77. package/dist/src/agent/tools/dreaming-tool.js +102 -0
  78. package/dist/src/agent/tools/dreaming-tool.js.map +1 -0
  79. package/dist/src/agent/tools/execute-code-tool.js +1 -1
  80. package/dist/src/agent/tools/execute-code-tool.js.map +1 -1
  81. package/dist/src/agent/tools/factory.js +5 -0
  82. package/dist/src/agent/tools/factory.js.map +1 -1
  83. package/dist/src/agent/tools/index.d.ts +1 -0
  84. package/dist/src/agent/tools/index.js +2 -1
  85. package/dist/src/agent/tools/memory-tool.js +9 -2
  86. package/dist/src/agent/tools/memory-tool.js.map +1 -1
  87. package/dist/src/cli/commands/extension-dev.d.ts +2 -0
  88. package/dist/src/cli/commands/extension-dev.js +196 -0
  89. package/dist/src/cli/commands/extension-dev.js.map +1 -0
  90. package/dist/src/cli/commands/extension-marketplace.d.ts +4 -0
  91. package/dist/src/cli/commands/extension-marketplace.js +145 -0
  92. package/dist/src/cli/commands/extension-marketplace.js.map +1 -0
  93. package/dist/src/cli/commands/extension-pack.d.ts +2 -0
  94. package/dist/src/cli/commands/extension-pack.js +242 -0
  95. package/dist/src/cli/commands/extension-pack.js.map +1 -0
  96. package/dist/src/cli/commands/extension.js +13 -0
  97. package/dist/src/cli/commands/extension.js.map +1 -1
  98. package/dist/src/cli/index.js +5 -1
  99. package/dist/src/cli/index.js.map +1 -1
  100. package/dist/src/config/schema.d.ts +39 -0
  101. package/dist/src/config/schema.js +17 -2
  102. package/dist/src/config/schema.js.map +1 -1
  103. package/dist/src/extensions/api.d.ts +6 -1
  104. package/dist/src/extensions/api.js +30 -0
  105. package/dist/src/extensions/api.js.map +1 -1
  106. package/dist/src/extensions/engine-check.d.ts +14 -0
  107. package/dist/src/extensions/engine-check.js +148 -0
  108. package/dist/src/extensions/engine-check.js.map +1 -0
  109. package/dist/src/extensions/loader.js +23 -0
  110. package/dist/src/extensions/loader.js.map +1 -1
  111. package/dist/src/extensions/marketplace.d.ts +24 -0
  112. package/dist/src/extensions/marketplace.js +98 -0
  113. package/dist/src/extensions/marketplace.js.map +1 -0
  114. package/dist/src/extensions/normalize-manifest.js +16 -4
  115. package/dist/src/extensions/normalize-manifest.js.map +1 -1
  116. package/dist/src/extensions/sdk/index.d.ts +2 -0
  117. package/dist/src/extensions/sdk/index.js +2 -1
  118. package/dist/src/extensions/sdk/index.js.map +1 -1
  119. package/dist/src/extensions/sdk/testing.d.ts +49 -3
  120. package/dist/src/extensions/sdk/testing.js +174 -5
  121. package/dist/src/extensions/sdk/testing.js.map +1 -1
  122. package/dist/src/extensions/types/core.d.ts +5 -0
  123. package/dist/src/extensions/types/manifest.d.ts +17 -0
  124. package/dist/src/extensions/when-context.d.ts +6 -0
  125. package/dist/src/extensions/when-context.js +28 -0
  126. package/dist/src/extensions/when-context.js.map +1 -0
  127. package/dist/src/extensions/when-expression.d.ts +11 -0
  128. package/dist/src/extensions/when-expression.js +215 -0
  129. package/dist/src/extensions/when-expression.js.map +1 -0
  130. package/dist/src/gateway/hono/app.js +1 -1
  131. package/dist/src/gateway/hono/app.js.map +1 -1
  132. package/dist/src/gateway/hono/lib/config-payload.d.ts +13 -0
  133. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +28 -0
  134. package/dist/src/gateway/hono/routes/auth-registry-extensions.js.map +1 -1
  135. package/dist/src/gateway/hono/routes/config.js +48 -0
  136. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  137. package/dist/src/gateway/hono/routes/dreaming.d.ts +3 -0
  138. package/dist/src/gateway/hono/routes/dreaming.js +198 -0
  139. package/dist/src/gateway/hono/routes/dreaming.js.map +1 -0
  140. package/dist/src/gateway/hono/routes/index.js +2 -0
  141. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  142. package/dist/src/gateway/lock.js +1 -1
  143. package/dist/src/gateway/service.js +7 -0
  144. package/dist/src/gateway/service.js.map +1 -1
  145. package/dist/src/providers/index.d.ts +6 -3
  146. package/dist/src/providers/index.js +12 -23
  147. package/dist/src/providers/index.js.map +1 -1
  148. package/package.json +2 -1
  149. package/dist/gateway/static/root/assets/agents-BY_DaknM.js +0 -216
  150. package/dist/gateway/static/root/assets/agents-BY_DaknM.js.map +0 -1
  151. package/dist/gateway/static/root/assets/apps-page-BO3nQbJY.js +0 -2
  152. package/dist/gateway/static/root/assets/apps-page-BO3nQbJY.js.map +0 -1
  153. package/dist/gateway/static/root/assets/attachment-utils-core-Dt6UxMPV.js.map +0 -1
  154. package/dist/gateway/static/root/assets/channels-settings-CGzrrBlT.js +0 -9
  155. package/dist/gateway/static/root/assets/cron-page-BGCdDf2w.js +0 -2
  156. package/dist/gateway/static/root/assets/cron-utils-Dnks4wWv.js +0 -3
  157. package/dist/gateway/static/root/assets/dist-BG1ChbY9.js +0 -2
  158. package/dist/gateway/static/root/assets/extension-debug-page-CRC16AbL.js +0 -2
  159. package/dist/gateway/static/root/assets/extension-page-BagrJnbm.js +0 -2
  160. package/dist/gateway/static/root/assets/extension-settings-page-DEpxRKKK.js +0 -2
  161. package/dist/gateway/static/root/assets/extension-settings-page-DEpxRKKK.js.map +0 -1
  162. package/dist/gateway/static/root/assets/index-CYVs9x8D.css +0 -1
  163. package/dist/gateway/static/root/assets/index-KNzRh6gu.js +0 -150
  164. package/dist/gateway/static/root/assets/index-KNzRh6gu.js.map +0 -1
  165. package/dist/gateway/static/root/assets/logs-page-Bo9EsE_D.js +0 -2
  166. package/dist/gateway/static/root/assets/sessions-page-CDgXq8qp.js +0 -2
  167. package/dist/gateway/static/root/assets/settings-page-BWMTFST6.js +0 -2
  168. package/dist/gateway/static/root/assets/settings-page-BWMTFST6.js.map +0 -1
  169. package/dist/gateway/static/root/assets/skills-page-BRS5qYTw.js +0 -3
  170. package/dist/gateway/static/root/assets/vendor-swr-Dp4nzp5h.js +0 -2
@@ -10,6 +10,8 @@ import { getDefaultModelSync, init_providers, resolveModel } from "../providers/
10
10
  import { extractTextContent, loadBootstrapFiles } from "./context/workspace.js";
11
11
  import { isTTSAvailable } from "../voice/tts/factory.js";
12
12
  import { mergeTtsConfigFromAppConfig } from "../voice/tts/merge-config.js";
13
+ import { DREAMING_CRON_NAME, DREAMING_SWEEP_TOKEN } from "./memory/dreaming/constants.js";
14
+ import { resolveDreamingConfig } from "./memory/dreaming/config.js";
13
15
  import { speak } from "../voice/tts/speak-core.js";
14
16
  import { getChannelOutputFormat, shouldUseTTS } from "../voice/tts/service.js";
15
17
  import { compressAudio } from "../voice/tts/audio.js";
@@ -405,6 +407,13 @@ var AgentService = class {
405
407
  port: 0,
406
408
  host: "cli"
407
409
  });
410
+ await this.reconcileDreamingCronJob().catch((err) => {
411
+ const em = err instanceof Error ? err.message : String(err);
412
+ log.warn({
413
+ err,
414
+ errorMessage: em
415
+ }, `Dreaming cron reconcile failed: ${em}`);
416
+ });
408
417
  log.debug("Agent service started");
409
418
  await this.hookHandler.trigger("session_start", { sessionId: this.agentId });
410
419
  while (this.running) try {
@@ -434,6 +443,49 @@ var AgentService = class {
434
443
  return Promise.resolve();
435
444
  }
436
445
  /**
446
+ * Reconcile managed Dreaming cron job against the current effective config.
447
+ * Safe to call after config saves to apply changes without restarting the process.
448
+ */
449
+ async reconcileDreamingNow() {
450
+ await this.reconcileDreamingCronJob();
451
+ }
452
+ async reconcileDreamingCronJob() {
453
+ const cron = this.config.getCronService?.();
454
+ if (!cron) return;
455
+ const dreaming = resolveDreamingConfig(this.effectiveAppConfig());
456
+ const managed = (await cron.listJobs()).filter((job) => job.name === "Memory Dreaming - Deep Promotion" || (job.name?.includes?.("[managed-by=xopc.memory.dreaming]") ?? false));
457
+ const desiredPayload = {
458
+ kind: "agentTurn",
459
+ message: DREAMING_SWEEP_TOKEN
460
+ };
461
+ const desired = {
462
+ name: DREAMING_CRON_NAME,
463
+ timezone: dreaming.timezone,
464
+ sessionTarget: "isolated",
465
+ payload: desiredPayload,
466
+ enabled: true
467
+ };
468
+ if (!dreaming.enabled || !dreaming.deep.enabled) {
469
+ for (const job of managed) await cron.removeJob(job.id).catch(() => {});
470
+ return;
471
+ }
472
+ if (managed.length === 0) {
473
+ await cron.addJob(dreaming.frequency, { ...desired });
474
+ return;
475
+ }
476
+ const primary = managed[0];
477
+ for (const dup of managed.slice(1)) await cron.removeJob(dup.id).catch(() => {});
478
+ const payloadMessage = primary.payload?.kind === "agentTurn" ? primary.payload.message : primary.payload?.text;
479
+ if (primary.schedule !== dreaming.frequency || (dreaming.timezone ?? null) !== (primary.timezone ?? null) || primary.sessionTarget !== "isolated" || payloadMessage !== "__xopc_memory_dreaming_sweep__" || primary.enabled !== true || primary.name !== "Memory Dreaming - Deep Promotion") await cron.updateJob(primary.id, {
480
+ schedule: dreaming.frequency,
481
+ timezone: dreaming.timezone ?? void 0,
482
+ sessionTarget: "isolated",
483
+ name: DREAMING_CRON_NAME,
484
+ payload: desiredPayload,
485
+ enabled: true
486
+ });
487
+ }
488
+ /**
437
489
  * Persist agent messages with the same sanitizer + transcript hygiene as AgentOrchestrator.
438
490
  * Uses persistence hygiene so `thinking` blocks remain on disk for the web UI (LLM load path still drops them).
439
491
  */
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","names":["parseRoutingSessionKey"],"sources":["../../../src/agent/service.ts"],"sourcesContent":["import type { AgentEvent, AgentMessage, ThinkingLevel } from '@mariozechner/pi-agent-core';\nimport { MessageBusShutdownError, type MessageBus, type InboundMessage } from '../infra/bus/index.js';\nimport { type Config, getAgentDefaultModelRef } from '../config/schema.js';\nimport { maybeAutoTitleSessionStore } from '../session/session-title.js';\nimport type { ChannelManager } from '../channels/manager.js';\nimport { INTERNAL_OUTBOUND_DROP_CHANNEL } from '../channels/internal-outbound.js';\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport {\n SessionStore,\n SessionConfigStore,\n resolveEffectiveThinkingLevel,\n resolveEffectiveReasoningLevel,\n effectiveWorkspacePathForSession,\n normalizeWorkingDirectoryInput,\n type CompactionConfig,\n type WindowConfig,\n} from '../session/index.js';\nimport {\n normalizeThinkLevel,\n normalizeReasoningLevel,\n type ThinkLevel,\n type ReasoningLevel,\n} from './transcript/thinking-types.js';\nimport { createLogger, runWithLogContext, updateAsyncLogContext } from '../utils/logger.js';\nimport { ExtensionHookRunner } from '../extensions/index.js';\nimport { loadBootstrapFiles, extractTextContent } from './context/workspace.js';\nimport { SessionTracker } from './session/tracker.js';\nimport { ModelManager } from './models/index.js';\nimport { commandRegistry, initializeCommands } from '../chat-commands/index.js';\nimport { parseSlashCommand } from '../chat-commands/command-parse.js';\nimport { ProgressFeedbackManager } from './lifecycle/progress.js';\nimport { HookHandler } from './lifecycle/hook-handler.js';\nimport { ToolErrorTracker } from './tools/error-tracker.js';\nimport { RequestLimiter } from './models/request-limiter.js';\nimport { SystemReminder } from './prompt/system-reminder.js';\nimport { ToolUsageAnalyzer } from './tools/usage-analyzer.js';\nimport { ToolChainTracker } from './tools/chain-tracker.js';\nimport { ErrorPatternMatcher } from './tools/error-pattern-matcher.js';\nimport { ContextMiddleware, SelfVerifyMiddleware } from './middleware/index.js';\nimport { LifecycleManager } from './lifecycle/index.js';\nimport { CompactionLifecycleHandler } from './lifecycle/handlers/compaction.js';\n\nimport { MessageRouter, CommandHandler, StreamManager } from './messaging/index.js';\nimport { SessionContextManager, SessionLifecycleManager, type SessionContext } from './session/index.js';\nimport { AgentOrchestrator, AgentEventHandler } from './orchestration/index.js';\nimport { runAgentTurnWithModelFallbacks } from './orchestration/run-agent-turn-with-fallbacks.js';\nimport { FeedbackCoordinator } from './feedback/index.js';\nimport { AgentManager, type SkillCatalogEntry } from './agent-manager.js';\nimport { extractAgentUserPlainText } from './memory/user-message-text.js';\nimport { inboundMessageLogRequestId } from './service-inbound-utils.js';\nimport type { AgentServiceConfig, StreamHandle } from './service.types.js';\nimport {\n runProcessDirectStreaming,\n type ProcessDirectStreamingDeps,\n} from './service/process-direct-streaming.js';\n\nimport {\n resolveAgentHomeDir,\n resolveDefaultAgentId,\n} from './agent-scope.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../routing/session-key.js';\nimport { extractProfileAgentId, resolveAgentBootstrapDir } from '../config/agent-profile.js';\nimport { DEFAULT_ACK_MAX_CHARS, NO_REPLY, shouldSilence } from '../heartbeat/tokens.js';\nimport { createTypingController, type TypingController } from './lifecycle/typing.js';\nimport { cleanTrailingErrors, sanitizeMessages } from './memory/message-sanitizer.js';\nimport {\n tryApplySessionTranscriptHygiene,\n tryApplySessionTranscriptHygieneForPersistence,\n} from './transcript/transcript-hygiene.js';\nimport {\n persistInboundAttachmentsToWorkspace,\n formatInboundFileTextBlock,\n type InternalAttachmentRoots,\n} from '../channels/attachments/inbound-persist.js';\nimport { expandAtFileMentionsInPlainText } from './context/expand-at-file-mentions.js';\nimport { resolveInboundImageContentParts } from './image/inbound-image-handling.js';\nimport { getDefaultModelSync, resolveModel } from '../providers/index.js';\nimport { complete, type UserMessage } from '@mariozechner/pi-ai';\nimport { resolveEffectiveAgentProfileForSession } from '../config/agent-profile.js';\nimport type { CompactionResult } from './memory/compaction.js';\nimport { persistOutboundTtsAudio } from '../channels/attachments/outbound-tts-persist.js';\nimport { compressAudio } from '../voice/tts/audio.js';\nimport { speak } from '../voice/tts/index.js';\nimport { mergeTtsConfigFromAppConfig } from '../voice/tts/merge-config.js';\nimport { applyConfigOverrides } from '../config/runtime-overrides.js';\nimport { shouldUseTTS, getChannelOutputFormat } from '../voice/tts/service.js';\nimport { isTTSAvailable } from '../voice/tts/factory.js';\n\nexport type { AgentServiceConfig, AgentContext, StreamHandle } from './service.types.js';\n\nconst log = createLogger('AgentService');\n\nexport class AgentService {\n private sessionStore: SessionStore;\n private sessionConfigStore: SessionConfigStore;\n private hookRunner?: ExtensionHookRunner;\n private running = false;\n private agentId: string;\n private workspaceDir: string;\n private bootstrapFiles: ReturnType<typeof loadBootstrapFiles> = [];\n private channelManagerRef: ChannelManager | null = null;\n private bus: MessageBus;\n private config: AgentServiceConfig;\n\n private sessionTracker: SessionTracker;\n private modelManager: ModelManager;\n private progressManager: ProgressFeedbackManager;\n private hookHandler: HookHandler;\n private lifecycleManager: LifecycleManager;\n private errorTracker: ToolErrorTracker;\n private requestLimiter: RequestLimiter;\n private systemReminder: SystemReminder;\n private toolUsageAnalyzer: ToolUsageAnalyzer;\n private toolChainTracker: ToolChainTracker;\n private errorPatternMatcher: ErrorPatternMatcher;\n private selfVerifyMiddleware: SelfVerifyMiddleware;\n private contextMiddleware: ContextMiddleware;\n\n private messageRouter: MessageRouter;\n private commandHandler: CommandHandler;\n private streamManager: StreamManager;\n private sessionContextManager: SessionContextManager;\n private sessionLifecycleManager: SessionLifecycleManager;\n private agentOrchestrator: AgentOrchestrator;\n private agentEventHandler: AgentEventHandler;\n private feedbackCoordinator: FeedbackCoordinator;\n private agentManager: AgentManager;\n\n /** Webchat SSE queue pushers for `clarify_request` and similar mid-turn UI events. */\n private webchatSseEnqueueBySession = new Map<\n string,\n (event: { type: string; [key: string]: unknown }) => void\n >();\n\n // Track event unsubscribers per session\n private sessionUnsubscribers: Map<string, () => void> = new Map();\n\n private effectiveAppConfig(): Config | undefined {\n const base = this.config.config;\n return base ? applyConfigOverrides(base) : undefined;\n }\n\n constructor(bus: MessageBus, config: AgentServiceConfig) {\n this.bus = bus;\n this.config = config;\n this.agentId = `agent-${Date.now()}`;\n this.workspaceDir = config.workspace;\n\n if (config.config) {\n const aid = resolveDefaultAgentId(config.config);\n this.bootstrapFiles = loadBootstrapFiles(resolveAgentBootstrapDir(config.config, aid));\n } else {\n this.bootstrapFiles = [];\n }\n\n this.sessionTracker = new SessionTracker();\n this.modelManager = new ModelManager({\n defaultModel: config.model,\n config: config.config,\n });\n\n initializeCommands();\n log.debug('Command system initialized');\n\n this.sessionStore = this.createSessionStore();\n const appCfgForPaths = this.config.config;\n if (!appCfgForPaths) {\n throw new Error('AgentService requires config.config for session paths');\n }\n const defaultAid = resolveDefaultAgentId(appCfgForPaths);\n const defaultAgentHome = resolveAgentHomeDir(appCfgForPaths, defaultAid);\n this.sessionConfigStore = new SessionConfigStore(defaultAgentHome);\n\n this.hookRunner = this.createHookRunner();\n this.hookHandler = new HookHandler({\n hookRunner: this.hookRunner,\n agentId: this.agentId,\n get sessionKey() { return this.currentContext?.sessionKey; },\n });\n\n this.progressManager = this.createProgressManager();\n this.initializeReliabilityModules();\n\n this.lifecycleManager = new LifecycleManager();\n this.initializeLifecycleHandlers();\n\n this.streamManager = new StreamManager();\n this.sessionContextManager = new SessionContextManager();\n this.feedbackCoordinator = new FeedbackCoordinator({\n progressManager: this.progressManager,\n bus,\n });\n\n // Initialize AgentManager\n this.agentManager = new AgentManager({\n workspace: config.workspace,\n model: config.model,\n config: config.config,\n extensionRegistry: config.extensionRegistry,\n hookRunner: this.hookRunner,\n bus,\n getCurrentContext: () => this.sessionContextManager.getContext(),\n getSessionStore: () => this.sessionStore,\n getModelManager: () => this.modelManager,\n thinkingLevel: config.thinkingLevel,\n reasoningLevel: config.reasoningLevel,\n verboseLevel: config.verboseLevel,\n gatewayClarify: config.gatewayClarify,\n getCronService: config.getCronService,\n });\n\n this.agentEventHandler = new AgentEventHandler({\n progressManager: this.progressManager,\n errorTracker: this.errorTracker,\n requestLimiter: this.requestLimiter,\n lifecycleManager: this.lifecycleManager,\n toolChainTracker: this.toolChainTracker,\n selfVerifyMiddleware: this.selfVerifyMiddleware,\n systemReminder: this.systemReminder,\n toolUsageAnalyzer: this.toolUsageAnalyzer,\n errorPatternMatcher: this.errorPatternMatcher,\n modelManager: this.modelManager,\n });\n\n this.agentOrchestrator = new AgentOrchestrator({\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n eventHandler: this.agentEventHandler,\n feedbackCoordinator: this.feedbackCoordinator,\n sessionConfigStore: this.sessionConfigStore,\n hydrateSessionWorkspaceFromStore: (sessionKey) => this.hydrateSessionWorkspaceFromStore(sessionKey),\n getConfig: () => this.effectiveAppConfig(),\n getThinkingDefault: () => this.effectiveAppConfig()?.agents?.defaults?.thinkingDefault,\n getThinkingDefaultForSession: (sessionKey: string) =>\n this.agentManager.getThinkingDefaultForSession(sessionKey),\n workspaceRoot: this.workspaceDir,\n getWorkspaceRootForSession: (sessionKey: string) =>\n this.agentManager.getResolvedWorkspaceForSession(sessionKey),\n getAgentInternalStorageRootForSession: (sessionKey: string) =>\n resolveAgentHomeDir(this.config.config!, extractProfileAgentId(sessionKey, this.config.config!)),\n enqueueAutoTitle: (sessionKey: string) => this.enqueueMaybeAutoTitleAfterPersist(sessionKey),\n });\n\n this.messageRouter = new MessageRouter();\n this.commandHandler = new CommandHandler({\n config: config.config!,\n bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n applySessionThinkingLevel: (sessionKey: string, level: ThinkLevel) => {\n this.agentManager.setThinkingLevel(sessionKey, level as ThinkingLevel);\n },\n getCurrentModel: () => this.agentOrchestrator.getCurrentModel(),\n switchModelForSession: (sessionKey: string, modelId: string) =>\n this.switchModelForSession(sessionKey, modelId),\n invalidateAgentSession: (sessionKey: string) => {\n this.agentManager.removeAgent(sessionKey);\n },\n abortSessionTurn: async (sessionKey: string) => {\n await this.streamManager.abort();\n this.agentOrchestrator.abort(sessionKey);\n },\n compactSession: (sessionKey, options) => this.compactSession(sessionKey, options),\n btwQuery: (sessionKey, question) => this.btwQuery(sessionKey, question),\n getSessionContextReport: (sessionKey, mode) => this.getSessionContextReport(sessionKey, mode),\n });\n\n this.sessionLifecycleManager = new SessionLifecycleManager(\n this.sessionStore,\n this.sessionTracker,\n this.lifecycleManager\n );\n\n // Register signal handlers only if not running as an Electron subprocess.\n // In Electron, the parent process manages the lifecycle and signals should not trigger disposal.\n const isElectronSubprocess = !!process.env.ELECTRON_RUN_AS_NODE;\n if (!isElectronSubprocess) {\n process.on('SIGINT', () => this.dispose());\n process.on('SIGTERM', () => this.dispose());\n }\n\n log.info('AgentService initialized');\n }\n\n private attachmentRootsForSession(sessionKey: string): InternalAttachmentRoots {\n const cfg = this.config.config!;\n return {\n agentHome: resolveAgentHomeDir(cfg, extractProfileAgentId(sessionKey, cfg)),\n };\n }\n\n private createSessionStore(): SessionStore {\n const sessionStoreDefaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n const windowConfig: Partial<WindowConfig> = {\n maxMessages: 100,\n keepRecentMessages: sessionStoreDefaults?.maxToolIterations || 20,\n preserveSystemMessages: true,\n };\n const compactionConfig: Partial<CompactionConfig> = {\n enabled: sessionStoreDefaults?.compaction?.enabled ?? true,\n mode: (sessionStoreDefaults?.compaction?.mode as 'extractive' | 'abstractive' | 'structured') || 'abstractive',\n reserveTokens: sessionStoreDefaults?.compaction?.reserveTokens || 8000,\n triggerThreshold: sessionStoreDefaults?.compaction?.triggerThreshold || 0.8,\n minMessagesBeforeCompact: sessionStoreDefaults?.compaction?.minMessagesBeforeCompact || 10,\n keepRecentMessages: sessionStoreDefaults?.compaction?.keepRecentMessages || 10,\n evictionWindow: sessionStoreDefaults?.compaction?.evictionWindow || 0.2,\n retentionWindow: sessionStoreDefaults?.compaction?.retentionWindow || 6,\n };\n const appCfg = this.config.config;\n if (!appCfg) {\n throw new Error('AgentService requires config.config for session store paths');\n }\n return new SessionStore(\n {\n config: appCfg,\n agentId: resolveDefaultAgentId(appCfg),\n },\n windowConfig,\n compactionConfig,\n );\n }\n\n private createHookRunner(): ExtensionHookRunner | undefined {\n if (!this.config.extensionRegistry) return undefined;\n\n return new ExtensionHookRunner(this.config.extensionRegistry, {\n catchErrors: true,\n logger: {\n info: (msg: string) => log.info({ hook: true }, msg),\n warn: (msg: string) => log.warn({ hook: true }, msg),\n error: (msg: string) => log.error({ hook: true }, msg),\n },\n });\n }\n\n private createProgressManager(): ProgressFeedbackManager {\n return new ProgressFeedbackManager({\n level: 'normal',\n showThinking: true,\n streamToolProgress: true,\n heartbeatEnabled: true,\n heartbeatIntervalMs: 20000,\n longTaskThresholdMs: 30000,\n });\n }\n\n private initializeReliabilityModules(): void {\n const defaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n\n this.errorTracker = new ToolErrorTracker({\n maxFailuresPerTool: defaults?.maxToolFailuresPerTurn || 3,\n maxTotalFailures: defaults?.maxToolFailuresPerTurn ? defaults.maxToolFailuresPerTurn + 2 : 5,\n resetOnTurnEnd: true,\n });\n\n this.selfVerifyMiddleware = new SelfVerifyMiddleware({\n maxEditsPerFile: 5,\n enablePreCompletionCheck: true,\n minTurnsForVerification: 4,\n resetOnVerification: true,\n });\n\n this.requestLimiter = new RequestLimiter({\n maxRequestsPerTurn: defaults?.maxRequestsPerTurn || 50,\n warnThreshold: 0.8,\n softLimit: false,\n });\n\n this.systemReminder = new SystemReminder({\n enabled: true,\n appendToToolResults: true,\n maxRemindersPerTurn: 3,\n });\n\n this.toolUsageAnalyzer = new ToolUsageAnalyzer({\n enabled: true,\n lowUsageThreshold: 5,\n veryLowUsageThreshold: 1,\n minCallsForAnalysis: 100,\n reportIntervalMs: 60 * 60 * 1000,\n });\n\n this.toolChainTracker = new ToolChainTracker({\n enabled: true,\n maxChainsPerSession: 10,\n maxNodesPerChain: 100,\n trackParams: true,\n trackResults: true,\n autoPrune: true,\n });\n\n this.errorPatternMatcher = new ErrorPatternMatcher({\n enabled: true,\n defaultMaxRetries: 1,\n logMatches: true,\n });\n\n // Initialize context middleware for automatic request tracking\n this.contextMiddleware = new ContextMiddleware();\n }\n\n private initializeLifecycleHandlers(): void {\n this.lifecycleManager.on('llm_response', new CompactionLifecycleHandler({\n minMessages: 20,\n maxTokens: 8000,\n preserveReasoning: true,\n accumulateUsage: true,\n }));\n\n log.debug(\n { handlers: this.lifecycleManager.getRegisteredHandlers() },\n 'Lifecycle handlers initialized'\n );\n }\n\n setChannelManager(channelManager: ChannelManager): void {\n this.modelManager.setChannelManager(channelManager);\n this.channelManagerRef = channelManager;\n }\n\n /**\n * Apply config after save or hot reload so the default model updates without restarting the gateway.\n */\n applyAgentDefaultsFromConfig(config: Config): void {\n this.config.config = config;\n const ref = getAgentDefaultModelRef(config);\n this.config.model = ref;\n this.modelManager.updateFromConfig(config);\n this.agentManager.updateAgentDefaults(config);\n this.commandHandler.updateAgentConfig(config);\n }\n\n getSkillCatalog(): SkillCatalogEntry[] {\n return this.agentManager.getSkillCatalog();\n }\n\n getSkillMarkdownSource(skillName: string): { name: string; markdown: string } | null {\n return this.agentManager.getSkillMarkdownSource(skillName);\n }\n\n refreshSkillsAfterDiskChange(): void {\n this.agentManager.refreshSkillsAfterDiskChange();\n }\n\n refreshSkillsAfterSkillConfigChange(): void {\n this.agentManager.refreshSkillsAfterSkillConfigChange();\n }\n\n getModelForSession(sessionKey: string): string {\n return this.modelManager.getModelForSession(sessionKey);\n }\n\n async switchModelForSession(sessionKey: string, modelId: string): Promise<boolean> {\n const ok = await this.modelManager.switchModelForSession(sessionKey, modelId);\n if (!ok) return false;\n await this.sessionConfigStore.update(sessionKey, { modelOverride: modelId });\n const result = this.agentManager.setModelForSession(sessionKey, modelId);\n if (result) {\n this.sessionTracker.touchSession(sessionKey);\n }\n return true;\n }\n\n private async clearSessionModelOverride(sessionKey: string): Promise<void> {\n this.modelManager.clearSessionModelOverride(sessionKey);\n await this.sessionConfigStore.update(sessionKey, { modelOverride: undefined });\n const agent = this.agentManager.getAgent(sessionKey);\n if (agent) {\n await this.modelManager.applyModelForSession(agent, sessionKey);\n }\n }\n\n /**\n * Clears per-session model override so the next turn uses the configured agent default\n * (e.g. cron isolated job with no explicit model).\n */\n async resetSessionModelToAgentDefault(sessionKey: string): Promise<void> {\n await this.clearSessionModelOverride(sessionKey);\n }\n\n private async hydrateSessionModelFromStore(sessionKey: string): Promise<void> {\n const cfg = await this.sessionConfigStore.get(sessionKey);\n if (cfg?.modelOverride) {\n await this.modelManager.switchModelForSession(sessionKey, cfg.modelOverride);\n }\n }\n\n setStreamHandle(handle: StreamHandle): void {\n this.streamManager.setHandle(handle);\n this.feedbackCoordinator.setStreamHandle(handle);\n }\n\n clearStreamHandle(): void {\n this.streamManager.clearHandle();\n this.feedbackCoordinator.endTask();\n }\n\n async start(): Promise<void> {\n this.running = true;\n await this.sessionConfigStore.initialize();\n await this.hookHandler.trigger('gateway_start', { port: 0, host: 'cli' });\n log.debug('Agent service started');\n await this.hookHandler.trigger('session_start', { sessionId: this.agentId });\n\n while (this.running) {\n try {\n const msg = await this.bus.consumeInbound();\n await this.handleInboundMessage(msg);\n } catch (error) {\n if (error instanceof MessageBusShutdownError) {\n break;\n }\n const em = error instanceof Error ? error.message : String(error);\n log.error(\n { err: error, errorMessage: em, phase: 'inbound_consume' },\n `Agent loop failed (will retry in 1s): ${em}`,\n );\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n\n await this.hookHandler.trigger('session_end', {\n sessionId: this.agentId,\n messageCount: 0, // No longer tracking single agent messages\n });\n }\n\n stop(): Promise<void> {\n this.running = false;\n this.agentManager.dispose();\n this.dispose();\n\n this.hookHandler.trigger('gateway_stop', { reason: 'stopped' });\n log.debug('Agent service stopped');\n return Promise.resolve();\n }\n\n /**\n * Persist agent messages with the same sanitizer + transcript hygiene as AgentOrchestrator.\n * Uses persistence hygiene so `thinking` blocks remain on disk for the web UI (LLM load path still drops them).\n */\n private async persistAgentSessionMessages(sessionKey: string): Promise<void> {\n const raw = this.agentManager.getMessages(sessionKey);\n if (!raw) {\n return;\n }\n const { messages } = sanitizeMessages(raw);\n let toSave = messages;\n try {\n const model = this.modelManager.getResolvedModelForSession(sessionKey);\n toSave = tryApplySessionTranscriptHygieneForPersistence(messages, model);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Transcript hygiene on save skipped');\n }\n await this.sessionStore.save(sessionKey, toSave);\n this.enqueueMaybeAutoTitleAfterPersist(sessionKey);\n }\n\n /**\n * Fire-and-forget: `maybeAutoTitleSessionStore` no-ops for cron/heartbeat keys.\n * Runs after persist so the store has the latest transcript; does not block SSE / callers.\n */\n private enqueueMaybeAutoTitleAfterPersist(sessionKey: string): void {\n void (async () => {\n try {\n let modelRef =\n getAgentDefaultModelRef(this.config.config ?? ({} as Config)) ?? this.config.model;\n if (!modelRef?.trim()) {\n try {\n modelRef = this.modelManager.getModelForSession(sessionKey);\n } catch {\n modelRef = undefined;\n }\n }\n await maybeAutoTitleSessionStore(this.sessionStore, sessionKey, modelRef?.trim() || undefined);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Auto session title failed');\n }\n })();\n }\n\n private prepareLoadedSessionMessages(sessionKey: string, messages: AgentMessage[]): AgentMessage[] {\n let out = cleanTrailingErrors(messages);\n try {\n const model = this.modelManager.getResolvedModelForSession(sessionKey);\n out = tryApplySessionTranscriptHygiene(out, model);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Transcript hygiene on load skipped');\n }\n return out;\n }\n\n private parseSessionKey(sessionKey: string): { channel: string; chatId: string } {\n const parts = sessionKey.split(':').filter(Boolean);\n const first = parts[0] || 'cli';\n\n // Heartbeat sessions use keys like `heartbeat:main` / `heartbeat:isolated:ts` — not a real channel id.\n // Route tool outbounds to configured delivery target, or a synthetic channel that ChannelManager drops.\n if (first === 'heartbeat') {\n const hb = this.config.config?.gateway?.heartbeat;\n const target = hb?.target?.trim();\n const targetChatId = hb?.targetChatId?.trim();\n if (target && targetChatId) {\n return { channel: target, chatId: targetChatId };\n }\n return { channel: INTERNAL_OUTBOUND_DROP_CHANNEL, chatId: parts.slice(1).join(':') || 'heartbeat' };\n }\n\n const parsed = parseRoutingSessionKey(sessionKey);\n if (parsed) {\n return { channel: parsed.source, chatId: parsed.peerId };\n }\n\n if (first === 'cron') {\n return { channel: INTERNAL_OUTBOUND_DROP_CHANNEL, chatId: parts.slice(1).join(':') || 'cron' };\n }\n\n return {\n channel: first,\n chatId: parts.slice(1).join(':') || 'direct',\n };\n }\n\n private initSessionContext(\n sessionKey: string,\n channel: string,\n chatId: string,\n senderId = '',\n ): SessionContext {\n const context: SessionContext = {\n sessionKey,\n channel,\n chatId,\n senderId,\n isGroup: false,\n };\n\n this.contextMiddleware.onRequest({\n sessionKey,\n userId: context.senderId,\n channel,\n chatId,\n });\n\n this.sessionContextManager.setContext(context);\n this.feedbackCoordinator.setContext(context);\n this.setupSessionEventHandling(sessionKey);\n\n return context;\n }\n\n private async buildMessageContent(\n content: string,\n attachments?: Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>,\n sessionKey?: string,\n ): Promise<Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }>> {\n const messageContent: Array<\n { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }\n > = [];\n\n const sk = sessionKey ?? '';\n\n if (content.trim()) {\n let textPart = content;\n if (/@file:/.test(textPart)) {\n const wsKey = sk !== '' ? sk : 'cli:direct';\n const root = this.agentManager.getResolvedWorkspaceForSession(wsKey);\n textPart = await expandAtFileMentionsInPlainText(textPart, root);\n }\n messageContent.push({ type: 'text', text: textPart });\n }\n\n if (!attachments?.length) {\n return messageContent;\n }\n\n const modelRef =\n sk !== ''\n ? this.modelManager.getModelForSession(sk)\n : getAgentDefaultModelRef(this.config.config!) ?? getDefaultModelSync(this.config.config);\n const cfg = this.config.config;\n\n const storageRoot =\n sk !== ''\n ? resolveAgentHomeDir(this.config.config!, extractProfileAgentId(sk, this.config.config!))\n : resolveAgentHomeDir(this.config.config!, resolveDefaultAgentId(this.config.config!));\n\n let i = 0;\n while (i < attachments.length) {\n const att = attachments[i]!;\n const isImage =\n att.type === 'image' ||\n att.type === 'photo' ||\n Boolean(att.mimeType?.startsWith('image/'));\n\n if (isImage) {\n const group: Array<{ data: string; mimeType: string }> = [];\n while (i < attachments.length) {\n const a = attachments[i]!;\n const img =\n a.type === 'image' || a.type === 'photo' || Boolean(a.mimeType?.startsWith('image/'));\n if (!img) {\n break;\n }\n if (!a.data || a.data.length === 0) {\n i += 1;\n continue;\n }\n group.push({ data: a.data, mimeType: a.mimeType || 'image/png' });\n i += 1;\n }\n if (group.length > 0) {\n const parts = await resolveInboundImageContentParts({\n modelRef: modelRef || getDefaultModelSync(cfg),\n cfg,\n userTextForContext: content.trim() ? content : '',\n images: group,\n });\n messageContent.push(...parts);\n }\n } else {\n const fileBlock = formatInboundFileTextBlock(att, storageRoot);\n messageContent.push({ type: 'text', text: fileBlock });\n i += 1;\n }\n }\n\n return messageContent;\n }\n\n /**\n * Persist inbound file attachments under agent home `inbound/` (non-images with data).\n * Idempotent if `workspaceRelativePath` is already set on an attachment.\n */\n async prepareInboundAttachments(\n sessionKey: string,\n attachments?: Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>,\n ): Promise<\n | Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>\n | undefined\n > {\n const cfg = this.config.config!;\n const storageRoot = resolveAgentHomeDir(cfg, extractProfileAgentId(sessionKey, cfg));\n return persistInboundAttachmentsToWorkspace(storageRoot, sessionKey, attachments);\n }\n\n private endDirectRequestContext(): void {\n this.sessionContextManager.clearContext();\n this.feedbackCoordinator.clearContext();\n this.contextMiddleware.onResponse();\n }\n\n async compactSession(\n sessionKey: string,\n options?: { instructions?: string; force?: boolean },\n ): Promise<CompactionResult> {\n const messages = await this.sessionStore.load(sessionKey);\n const contextWindow = this.getContextWindow();\n const result = await this.sessionStore.compact(\n sessionKey,\n messages,\n contextWindow,\n options?.instructions,\n options?.force ?? true,\n );\n if (result.compacted) {\n await this.sessionStore.save(sessionKey, await this.sessionStore.load(sessionKey));\n this.agentManager.removeAgent(sessionKey);\n }\n log.info({ sessionKey, result }, 'Manual compaction complete');\n return result;\n }\n\n /**\n * One-shot LLM answer for /btw: uses transcript as background only; does not persist to session.\n */\n async btwQuery(sessionKey: string, question: string): Promise<{ text: string; error?: string }> {\n const q = question.trim();\n if (!q) {\n return { text: '', error: 'Empty question.' };\n }\n const messages = await this.sessionStore.load(sessionKey);\n const modelRef = this.modelManager.getModelForSession(sessionKey);\n let model;\n try {\n model = resolveModel(modelRef);\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, modelRef, errorMessage: em }, 'btwQuery: model resolve failed');\n return { text: '', error: `Could not resolve model: ${modelRef}` };\n }\n\n const background = this.formatMessagesForBtw(messages.slice(-40));\n const systemBlock = [\n 'You are answering an ephemeral /btw side question about the current conversation.',\n 'Use the conversation only as background context.',\n 'Answer only the side question. Do not continue or complete any unfinished task from the conversation.',\n 'Do not use tools, shell, or file writes unless the question explicitly requires a tiny code snippet.',\n 'If the question can be answered briefly, answer briefly.',\n ].join('\\n');\n\n const userPrompt = [\n systemBlock,\n '',\n '---',\n 'Conversation background (read-only):',\n background || '(empty)',\n '',\n '---',\n 'Side question:',\n q,\n ].join('\\n');\n\n const userMessage: UserMessage = { role: 'user', content: userPrompt, timestamp: Date.now() };\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 90_000);\n try {\n const out = await complete(model, { messages: [userMessage] }, {\n maxTokens: 2048,\n temperature: 0.4,\n signal: controller.signal as AbortSignal,\n });\n const text = Array.isArray(out.content)\n ? out.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text' && typeof (c as { text?: unknown }).text === 'string')\n .map((c) => c.text || '')\n .join('')\n : '';\n return { text: text.trim() };\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey, errorMessage: em }, 'btwQuery failed');\n return { text: '', error: em };\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private formatMessagesForBtw(messages: AgentMessage[]): string {\n return messages\n .map((m) => {\n const role = m.role;\n let body = '';\n if (typeof m.content === 'string') {\n body = m.content;\n } else if (Array.isArray(m.content)) {\n body = m.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text || '')\n .join('\\n');\n }\n const line = `[${role}]: ${body}`;\n return line.length > 4000 ? `${line.slice(0, 4000)}…` : line;\n })\n .join('\\n\\n');\n }\n\n /** Markdown or JSON summary for /context (prompt assembly is approximated from config + transcript stats). */\n async getSessionContextReport(\n sessionKey: string,\n mode: 'list' | 'detail' | 'json',\n ): Promise<string> {\n const messages = await this.sessionStore.load(sessionKey);\n const cw = this.getContextWindow();\n const stats = this.getSessionStats(sessionKey, messages);\n const cfg = this.effectiveAppConfig() ?? this.config.config!;\n const model = this.modelManager.getModelForSession(sessionKey);\n const sc = await this.sessionConfigStore.get(sessionKey);\n const workspace = effectiveWorkspacePathForSession(cfg, sessionKey, sc);\n const estTokens = await this.sessionStore.estimateTokenUsage(sessionKey, messages);\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const defaults = cfg.agents?.defaults;\n const compaction = defaults?.compaction;\n const tools = defaults?.tools;\n\n const toolsSummary =\n tools && typeof tools === 'object'\n ? Object.entries(tools as Record<string, unknown>)\n .filter(([, v]) => v === true)\n .map(([k]) => k)\n .slice(0, 16)\n .join(', ') || '(none explicitly true)'\n : '(see agents.defaults.tools in config)';\n\n const payload: Record<string, unknown> = {\n sessionKey,\n model,\n workspacePath: workspace,\n agentId: profile.agentId,\n messageCount: messages.length,\n contextWindowNominal: cw,\n estimatedTranscriptTokens: estTokens,\n approxWindowUsage: cw > 0 ? estTokens / cw : null,\n thinkingDefault: defaults?.thinkingDefault,\n reasoningDefault: defaults?.reasoningDefault,\n verboseDefault: defaults?.verboseDefault,\n compaction,\n toolsFlagsOn: toolsSummary,\n windowStats: stats.windowStats,\n compactionRunStats: stats.compactionStats,\n };\n\n if (mode === 'json') {\n return JSON.stringify(payload, null, 2);\n }\n\n const lines: string[] = [\n '📎 *Context overview*',\n '',\n `• Session: \\`${sessionKey}\\``,\n `• Model: \\`${model}\\``,\n `• Agent profile: \\`${profile.agentId}\\``,\n `• Workspace: \\`${workspace}\\``,\n `• Messages: ${messages.length}`,\n `• Est. transcript tokens (rough): ${estTokens}`,\n `• Nominal context budget (4× maxTokens): ${cw}`,\n ];\n if (cw > 0) {\n lines.push(`• Approx. usage vs budget: ${((estTokens / cw) * 100).toFixed(1)}%`);\n }\n lines.push(\n `• Thinking / reasoning / verbose defaults: ${defaults?.thinkingDefault ?? '—'} / ${defaults?.reasoningDefault ?? '—'} / ${defaults?.verboseDefault ?? '—'}`,\n `• Tools (true flags): ${toolsSummary}`,\n '',\n '_Full system prompt, skills, and memory blocks are assembled at agent runtime; use Web settings or logs for deep inspection._',\n );\n\n if (mode === 'detail') {\n lines.push('', '*Compaction config (agents.defaults.compaction):*', '```json');\n lines.push(JSON.stringify(compaction ?? {}, null, 2));\n lines.push('```', '', '*Window stats:*', '```json');\n lines.push(JSON.stringify(stats.windowStats ?? {}, null, 2));\n lines.push('```');\n }\n\n return lines.join('\\n');\n }\n\n getSessionStats(sessionKey: string, messages: AgentMessage[]) {\n return {\n windowStats: this.sessionStore.getWindowStats(messages),\n compactionStats: this.sessionStore.getCompactionStats(sessionKey),\n tokenEstimate: this.sessionStore.estimateTokenUsage(sessionKey, messages),\n };\n }\n\n private async applyResolvedThinkingLevel(sessionKey: string, requestOverride?: string | null): Promise<void> {\n const def = this.effectiveAppConfig()?.agents?.defaults?.thinkingDefault;\n const level = await resolveEffectiveThinkingLevel(\n this.sessionConfigStore,\n sessionKey,\n requestOverride,\n def,\n );\n this.agentManager.setThinkingLevel(sessionKey, level);\n }\n\n /** Resolved thinking level and effective model ref for a session (Web UI). */\n async getSessionAgentConfig(sessionKey: string): Promise<{\n thinkingLevel: ThinkingLevel;\n model: string;\n reasoningLevel: ReasoningLevel;\n effectiveWorkspacePath: string;\n workingDirectoryLocked: boolean;\n }> {\n await this.hydrateSessionModelFromStore(sessionKey);\n const cfg = this.effectiveAppConfig()!;\n const sc = await this.sessionConfigStore.get(sessionKey);\n\n // Ensure model display matches the effective agent profile even before an Agent instance exists.\n // Otherwise, `ModelManager.getModelForSession()` falls back to the global default until the first turn creates the agent.\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const profileModelRef = profile.primaryModelRef?.trim();\n if (profileModelRef) {\n this.modelManager.setSessionProfileDefault(sessionKey, profileModelRef);\n }\n\n const defThink = cfg.agents?.defaults?.thinkingDefault ?? 'medium';\n const level = await resolveEffectiveThinkingLevel(this.sessionConfigStore, sessionKey, null, defThink);\n const defReason = (cfg.agents?.defaults?.reasoningDefault ?? 'off') as ReasoningLevel;\n const reasoningLevel = await resolveEffectiveReasoningLevel(this.sessionConfigStore, sessionKey, defReason);\n const model = this.modelManager.getModelForSession(sessionKey);\n return {\n thinkingLevel: level,\n model,\n reasoningLevel,\n effectiveWorkspacePath: effectiveWorkspacePathForSession(cfg, sessionKey, sc),\n workingDirectoryLocked: Boolean(sc?.workingDirectoryOverride?.trim()),\n };\n }\n\n /**\n * Load session working directory override into AgentManager, ensure directory exists.\n * Call before AgentManager.getOrCreateAgent for this session.\n */\n async hydrateSessionWorkspaceFromStore(sessionKey: string): Promise<void> {\n const cfg = this.config.config;\n if (!cfg) {\n return;\n }\n const loaded = await this.sessionConfigStore.get(sessionKey);\n if (loaded?.workingDirectoryOverride?.trim()) {\n const wdStored = normalizeWorkingDirectoryInput(loaded.workingDirectoryOverride);\n if (wdStored.ok) {\n this.agentManager.setSessionWorkspaceOverride(sessionKey, wdStored.path);\n } else {\n log.warn({ sessionKey }, 'Invalid stored workingDirectoryOverride; ignoring');\n this.agentManager.setSessionWorkspaceOverride(sessionKey, null);\n }\n } else {\n this.agentManager.setSessionWorkspaceOverride(sessionKey, null);\n }\n const effective = effectiveWorkspacePathForSession(cfg, sessionKey, loaded);\n await mkdir(effective, { recursive: true });\n }\n\n /**\n * Sync persisted session workspace override for an isolated cron run (runs may change when the job is edited).\n * Omit or pass empty `workingDirectory` to use the effective agent default workspace for this session key.\n */\n async applyCronJobWorkingDirectory(sessionKey: string, workingDirectory: string | undefined): Promise<void> {\n const raw = workingDirectory?.trim();\n if (raw) {\n const wdNorm = normalizeWorkingDirectoryInput(raw);\n if (wdNorm.ok === false) {\n log.warn({ sessionKey, error: wdNorm.error }, 'Cron job working directory invalid; using agent default');\n await this.clearCronSessionWorkingDirectoryOverride(sessionKey);\n return;\n }\n await mkdir(wdNorm.path, { recursive: true });\n await this.sessionConfigStore.update(sessionKey, { workingDirectoryOverride: wdNorm.path });\n this.agentManager.setSessionWorkspaceOverride(sessionKey, wdNorm.path);\n return;\n }\n await this.clearCronSessionWorkingDirectoryOverride(sessionKey);\n }\n\n private async clearCronSessionWorkingDirectoryOverride(sessionKey: string): Promise<void> {\n const existing = await this.sessionConfigStore.get(sessionKey);\n if (existing?.workingDirectoryOverride) {\n const { workingDirectoryOverride: _removed, ...rest } = existing;\n await this.sessionConfigStore.set(sessionKey, rest);\n }\n this.agentManager.setSessionWorkspaceOverride(sessionKey, null);\n }\n\n /** Workspace root for UI file tree / editor (same as agent tools after hydration). */\n async getEffectiveWorkspacePathForSession(sessionKey: string): Promise<string> {\n await this.hydrateSessionWorkspaceFromStore(sessionKey);\n const cfg = this.config.config!;\n const sc = await this.sessionConfigStore.get(sessionKey);\n return effectiveWorkspacePathForSession(cfg, sessionKey, sc);\n }\n\n async patchSessionAgentConfig(\n sessionKey: string,\n partial: {\n thinkingLevel?: string;\n model?: string | null;\n reasoningLevel?: string;\n workingDirectory?: string;\n },\n ): Promise<{ ok: boolean; error?: string }> {\n if (partial.model !== undefined) {\n if (partial.model === null || partial.model === '') {\n await this.clearSessionModelOverride(sessionKey);\n } else {\n const ok = await this.modelManager.switchModelForSession(sessionKey, partial.model);\n if (!ok) {\n return { ok: false, error: 'Invalid model' };\n }\n await this.sessionConfigStore.update(sessionKey, { modelOverride: partial.model });\n this.agentManager.setModelForSession(sessionKey, partial.model);\n }\n }\n\n if (partial.thinkingLevel !== undefined) {\n const normalized = normalizeThinkLevel(partial.thinkingLevel);\n if (!normalized) {\n return { ok: false, error: 'Invalid thinking level' };\n }\n await this.sessionConfigStore.update(sessionKey, { thinkingLevel: normalized });\n this.agentManager.setThinkingLevel(sessionKey, normalized as ThinkingLevel);\n }\n\n if (partial.reasoningLevel !== undefined) {\n const normalized = normalizeReasoningLevel(partial.reasoningLevel);\n if (!normalized) {\n return { ok: false, error: 'Invalid reasoning level' };\n }\n await this.sessionConfigStore.update(sessionKey, { reasoningLevel: normalized });\n }\n\n if (partial.workingDirectory !== undefined) {\n const cfg = this.config.config;\n if (!cfg) {\n return { ok: false, error: 'Config not loaded' };\n }\n const existing = await this.sessionConfigStore.get(sessionKey);\n const existingRaw = existing?.workingDirectoryOverride?.trim();\n const incoming = partial.workingDirectory.trim();\n\n const priorMessages = await this.sessionStore.load(sessionKey);\n\n if (priorMessages.length > 0) {\n if (!incoming) {\n return { ok: false, error: 'workingDirectory is empty' };\n }\n if (!existingRaw) {\n return {\n ok: false,\n error: 'Working directory can only be set before the first message in this conversation',\n };\n }\n const prev = normalizeWorkingDirectoryInput(existingRaw);\n const next = normalizeWorkingDirectoryInput(incoming);\n if (prev.ok && next.ok && prev.path === next.path) {\n /* idempotent */\n } else {\n return { ok: false, error: 'Working directory is already set for this session' };\n }\n } else {\n if (!incoming) {\n return { ok: false, error: 'workingDirectory is empty' };\n }\n const wdNorm = normalizeWorkingDirectoryInput(incoming);\n switch (wdNorm.ok) {\n case true:\n if (existingRaw) {\n const prev = normalizeWorkingDirectoryInput(existingRaw);\n if (prev.ok && prev.path === wdNorm.path) {\n break;\n }\n }\n await mkdir(wdNorm.path, { recursive: true });\n await this.sessionConfigStore.update(sessionKey, { workingDirectoryOverride: wdNorm.path });\n this.agentManager.setSessionWorkspaceOverride(sessionKey, wdNorm.path);\n this.agentManager.removeAgent(sessionKey);\n break;\n case false:\n return { ok: false, error: wdNorm.error };\n default:\n return { ok: false, error: 'Invalid working directory' };\n }\n }\n }\n\n return { ok: true };\n }\n\n async *processDirectStreaming(\n content: string,\n sessionKey = 'cli:direct',\n attachments?: Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>,\n thinking?: string,\n options?: { signal?: AbortSignal },\n ): AsyncGenerator<{ type: string; [key: string]: unknown }, void, unknown> {\n yield* runProcessDirectStreaming(this.createProcessDirectStreamingDeps(), {\n content,\n sessionKey,\n attachments,\n thinking,\n signal: options?.signal,\n });\n }\n\n /**\n * Best-effort timezone resolution for webchat envelope timestamps.\n * Reads `USER.md` in the resolved workspace and extracts a `Timezone:` line.\n */\n resolveUserTimezoneForSession(sessionKey: string): string | undefined {\n try {\n const workspace = this.agentManager.getResolvedWorkspaceForSession(sessionKey);\n const userPath = join(workspace, 'USER.md');\n if (!existsSync(userPath)) return undefined;\n const raw = readFileSync(userPath, 'utf-8');\n const match = raw.match(/Timezone:\\s*(.+)/i);\n const tz = match?.[1]?.trim();\n return tz || undefined;\n } catch {\n return undefined;\n }\n }\n\n private createProcessDirectStreamingDeps(): ProcessDirectStreamingDeps {\n return {\n log,\n parseSessionKey: (sk) => this.parseSessionKey(sk),\n initDirectStreamingSession: (sk, channel, chatId) => this.initSessionContext(sk, channel, chatId),\n registerWebchatSsePublisher: (sk, publisher) => {\n this.webchatSseEnqueueBySession.set(sk, publisher);\n },\n unregisterWebchatSsePublisher: (sk) => {\n this.webchatSseEnqueueBySession.delete(sk);\n },\n agentManager: this.agentManager,\n hydrateSessionWorkspaceFromStore: (sk) => this.hydrateSessionWorkspaceFromStore(sk),\n hydrateSessionModelFromStore: (sk) => this.hydrateSessionModelFromStore(sk),\n agentEventHandler: this.agentEventHandler,\n sessionStore: this.sessionStore,\n prepareLoadedSessionMessages: (sk, msgs) => this.prepareLoadedSessionMessages(sk, msgs),\n modelManager: this.modelManager,\n applyResolvedThinkingLevel: (sk, t) => this.applyResolvedThinkingLevel(sk, t),\n getConfig: () => this.effectiveAppConfig(),\n sessionConfigStore: this.sessionConfigStore,\n attachmentRootsForSession: (sk) => this.attachmentRootsForSession(sk),\n agentOrchestrator: this.agentOrchestrator,\n commandHandler: this.commandHandler,\n prepareInboundAttachments: (sk, att) => this.prepareInboundAttachments(sk, att),\n buildMessageContent: (text, prepared, sk) => this.buildMessageContent(text, prepared, sk),\n persistAgentSessionMessages: (sk) => this.persistAgentSessionMessages(sk),\n maybeEmitWebchatTts: (sk, hadVoice) => this.maybeEmitWebchatTts(sk, hadVoice),\n endDirectRequestContext: () => this.endDirectRequestContext(),\n };\n }\n\n /**\n * Inject an SSE event into an in-flight webchat stream (same queue as tokens/tools).\n */\n enqueueWebchatSseEvent(sessionKey: string, event: { type: string; [key: string]: unknown }): void {\n const pub = this.webchatSseEnqueueBySession.get(sessionKey);\n if (pub) {\n pub(event);\n }\n }\n\n /**\n * Queue a steering user message into pi-agent's in-flight run (delivered after current tool work, before the next LLM call).\n * See `Agent.steer` in `@mariozechner/pi-agent-core`.\n */\n async steerWebchatSession(sessionKey: string, text: string): Promise<boolean> {\n const trimmed = text.trim();\n if (!trimmed) return false;\n try {\n await this.hydrateSessionWorkspaceFromStore(sessionKey);\n const agent = this.agentManager.getOrCreateAgent(sessionKey);\n const msg: AgentMessage = {\n role: 'user',\n content: [{ type: 'text', text: trimmed }],\n timestamp: Date.now(),\n };\n agent.steer(msg);\n return true;\n } catch (err) {\n log.warn({ err, sessionKey }, 'steerWebchatSession failed');\n return false;\n }\n }\n\n /**\n * Generate TTS for webchat when config allows, persist under agent home `tts/`, attach to last assistant turn.\n */\n private async maybeEmitWebchatTts(\n sessionKey: string,\n hadInboundVoice: boolean,\n ): Promise<{ type: 'tts_audio'; workspaceRelativePath: string; mimeType: string; name: string } | null> {\n const ttsConfig = mergeTtsConfigFromAppConfig(this.config.config?.tts);\n if (!isTTSAvailable(ttsConfig)) {\n return null;\n }\n const decision = shouldUseTTS(ttsConfig, hadInboundVoice);\n if (!decision.useTTS) {\n return null;\n }\n const text = this.agentManager.getLastAssistantContent(sessionKey)?.trim();\n if (!text) {\n return null;\n }\n try {\n const webOut = getChannelOutputFormat('webchat');\n const fmt = webOut.format as 'opus' | 'mp3' | 'wav';\n const ttsResult = await speak(text, ttsConfig, {\n appConfig: this.config.config,\n tts: { format: fmt },\n });\n const { buffer, format } = await compressAudio(\n Buffer.from(ttsResult.audio),\n ttsResult.format,\n webOut.format === 'mp3' ? 'mp3' : 'opus',\n );\n const normalizedMime =\n format === 'opus' || format === 'ogg'\n ? 'audio/ogg'\n : format === 'mp3' || format === 'mpeg'\n ? 'audio/mpeg'\n : format === 'wav'\n ? 'audio/wav'\n : `audio/${format}`;\n const persisted = await persistOutboundTtsAudio(\n resolveAgentHomeDir(this.config.config!, extractProfileAgentId(sessionKey, this.config.config!)),\n sessionKey,\n buffer,\n format,\n );\n await this.appendAttachmentToLastAssistant(sessionKey, {\n type: 'audio',\n mimeType: normalizedMime,\n name: persisted.name,\n size: persisted.size,\n workspaceRelativePath: persisted.workspaceRelativePath,\n });\n return {\n type: 'tts_audio',\n workspaceRelativePath: persisted.workspaceRelativePath,\n mimeType: normalizedMime,\n name: persisted.name,\n };\n } catch (err) {\n log.warn({ err, sessionKey }, 'Webchat TTS failed');\n return null;\n }\n }\n\n private async appendAttachmentToLastAssistant(\n sessionKey: string,\n att: {\n type: string;\n mimeType: string;\n name: string;\n size: number;\n workspaceRelativePath: string;\n },\n ): Promise<void> {\n const loaded = await this.sessionStore.load(sessionKey);\n for (let i = loaded.length - 1; i >= 0; i--) {\n const m = loaded[i] as { role?: string; attachments?: unknown[] };\n if (m.role === 'assistant') {\n const prev = (m.attachments ?? []) as Array<{ workspaceRelativePath?: string }>;\n if (prev.some((x) => x.workspaceRelativePath === att.workspaceRelativePath)) {\n return;\n }\n const next = [...prev, att];\n loaded[i] = { ...m, attachments: next } as unknown as AgentMessage;\n await this.sessionStore.save(sessionKey, loaded);\n return;\n }\n }\n }\n\n async processDirect(\n content: string,\n sessionKey = 'cli:direct',\n attachments?: Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>,\n thinking?: string,\n ): Promise<string> {\n const { channel, chatId } = this.parseSessionKey(sessionKey);\n this.initSessionContext(sessionKey, channel, chatId);\n\n try {\n await this.hydrateSessionWorkspaceFromStore(sessionKey);\n // Get or create agent for this session\n const agent = this.agentManager.getOrCreateAgent(sessionKey);\n\n await this.hydrateSessionModelFromStore(sessionKey);\n\n const loaded = await this.sessionStore.load(sessionKey);\n agent.state.messages = this.prepareLoadedSessionMessages(sessionKey, loaded);\n\n await this.modelManager.applyModelForSession(agent, sessionKey);\n await this.applyResolvedThinkingLevel(sessionKey, thinking);\n\n const prepared = await this.prepareInboundAttachments(sessionKey, attachments);\n\n const cmd = parseSlashCommand(content);\n if (cmd && commandRegistry.has(cmd.command)) {\n const { aggregatedText } = await this.commandHandler.executeCommandAndAggregateReply(cmd.command, cmd.args, {\n sessionKey,\n channel,\n chatId,\n senderId: '',\n isGroup: false,\n });\n await this.persistAgentSessionMessages(sessionKey);\n return aggregatedText;\n }\n\n const textForDirect = content.trimStart().startsWith('/skill:')\n ? this.agentManager.expandSkillUserText(content)\n : content;\n const messageContent = await this.buildMessageContent(textForDirect, prepared, sessionKey);\n\n const userMessage = {\n role: 'user' as const,\n content: messageContent,\n timestamp: Date.now(),\n };\n const userPlain = extractAgentUserPlainText(userMessage);\n const userMessageForModel = await this.agentManager.applyMemoryPrefetchToUserMessage(\n userMessage,\n sessionKey,\n );\n\n await runAgentTurnWithModelFallbacks({\n agent,\n sessionKey,\n modelManager: this.modelManager,\n userMessage: userMessageForModel,\n log,\n getConfig: () => this.config.config,\n beforeUserPrompt: () => this.agentManager.beginBackgroundReviewUserTurn(sessionKey),\n });\n\n this.agentManager.afterAgentTurn(sessionKey, userPlain);\n this.agentManager.scheduleBackgroundReviewAfterUserTurn(sessionKey);\n\n const response = this.agentManager.getLastAssistantContent(sessionKey) || '';\n await this.persistAgentSessionMessages(sessionKey);\n\n return response;\n } finally {\n this.endDirectRequestContext();\n // Don't unsubscribe here - keep the session agent alive for future messages\n }\n }\n\n private async handleInboundMessage(msg: InboundMessage): Promise<void> {\n const requestId = inboundMessageLogRequestId(msg);\n\n await runWithLogContext({ requestId }, async () => {\n const routing = await this.messageRouter.routeMessage(msg);\n const { context, isCommand, command, commandArgs } = routing;\n\n const sessionContext: SessionContext = {\n sessionKey: context.sessionKey,\n channel: context.channel,\n chatId: context.chatId,\n senderId: context.senderId || '',\n isGroup: context.isGroup || false,\n metadata: {\n transcribedVoice: msg.metadata?.transcribedVoice === true,\n },\n };\n\n updateAsyncLogContext({ sessionId: sessionContext.sessionKey });\n\n this.sessionContextManager.setContext(sessionContext);\n this.feedbackCoordinator.setContext(sessionContext);\n\n // Setup event handling for this session\n this.setupSessionEventHandling(sessionContext.sessionKey);\n\n await this.sessionLifecycleManager.startSession(sessionContext);\n\n /** Declared on the function so `finally` can clear typing after outbound (TTS + send). */\n let typingController: TypingController | null = null;\n\n try {\n if (msg.channel === 'system') {\n await this.handleSystemMessage(msg, sessionContext);\n return;\n }\n\n if (isCommand && command) {\n const handled = await this.commandHandler.executeCommand(command, commandArgs || '', {\n sessionKey: sessionContext.sessionKey,\n channel: sessionContext.channel,\n chatId: sessionContext.chatId,\n senderId: sessionContext.senderId,\n isGroup: sessionContext.isGroup,\n });\n\n if (handled) {\n return;\n }\n }\n\n // Start continuous typing indicator (renews every 5 seconds)\n if (msg.channel !== 'cli') {\n typingController = createTypingController({\n intervalSeconds: 5,\n onStart: async () => {\n await this.bus.publishOutbound({\n channel: msg.channel,\n chat_id: msg.chat_id,\n content: '',\n type: 'typing_on',\n metadata: {\n accountId: msg.metadata?.accountId,\n threadId: msg.metadata?.threadId,\n },\n });\n },\n onStop: async () => {\n await this.bus.publishOutbound({\n channel: msg.channel,\n chat_id: msg.chat_id,\n content: '',\n type: 'typing_off',\n metadata: {\n accountId: msg.metadata?.accountId,\n threadId: msg.metadata?.threadId,\n },\n });\n },\n });\n typingController.start();\n }\n\n if (this.channelManagerRef && msg.channel !== 'cli') {\n const meta = msg.metadata as Record<string, unknown> | undefined;\n const streamHandle = this.channelManagerRef.startStream(\n msg.channel,\n msg.chat_id,\n meta?.accountId as string | undefined,\n {\n threadId: meta?.threadId as string | undefined,\n replyToMessageId: meta?.messageId as string | undefined,\n },\n );\n\n if (streamHandle) {\n this.setStreamHandle(streamHandle as StreamHandle);\n }\n }\n\n await this.agentOrchestrator.process(msg, sessionContext);\n } finally {\n await this.sessionLifecycleManager.endSession(sessionContext);\n await this.streamManager.end();\n try {\n await this.sendFinalResponse(msg, sessionContext);\n } finally {\n // After outbound (incl. TTS); previously we cleared typing right after LLM finished, so Weixin showed typing_off before the message.\n await typingController?.stop();\n }\n this.feedbackCoordinator.endTask();\n this.sessionContextManager.clearContext();\n this.feedbackCoordinator.clearContext();\n }\n });\n }\n\n private async handleSystemMessage(msg: InboundMessage, context: SessionContext): Promise<void> {\n log.debug({ sessionKey: context.sessionKey }, 'Processing system message');\n\n await this.hydrateSessionWorkspaceFromStore(context.sessionKey);\n\n // Get or create agent for this session\n const agent = this.agentManager.getOrCreateAgent(context.sessionKey);\n\n const messages = await this.sessionStore.load(context.sessionKey);\n await this.checkAndCompact(context.sessionKey, messages);\n const refreshedMessages = await this.sessionStore.load(context.sessionKey);\n agent.state.messages = this.prepareLoadedSessionMessages(context.sessionKey, refreshedMessages);\n\n const systemMessage: AgentMessage = {\n role: 'user',\n content: [{ type: 'text', text: `[System: ${msg.sender_id}] ${msg.content}` }],\n timestamp: Date.now(),\n };\n\n try {\n await agent.prompt(systemMessage);\n await agent.waitForIdle();\n\n const finalContent = this.agentManager.getLastAssistantContent(context.sessionKey);\n if (finalContent) {\n const hookResult = await this.hookHandler.runMessageSending(\n context.chatId,\n finalContent,\n context.channel,\n );\n if (hookResult.send) {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: hookResult.content || finalContent,\n type: 'message',\n });\n }\n }\n\n await this.persistAgentSessionMessages(context.sessionKey);\n } catch (error) {\n const em = error instanceof Error ? error.message : String(error);\n log.error(\n {\n err: error,\n errorMessage: em,\n sessionKey: context.sessionKey,\n channel: context.channel,\n chatId: context.chatId,\n senderId: msg.sender_id,\n },\n `System message handling failed: ${em}`,\n );\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: '❌ An error occurred while processing the system message.',\n type: 'message',\n });\n }\n }\n\n /**\n * Setup event handling for a specific session\n */\n private setupSessionEventHandling(sessionKey: string): void {\n // If already subscribed, skip\n if (this.sessionUnsubscribers.has(sessionKey)) {\n return;\n }\n\n const unsubscribe = this.agentManager.subscribeToSession(sessionKey, (event) => {\n this.handleSessionEvent(sessionKey, event);\n });\n\n if (unsubscribe) {\n this.sessionUnsubscribers.set(sessionKey, unsubscribe);\n }\n }\n\n /**\n * Handle events from a specific session's agent\n */\n private handleSessionEvent(sessionKey: string, event: AgentEvent): void {\n const currentContext = this.sessionContextManager.getContext();\n if (!currentContext) {\n // Inbound `finally` clears context before trailing agent `message_update` events finish — ignore (not a bug).\n return;\n }\n\n if (currentContext.sessionKey !== sessionKey) {\n // Event from a different session — still process with current context where applicable\n this.agentEventHandler.handle(event, currentContext);\n return;\n }\n\n // Handle streaming updates for the current session\n if (event.type === 'message_update') {\n const msgEvent = event as Extract<AgentEvent, { type: 'message_update' }>;\n if (msgEvent.message?.role === 'assistant') {\n const content = msgEvent.message.content;\n const text = Array.isArray(content)\n ? extractTextContent(content as Array<{ type: string; text?: string }>)\n : String(content);\n\n this.streamManager.update(text);\n }\n }\n\n this.agentEventHandler.handle(event, currentContext);\n }\n\n private async checkAndCompact(sessionKey: string, messages: AgentMessage[]): Promise<void> {\n const contextWindow = this.getContextWindow();\n const prep = this.sessionStore.prepareCompaction(sessionKey, messages, contextWindow);\n if (!prep.needsCompaction) return;\n\n log.info({ sessionKey, reason: prep.stats?.reason, usagePercent: prep.stats?.usagePercent }, 'Session needs compaction');\n\n const result = await this.sessionStore.compact(sessionKey, messages, contextWindow, undefined, false);\n await this.hookHandler.trigger('after_compaction', {\n messageCount: messages.length,\n tokenCount: result.tokensBefore,\n compactedCount: messages.length - result.firstKeptIndex,\n });\n log.info({ sessionKey, tokensBefore: result.tokensBefore, tokensAfter: result.tokensAfter }, 'Session compacted');\n }\n\n private getContextWindow(): number {\n const defaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n return defaults?.maxTokens ? defaults.maxTokens * 4 : 128000;\n }\n\n private async sendFinalResponse(\n msg: InboundMessage,\n sessionContext: SessionContext\n ): Promise<void> {\n if (this.streamManager.consumeSkipFinalOutbound()) {\n return;\n }\n\n const finalContent = this.agentManager.getLastAssistantContent(sessionContext.sessionKey);\n if (!finalContent?.trim()) return;\n\n const ackMax =\n this.config.config?.gateway?.heartbeat?.ackMaxChars ?? DEFAULT_ACK_MAX_CHARS;\n if (shouldSilence(finalContent, ackMax) || finalContent.trim() === NO_REPLY) {\n log.debug(\n { sessionKey: sessionContext.sessionKey },\n 'Silent reply — skipping outbound',\n );\n return;\n }\n\n const hookResult = await this.hookHandler.runMessageSending(\n sessionContext.chatId,\n finalContent,\n sessionContext.channel,\n );\n if (!hookResult.send) return;\n\n // TTS is handled by ChannelManager, just send text message here\n await this.bus.publishOutbound({\n channel: sessionContext.channel,\n chat_id: sessionContext.chatId,\n content: hookResult.content || finalContent,\n type: 'message',\n metadata: {\n accountId: msg.metadata?.accountId,\n threadId: msg.metadata?.threadId,\n transcribedVoice: sessionContext.metadata?.transcribedVoice,\n },\n });\n }\n\n /** Extension hooks for ChannelManager outbound pipeline (Gateway). */\n async invokeOutboundMessageSending(\n to: string,\n content: string,\n channel: string,\n ): Promise<{ send: boolean; content?: string; reason?: string }> {\n return this.hookHandler.runMessageSending(to, content, channel);\n }\n\n async invokeOutboundMessageSent(\n to: string,\n content: string,\n success: boolean,\n error: string | undefined,\n channel: string,\n ): Promise<void> {\n return this.hookHandler.runMessageSent(to, content, success, error, channel);\n }\n\n private dispose(): void {\n this.sessionTracker.dispose();\n\n // Unsubscribe from all session agents\n for (const unsubscribe of this.sessionUnsubscribers.values()) {\n unsubscribe();\n }\n this.sessionUnsubscribers.clear();\n\n // Dispose all agent instances\n this.agentManager.dispose();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"service.js","names":["parseRoutingSessionKey"],"sources":["../../../src/agent/service.ts"],"sourcesContent":["import type { AgentEvent, AgentMessage, ThinkingLevel } from '@mariozechner/pi-agent-core';\nimport { MessageBusShutdownError, type MessageBus, type InboundMessage } from '../infra/bus/index.js';\nimport { type Config, getAgentDefaultModelRef } from '../config/schema.js';\nimport { maybeAutoTitleSessionStore } from '../session/session-title.js';\nimport type { ChannelManager } from '../channels/manager.js';\nimport { INTERNAL_OUTBOUND_DROP_CHANNEL } from '../channels/internal-outbound.js';\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport {\n SessionStore,\n SessionConfigStore,\n resolveEffectiveThinkingLevel,\n resolveEffectiveReasoningLevel,\n effectiveWorkspacePathForSession,\n normalizeWorkingDirectoryInput,\n type CompactionConfig,\n type WindowConfig,\n} from '../session/index.js';\nimport {\n normalizeThinkLevel,\n normalizeReasoningLevel,\n type ThinkLevel,\n type ReasoningLevel,\n} from './transcript/thinking-types.js';\nimport { createLogger, runWithLogContext, updateAsyncLogContext } from '../utils/logger.js';\nimport { ExtensionHookRunner } from '../extensions/index.js';\nimport { loadBootstrapFiles, extractTextContent } from './context/workspace.js';\nimport { SessionTracker } from './session/tracker.js';\nimport { ModelManager } from './models/index.js';\nimport { commandRegistry, initializeCommands } from '../chat-commands/index.js';\nimport { parseSlashCommand } from '../chat-commands/command-parse.js';\nimport { ProgressFeedbackManager } from './lifecycle/progress.js';\nimport { HookHandler } from './lifecycle/hook-handler.js';\nimport { ToolErrorTracker } from './tools/error-tracker.js';\nimport { RequestLimiter } from './models/request-limiter.js';\nimport { SystemReminder } from './prompt/system-reminder.js';\nimport { ToolUsageAnalyzer } from './tools/usage-analyzer.js';\nimport { ToolChainTracker } from './tools/chain-tracker.js';\nimport { ErrorPatternMatcher } from './tools/error-pattern-matcher.js';\nimport { ContextMiddleware, SelfVerifyMiddleware } from './middleware/index.js';\nimport { LifecycleManager } from './lifecycle/index.js';\nimport { CompactionLifecycleHandler } from './lifecycle/handlers/compaction.js';\n\nimport { MessageRouter, CommandHandler, StreamManager } from './messaging/index.js';\nimport { SessionContextManager, SessionLifecycleManager, type SessionContext } from './session/index.js';\nimport { AgentOrchestrator, AgentEventHandler } from './orchestration/index.js';\nimport { runAgentTurnWithModelFallbacks } from './orchestration/run-agent-turn-with-fallbacks.js';\nimport { FeedbackCoordinator } from './feedback/index.js';\nimport { AgentManager, type SkillCatalogEntry } from './agent-manager.js';\nimport { extractAgentUserPlainText } from './memory/user-message-text.js';\nimport { inboundMessageLogRequestId } from './service-inbound-utils.js';\nimport type { AgentServiceConfig, StreamHandle } from './service.types.js';\nimport {\n runProcessDirectStreaming,\n type ProcessDirectStreamingDeps,\n} from './service/process-direct-streaming.js';\n\nimport {\n resolveAgentHomeDir,\n resolveDefaultAgentId,\n} from './agent-scope.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../routing/session-key.js';\nimport { extractProfileAgentId, resolveAgentBootstrapDir } from '../config/agent-profile.js';\nimport { DEFAULT_ACK_MAX_CHARS, NO_REPLY, shouldSilence } from '../heartbeat/tokens.js';\nimport { createTypingController, type TypingController } from './lifecycle/typing.js';\nimport { cleanTrailingErrors, sanitizeMessages } from './memory/message-sanitizer.js';\nimport { DREAMING_CRON_NAME, DREAMING_CRON_TAG, DREAMING_SWEEP_TOKEN } from './memory/dreaming/constants.js';\nimport { resolveDreamingConfig } from './memory/dreaming/config.js';\nimport {\n tryApplySessionTranscriptHygiene,\n tryApplySessionTranscriptHygieneForPersistence,\n} from './transcript/transcript-hygiene.js';\nimport {\n persistInboundAttachmentsToWorkspace,\n formatInboundFileTextBlock,\n type InternalAttachmentRoots,\n} from '../channels/attachments/inbound-persist.js';\nimport { expandAtFileMentionsInPlainText } from './context/expand-at-file-mentions.js';\nimport { resolveInboundImageContentParts } from './image/inbound-image-handling.js';\nimport { getDefaultModelSync, resolveModel } from '../providers/index.js';\nimport { complete, type UserMessage } from '@mariozechner/pi-ai';\nimport { resolveEffectiveAgentProfileForSession } from '../config/agent-profile.js';\nimport type { CompactionResult } from './memory/compaction.js';\nimport { persistOutboundTtsAudio } from '../channels/attachments/outbound-tts-persist.js';\nimport { compressAudio } from '../voice/tts/audio.js';\nimport { speak } from '../voice/tts/index.js';\nimport { mergeTtsConfigFromAppConfig } from '../voice/tts/merge-config.js';\nimport { applyConfigOverrides } from '../config/runtime-overrides.js';\nimport { shouldUseTTS, getChannelOutputFormat } from '../voice/tts/service.js';\nimport { isTTSAvailable } from '../voice/tts/factory.js';\n\nexport type { AgentServiceConfig, AgentContext, StreamHandle } from './service.types.js';\n\nconst log = createLogger('AgentService');\n\nexport class AgentService {\n private sessionStore: SessionStore;\n private sessionConfigStore: SessionConfigStore;\n private hookRunner?: ExtensionHookRunner;\n private running = false;\n private agentId: string;\n private workspaceDir: string;\n private bootstrapFiles: ReturnType<typeof loadBootstrapFiles> = [];\n private channelManagerRef: ChannelManager | null = null;\n private bus: MessageBus;\n private config: AgentServiceConfig;\n\n private sessionTracker: SessionTracker;\n private modelManager: ModelManager;\n private progressManager: ProgressFeedbackManager;\n private hookHandler: HookHandler;\n private lifecycleManager: LifecycleManager;\n private errorTracker: ToolErrorTracker;\n private requestLimiter: RequestLimiter;\n private systemReminder: SystemReminder;\n private toolUsageAnalyzer: ToolUsageAnalyzer;\n private toolChainTracker: ToolChainTracker;\n private errorPatternMatcher: ErrorPatternMatcher;\n private selfVerifyMiddleware: SelfVerifyMiddleware;\n private contextMiddleware: ContextMiddleware;\n\n private messageRouter: MessageRouter;\n private commandHandler: CommandHandler;\n private streamManager: StreamManager;\n private sessionContextManager: SessionContextManager;\n private sessionLifecycleManager: SessionLifecycleManager;\n private agentOrchestrator: AgentOrchestrator;\n private agentEventHandler: AgentEventHandler;\n private feedbackCoordinator: FeedbackCoordinator;\n private agentManager: AgentManager;\n\n /** Webchat SSE queue pushers for `clarify_request` and similar mid-turn UI events. */\n private webchatSseEnqueueBySession = new Map<\n string,\n (event: { type: string; [key: string]: unknown }) => void\n >();\n\n // Track event unsubscribers per session\n private sessionUnsubscribers: Map<string, () => void> = new Map();\n\n private effectiveAppConfig(): Config | undefined {\n const base = this.config.config;\n return base ? applyConfigOverrides(base) : undefined;\n }\n\n constructor(bus: MessageBus, config: AgentServiceConfig) {\n this.bus = bus;\n this.config = config;\n this.agentId = `agent-${Date.now()}`;\n this.workspaceDir = config.workspace;\n\n if (config.config) {\n const aid = resolveDefaultAgentId(config.config);\n this.bootstrapFiles = loadBootstrapFiles(resolveAgentBootstrapDir(config.config, aid));\n } else {\n this.bootstrapFiles = [];\n }\n\n this.sessionTracker = new SessionTracker();\n this.modelManager = new ModelManager({\n defaultModel: config.model,\n config: config.config,\n });\n\n initializeCommands();\n log.debug('Command system initialized');\n\n this.sessionStore = this.createSessionStore();\n const appCfgForPaths = this.config.config;\n if (!appCfgForPaths) {\n throw new Error('AgentService requires config.config for session paths');\n }\n const defaultAid = resolveDefaultAgentId(appCfgForPaths);\n const defaultAgentHome = resolveAgentHomeDir(appCfgForPaths, defaultAid);\n this.sessionConfigStore = new SessionConfigStore(defaultAgentHome);\n\n this.hookRunner = this.createHookRunner();\n this.hookHandler = new HookHandler({\n hookRunner: this.hookRunner,\n agentId: this.agentId,\n get sessionKey() { return this.currentContext?.sessionKey; },\n });\n\n this.progressManager = this.createProgressManager();\n this.initializeReliabilityModules();\n\n this.lifecycleManager = new LifecycleManager();\n this.initializeLifecycleHandlers();\n\n this.streamManager = new StreamManager();\n this.sessionContextManager = new SessionContextManager();\n this.feedbackCoordinator = new FeedbackCoordinator({\n progressManager: this.progressManager,\n bus,\n });\n\n // Initialize AgentManager\n this.agentManager = new AgentManager({\n workspace: config.workspace,\n model: config.model,\n config: config.config,\n extensionRegistry: config.extensionRegistry,\n hookRunner: this.hookRunner,\n bus,\n getCurrentContext: () => this.sessionContextManager.getContext(),\n getSessionStore: () => this.sessionStore,\n getModelManager: () => this.modelManager,\n thinkingLevel: config.thinkingLevel,\n reasoningLevel: config.reasoningLevel,\n verboseLevel: config.verboseLevel,\n gatewayClarify: config.gatewayClarify,\n getCronService: config.getCronService,\n });\n\n this.agentEventHandler = new AgentEventHandler({\n progressManager: this.progressManager,\n errorTracker: this.errorTracker,\n requestLimiter: this.requestLimiter,\n lifecycleManager: this.lifecycleManager,\n toolChainTracker: this.toolChainTracker,\n selfVerifyMiddleware: this.selfVerifyMiddleware,\n systemReminder: this.systemReminder,\n toolUsageAnalyzer: this.toolUsageAnalyzer,\n errorPatternMatcher: this.errorPatternMatcher,\n modelManager: this.modelManager,\n });\n\n this.agentOrchestrator = new AgentOrchestrator({\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n eventHandler: this.agentEventHandler,\n feedbackCoordinator: this.feedbackCoordinator,\n sessionConfigStore: this.sessionConfigStore,\n hydrateSessionWorkspaceFromStore: (sessionKey) => this.hydrateSessionWorkspaceFromStore(sessionKey),\n getConfig: () => this.effectiveAppConfig(),\n getThinkingDefault: () => this.effectiveAppConfig()?.agents?.defaults?.thinkingDefault,\n getThinkingDefaultForSession: (sessionKey: string) =>\n this.agentManager.getThinkingDefaultForSession(sessionKey),\n workspaceRoot: this.workspaceDir,\n getWorkspaceRootForSession: (sessionKey: string) =>\n this.agentManager.getResolvedWorkspaceForSession(sessionKey),\n getAgentInternalStorageRootForSession: (sessionKey: string) =>\n resolveAgentHomeDir(this.config.config!, extractProfileAgentId(sessionKey, this.config.config!)),\n enqueueAutoTitle: (sessionKey: string) => this.enqueueMaybeAutoTitleAfterPersist(sessionKey),\n });\n\n this.messageRouter = new MessageRouter();\n this.commandHandler = new CommandHandler({\n config: config.config!,\n bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n applySessionThinkingLevel: (sessionKey: string, level: ThinkLevel) => {\n this.agentManager.setThinkingLevel(sessionKey, level as ThinkingLevel);\n },\n getCurrentModel: () => this.agentOrchestrator.getCurrentModel(),\n switchModelForSession: (sessionKey: string, modelId: string) =>\n this.switchModelForSession(sessionKey, modelId),\n invalidateAgentSession: (sessionKey: string) => {\n this.agentManager.removeAgent(sessionKey);\n },\n abortSessionTurn: async (sessionKey: string) => {\n await this.streamManager.abort();\n this.agentOrchestrator.abort(sessionKey);\n },\n compactSession: (sessionKey, options) => this.compactSession(sessionKey, options),\n btwQuery: (sessionKey, question) => this.btwQuery(sessionKey, question),\n getSessionContextReport: (sessionKey, mode) => this.getSessionContextReport(sessionKey, mode),\n });\n\n this.sessionLifecycleManager = new SessionLifecycleManager(\n this.sessionStore,\n this.sessionTracker,\n this.lifecycleManager\n );\n\n // Register signal handlers only if not running as an Electron subprocess.\n // In Electron, the parent process manages the lifecycle and signals should not trigger disposal.\n const isElectronSubprocess = !!process.env.ELECTRON_RUN_AS_NODE;\n if (!isElectronSubprocess) {\n process.on('SIGINT', () => this.dispose());\n process.on('SIGTERM', () => this.dispose());\n }\n\n log.info('AgentService initialized');\n }\n\n private attachmentRootsForSession(sessionKey: string): InternalAttachmentRoots {\n const cfg = this.config.config!;\n return {\n agentHome: resolveAgentHomeDir(cfg, extractProfileAgentId(sessionKey, cfg)),\n };\n }\n\n private createSessionStore(): SessionStore {\n const sessionStoreDefaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n const windowConfig: Partial<WindowConfig> = {\n maxMessages: 100,\n keepRecentMessages: sessionStoreDefaults?.maxToolIterations || 20,\n preserveSystemMessages: true,\n };\n const compactionConfig: Partial<CompactionConfig> = {\n enabled: sessionStoreDefaults?.compaction?.enabled ?? true,\n mode: (sessionStoreDefaults?.compaction?.mode as 'extractive' | 'abstractive' | 'structured') || 'abstractive',\n reserveTokens: sessionStoreDefaults?.compaction?.reserveTokens || 8000,\n triggerThreshold: sessionStoreDefaults?.compaction?.triggerThreshold || 0.8,\n minMessagesBeforeCompact: sessionStoreDefaults?.compaction?.minMessagesBeforeCompact || 10,\n keepRecentMessages: sessionStoreDefaults?.compaction?.keepRecentMessages || 10,\n evictionWindow: sessionStoreDefaults?.compaction?.evictionWindow || 0.2,\n retentionWindow: sessionStoreDefaults?.compaction?.retentionWindow || 6,\n };\n const appCfg = this.config.config;\n if (!appCfg) {\n throw new Error('AgentService requires config.config for session store paths');\n }\n return new SessionStore(\n {\n config: appCfg,\n agentId: resolveDefaultAgentId(appCfg),\n },\n windowConfig,\n compactionConfig,\n );\n }\n\n private createHookRunner(): ExtensionHookRunner | undefined {\n if (!this.config.extensionRegistry) return undefined;\n\n return new ExtensionHookRunner(this.config.extensionRegistry, {\n catchErrors: true,\n logger: {\n info: (msg: string) => log.info({ hook: true }, msg),\n warn: (msg: string) => log.warn({ hook: true }, msg),\n error: (msg: string) => log.error({ hook: true }, msg),\n },\n });\n }\n\n private createProgressManager(): ProgressFeedbackManager {\n return new ProgressFeedbackManager({\n level: 'normal',\n showThinking: true,\n streamToolProgress: true,\n heartbeatEnabled: true,\n heartbeatIntervalMs: 20000,\n longTaskThresholdMs: 30000,\n });\n }\n\n private initializeReliabilityModules(): void {\n const defaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n\n this.errorTracker = new ToolErrorTracker({\n maxFailuresPerTool: defaults?.maxToolFailuresPerTurn || 3,\n maxTotalFailures: defaults?.maxToolFailuresPerTurn ? defaults.maxToolFailuresPerTurn + 2 : 5,\n resetOnTurnEnd: true,\n });\n\n this.selfVerifyMiddleware = new SelfVerifyMiddleware({\n maxEditsPerFile: 5,\n enablePreCompletionCheck: true,\n minTurnsForVerification: 4,\n resetOnVerification: true,\n });\n\n this.requestLimiter = new RequestLimiter({\n maxRequestsPerTurn: defaults?.maxRequestsPerTurn || 50,\n warnThreshold: 0.8,\n softLimit: false,\n });\n\n this.systemReminder = new SystemReminder({\n enabled: true,\n appendToToolResults: true,\n maxRemindersPerTurn: 3,\n });\n\n this.toolUsageAnalyzer = new ToolUsageAnalyzer({\n enabled: true,\n lowUsageThreshold: 5,\n veryLowUsageThreshold: 1,\n minCallsForAnalysis: 100,\n reportIntervalMs: 60 * 60 * 1000,\n });\n\n this.toolChainTracker = new ToolChainTracker({\n enabled: true,\n maxChainsPerSession: 10,\n maxNodesPerChain: 100,\n trackParams: true,\n trackResults: true,\n autoPrune: true,\n });\n\n this.errorPatternMatcher = new ErrorPatternMatcher({\n enabled: true,\n defaultMaxRetries: 1,\n logMatches: true,\n });\n\n // Initialize context middleware for automatic request tracking\n this.contextMiddleware = new ContextMiddleware();\n }\n\n private initializeLifecycleHandlers(): void {\n this.lifecycleManager.on('llm_response', new CompactionLifecycleHandler({\n minMessages: 20,\n maxTokens: 8000,\n preserveReasoning: true,\n accumulateUsage: true,\n }));\n\n log.debug(\n { handlers: this.lifecycleManager.getRegisteredHandlers() },\n 'Lifecycle handlers initialized'\n );\n }\n\n setChannelManager(channelManager: ChannelManager): void {\n this.modelManager.setChannelManager(channelManager);\n this.channelManagerRef = channelManager;\n }\n\n /**\n * Apply config after save or hot reload so the default model updates without restarting the gateway.\n */\n applyAgentDefaultsFromConfig(config: Config): void {\n this.config.config = config;\n const ref = getAgentDefaultModelRef(config);\n this.config.model = ref;\n this.modelManager.updateFromConfig(config);\n this.agentManager.updateAgentDefaults(config);\n this.commandHandler.updateAgentConfig(config);\n }\n\n getSkillCatalog(): SkillCatalogEntry[] {\n return this.agentManager.getSkillCatalog();\n }\n\n getSkillMarkdownSource(skillName: string): { name: string; markdown: string } | null {\n return this.agentManager.getSkillMarkdownSource(skillName);\n }\n\n refreshSkillsAfterDiskChange(): void {\n this.agentManager.refreshSkillsAfterDiskChange();\n }\n\n refreshSkillsAfterSkillConfigChange(): void {\n this.agentManager.refreshSkillsAfterSkillConfigChange();\n }\n\n getModelForSession(sessionKey: string): string {\n return this.modelManager.getModelForSession(sessionKey);\n }\n\n async switchModelForSession(sessionKey: string, modelId: string): Promise<boolean> {\n const ok = await this.modelManager.switchModelForSession(sessionKey, modelId);\n if (!ok) return false;\n await this.sessionConfigStore.update(sessionKey, { modelOverride: modelId });\n const result = this.agentManager.setModelForSession(sessionKey, modelId);\n if (result) {\n this.sessionTracker.touchSession(sessionKey);\n }\n return true;\n }\n\n private async clearSessionModelOverride(sessionKey: string): Promise<void> {\n this.modelManager.clearSessionModelOverride(sessionKey);\n await this.sessionConfigStore.update(sessionKey, { modelOverride: undefined });\n const agent = this.agentManager.getAgent(sessionKey);\n if (agent) {\n await this.modelManager.applyModelForSession(agent, sessionKey);\n }\n }\n\n /**\n * Clears per-session model override so the next turn uses the configured agent default\n * (e.g. cron isolated job with no explicit model).\n */\n async resetSessionModelToAgentDefault(sessionKey: string): Promise<void> {\n await this.clearSessionModelOverride(sessionKey);\n }\n\n private async hydrateSessionModelFromStore(sessionKey: string): Promise<void> {\n const cfg = await this.sessionConfigStore.get(sessionKey);\n if (cfg?.modelOverride) {\n await this.modelManager.switchModelForSession(sessionKey, cfg.modelOverride);\n }\n }\n\n setStreamHandle(handle: StreamHandle): void {\n this.streamManager.setHandle(handle);\n this.feedbackCoordinator.setStreamHandle(handle);\n }\n\n clearStreamHandle(): void {\n this.streamManager.clearHandle();\n this.feedbackCoordinator.endTask();\n }\n\n async start(): Promise<void> {\n this.running = true;\n await this.sessionConfigStore.initialize();\n await this.hookHandler.trigger('gateway_start', { port: 0, host: 'cli' });\n await this.reconcileDreamingCronJob().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, errorMessage: em }, `Dreaming cron reconcile failed: ${em}`);\n });\n log.debug('Agent service started');\n await this.hookHandler.trigger('session_start', { sessionId: this.agentId });\n\n while (this.running) {\n try {\n const msg = await this.bus.consumeInbound();\n await this.handleInboundMessage(msg);\n } catch (error) {\n if (error instanceof MessageBusShutdownError) {\n break;\n }\n const em = error instanceof Error ? error.message : String(error);\n log.error(\n { err: error, errorMessage: em, phase: 'inbound_consume' },\n `Agent loop failed (will retry in 1s): ${em}`,\n );\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n\n await this.hookHandler.trigger('session_end', {\n sessionId: this.agentId,\n messageCount: 0, // No longer tracking single agent messages\n });\n }\n\n stop(): Promise<void> {\n this.running = false;\n this.agentManager.dispose();\n this.dispose();\n\n this.hookHandler.trigger('gateway_stop', { reason: 'stopped' });\n log.debug('Agent service stopped');\n return Promise.resolve();\n }\n\n /**\n * Reconcile managed Dreaming cron job against the current effective config.\n * Safe to call after config saves to apply changes without restarting the process.\n */\n async reconcileDreamingNow(): Promise<void> {\n await this.reconcileDreamingCronJob();\n }\n\n private async reconcileDreamingCronJob(): Promise<void> {\n const cron = this.config.getCronService?.();\n if (!cron) {\n return;\n }\n const cfg = this.effectiveAppConfig();\n const dreaming = resolveDreamingConfig(cfg);\n const jobs = await cron.listJobs();\n const managed = jobs.filter(\n (job) => job.name === DREAMING_CRON_NAME || (job.name?.includes?.(DREAMING_CRON_TAG) ?? false),\n );\n\n const desiredPayload = { kind: 'agentTurn' as const, message: DREAMING_SWEEP_TOKEN };\n const desired = {\n name: DREAMING_CRON_NAME,\n timezone: dreaming.timezone,\n sessionTarget: 'isolated' as const,\n payload: desiredPayload,\n enabled: true,\n };\n\n if (!dreaming.enabled || !dreaming.deep.enabled) {\n // Remove managed jobs when disabled.\n for (const job of managed) {\n await cron.removeJob(job.id).catch(() => {});\n }\n return;\n }\n\n if (managed.length === 0) {\n await cron.addJob(dreaming.frequency, { ...desired } as any);\n return;\n }\n\n const primary = managed[0]!;\n // Prune duplicates (best-effort).\n for (const dup of managed.slice(1)) {\n await cron.removeJob(dup.id).catch(() => {});\n }\n\n // Patch if needed (schedule/message/timezone/sessionTarget).\n const payloadMessage =\n primary.payload?.kind === 'agentTurn'\n ? primary.payload.message\n : (primary.payload as any)?.text;\n const needsUpdate =\n primary.schedule !== dreaming.frequency ||\n (dreaming.timezone ?? null) !== (primary.timezone ?? null) ||\n primary.sessionTarget !== 'isolated' ||\n payloadMessage !== DREAMING_SWEEP_TOKEN ||\n primary.enabled !== true ||\n primary.name !== DREAMING_CRON_NAME;\n\n if (needsUpdate) {\n await cron.updateJob(primary.id, {\n schedule: dreaming.frequency,\n timezone: dreaming.timezone ?? undefined,\n sessionTarget: 'isolated',\n name: DREAMING_CRON_NAME,\n payload: desiredPayload,\n enabled: true,\n } as any);\n }\n }\n\n /**\n * Persist agent messages with the same sanitizer + transcript hygiene as AgentOrchestrator.\n * Uses persistence hygiene so `thinking` blocks remain on disk for the web UI (LLM load path still drops them).\n */\n private async persistAgentSessionMessages(sessionKey: string): Promise<void> {\n const raw = this.agentManager.getMessages(sessionKey);\n if (!raw) {\n return;\n }\n const { messages } = sanitizeMessages(raw);\n let toSave = messages;\n try {\n const model = this.modelManager.getResolvedModelForSession(sessionKey);\n toSave = tryApplySessionTranscriptHygieneForPersistence(messages, model);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Transcript hygiene on save skipped');\n }\n await this.sessionStore.save(sessionKey, toSave);\n this.enqueueMaybeAutoTitleAfterPersist(sessionKey);\n }\n\n /**\n * Fire-and-forget: `maybeAutoTitleSessionStore` no-ops for cron/heartbeat keys.\n * Runs after persist so the store has the latest transcript; does not block SSE / callers.\n */\n private enqueueMaybeAutoTitleAfterPersist(sessionKey: string): void {\n void (async () => {\n try {\n let modelRef =\n getAgentDefaultModelRef(this.config.config ?? ({} as Config)) ?? this.config.model;\n if (!modelRef?.trim()) {\n try {\n modelRef = this.modelManager.getModelForSession(sessionKey);\n } catch {\n modelRef = undefined;\n }\n }\n await maybeAutoTitleSessionStore(this.sessionStore, sessionKey, modelRef?.trim() || undefined);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Auto session title failed');\n }\n })();\n }\n\n private prepareLoadedSessionMessages(sessionKey: string, messages: AgentMessage[]): AgentMessage[] {\n let out = cleanTrailingErrors(messages);\n try {\n const model = this.modelManager.getResolvedModelForSession(sessionKey);\n out = tryApplySessionTranscriptHygiene(out, model);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Transcript hygiene on load skipped');\n }\n return out;\n }\n\n private parseSessionKey(sessionKey: string): { channel: string; chatId: string } {\n const parts = sessionKey.split(':').filter(Boolean);\n const first = parts[0] || 'cli';\n\n // Heartbeat sessions use keys like `heartbeat:main` / `heartbeat:isolated:ts` — not a real channel id.\n // Route tool outbounds to configured delivery target, or a synthetic channel that ChannelManager drops.\n if (first === 'heartbeat') {\n const hb = this.config.config?.gateway?.heartbeat;\n const target = hb?.target?.trim();\n const targetChatId = hb?.targetChatId?.trim();\n if (target && targetChatId) {\n return { channel: target, chatId: targetChatId };\n }\n return { channel: INTERNAL_OUTBOUND_DROP_CHANNEL, chatId: parts.slice(1).join(':') || 'heartbeat' };\n }\n\n const parsed = parseRoutingSessionKey(sessionKey);\n if (parsed) {\n return { channel: parsed.source, chatId: parsed.peerId };\n }\n\n if (first === 'cron') {\n return { channel: INTERNAL_OUTBOUND_DROP_CHANNEL, chatId: parts.slice(1).join(':') || 'cron' };\n }\n\n return {\n channel: first,\n chatId: parts.slice(1).join(':') || 'direct',\n };\n }\n\n private initSessionContext(\n sessionKey: string,\n channel: string,\n chatId: string,\n senderId = '',\n ): SessionContext {\n const context: SessionContext = {\n sessionKey,\n channel,\n chatId,\n senderId,\n isGroup: false,\n };\n\n this.contextMiddleware.onRequest({\n sessionKey,\n userId: context.senderId,\n channel,\n chatId,\n });\n\n this.sessionContextManager.setContext(context);\n this.feedbackCoordinator.setContext(context);\n this.setupSessionEventHandling(sessionKey);\n\n return context;\n }\n\n private async buildMessageContent(\n content: string,\n attachments?: Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>,\n sessionKey?: string,\n ): Promise<Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }>> {\n const messageContent: Array<\n { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }\n > = [];\n\n const sk = sessionKey ?? '';\n\n if (content.trim()) {\n let textPart = content;\n if (/@file:/.test(textPart)) {\n const wsKey = sk !== '' ? sk : 'cli:direct';\n const root = this.agentManager.getResolvedWorkspaceForSession(wsKey);\n textPart = await expandAtFileMentionsInPlainText(textPart, root);\n }\n messageContent.push({ type: 'text', text: textPart });\n }\n\n if (!attachments?.length) {\n return messageContent;\n }\n\n const modelRef =\n sk !== ''\n ? this.modelManager.getModelForSession(sk)\n : getAgentDefaultModelRef(this.config.config!) ?? getDefaultModelSync(this.config.config);\n const cfg = this.config.config;\n\n const storageRoot =\n sk !== ''\n ? resolveAgentHomeDir(this.config.config!, extractProfileAgentId(sk, this.config.config!))\n : resolveAgentHomeDir(this.config.config!, resolveDefaultAgentId(this.config.config!));\n\n let i = 0;\n while (i < attachments.length) {\n const att = attachments[i]!;\n const isImage =\n att.type === 'image' ||\n att.type === 'photo' ||\n Boolean(att.mimeType?.startsWith('image/'));\n\n if (isImage) {\n const group: Array<{ data: string; mimeType: string }> = [];\n while (i < attachments.length) {\n const a = attachments[i]!;\n const img =\n a.type === 'image' || a.type === 'photo' || Boolean(a.mimeType?.startsWith('image/'));\n if (!img) {\n break;\n }\n if (!a.data || a.data.length === 0) {\n i += 1;\n continue;\n }\n group.push({ data: a.data, mimeType: a.mimeType || 'image/png' });\n i += 1;\n }\n if (group.length > 0) {\n const parts = await resolveInboundImageContentParts({\n modelRef: modelRef || getDefaultModelSync(cfg),\n cfg,\n userTextForContext: content.trim() ? content : '',\n images: group,\n });\n messageContent.push(...parts);\n }\n } else {\n const fileBlock = formatInboundFileTextBlock(att, storageRoot);\n messageContent.push({ type: 'text', text: fileBlock });\n i += 1;\n }\n }\n\n return messageContent;\n }\n\n /**\n * Persist inbound file attachments under agent home `inbound/` (non-images with data).\n * Idempotent if `workspaceRelativePath` is already set on an attachment.\n */\n async prepareInboundAttachments(\n sessionKey: string,\n attachments?: Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>,\n ): Promise<\n | Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>\n | undefined\n > {\n const cfg = this.config.config!;\n const storageRoot = resolveAgentHomeDir(cfg, extractProfileAgentId(sessionKey, cfg));\n return persistInboundAttachmentsToWorkspace(storageRoot, sessionKey, attachments);\n }\n\n private endDirectRequestContext(): void {\n this.sessionContextManager.clearContext();\n this.feedbackCoordinator.clearContext();\n this.contextMiddleware.onResponse();\n }\n\n async compactSession(\n sessionKey: string,\n options?: { instructions?: string; force?: boolean },\n ): Promise<CompactionResult> {\n const messages = await this.sessionStore.load(sessionKey);\n const contextWindow = this.getContextWindow();\n const result = await this.sessionStore.compact(\n sessionKey,\n messages,\n contextWindow,\n options?.instructions,\n options?.force ?? true,\n );\n if (result.compacted) {\n await this.sessionStore.save(sessionKey, await this.sessionStore.load(sessionKey));\n this.agentManager.removeAgent(sessionKey);\n }\n log.info({ sessionKey, result }, 'Manual compaction complete');\n return result;\n }\n\n /**\n * One-shot LLM answer for /btw: uses transcript as background only; does not persist to session.\n */\n async btwQuery(sessionKey: string, question: string): Promise<{ text: string; error?: string }> {\n const q = question.trim();\n if (!q) {\n return { text: '', error: 'Empty question.' };\n }\n const messages = await this.sessionStore.load(sessionKey);\n const modelRef = this.modelManager.getModelForSession(sessionKey);\n let model;\n try {\n model = resolveModel(modelRef);\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, modelRef, errorMessage: em }, 'btwQuery: model resolve failed');\n return { text: '', error: `Could not resolve model: ${modelRef}` };\n }\n\n const background = this.formatMessagesForBtw(messages.slice(-40));\n const systemBlock = [\n 'You are answering an ephemeral /btw side question about the current conversation.',\n 'Use the conversation only as background context.',\n 'Answer only the side question. Do not continue or complete any unfinished task from the conversation.',\n 'Do not use tools, shell, or file writes unless the question explicitly requires a tiny code snippet.',\n 'If the question can be answered briefly, answer briefly.',\n ].join('\\n');\n\n const userPrompt = [\n systemBlock,\n '',\n '---',\n 'Conversation background (read-only):',\n background || '(empty)',\n '',\n '---',\n 'Side question:',\n q,\n ].join('\\n');\n\n const userMessage: UserMessage = { role: 'user', content: userPrompt, timestamp: Date.now() };\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 90_000);\n try {\n const out = await complete(model, { messages: [userMessage] }, {\n maxTokens: 2048,\n temperature: 0.4,\n signal: controller.signal as AbortSignal,\n });\n const text = Array.isArray(out.content)\n ? out.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text' && typeof (c as { text?: unknown }).text === 'string')\n .map((c) => c.text || '')\n .join('')\n : '';\n return { text: text.trim() };\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey, errorMessage: em }, 'btwQuery failed');\n return { text: '', error: em };\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private formatMessagesForBtw(messages: AgentMessage[]): string {\n return messages\n .map((m) => {\n const role = m.role;\n let body = '';\n if (typeof m.content === 'string') {\n body = m.content;\n } else if (Array.isArray(m.content)) {\n body = m.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text || '')\n .join('\\n');\n }\n const line = `[${role}]: ${body}`;\n return line.length > 4000 ? `${line.slice(0, 4000)}…` : line;\n })\n .join('\\n\\n');\n }\n\n /** Markdown or JSON summary for /context (prompt assembly is approximated from config + transcript stats). */\n async getSessionContextReport(\n sessionKey: string,\n mode: 'list' | 'detail' | 'json',\n ): Promise<string> {\n const messages = await this.sessionStore.load(sessionKey);\n const cw = this.getContextWindow();\n const stats = this.getSessionStats(sessionKey, messages);\n const cfg = this.effectiveAppConfig() ?? this.config.config!;\n const model = this.modelManager.getModelForSession(sessionKey);\n const sc = await this.sessionConfigStore.get(sessionKey);\n const workspace = effectiveWorkspacePathForSession(cfg, sessionKey, sc);\n const estTokens = await this.sessionStore.estimateTokenUsage(sessionKey, messages);\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const defaults = cfg.agents?.defaults;\n const compaction = defaults?.compaction;\n const tools = defaults?.tools;\n\n const toolsSummary =\n tools && typeof tools === 'object'\n ? Object.entries(tools as Record<string, unknown>)\n .filter(([, v]) => v === true)\n .map(([k]) => k)\n .slice(0, 16)\n .join(', ') || '(none explicitly true)'\n : '(see agents.defaults.tools in config)';\n\n const payload: Record<string, unknown> = {\n sessionKey,\n model,\n workspacePath: workspace,\n agentId: profile.agentId,\n messageCount: messages.length,\n contextWindowNominal: cw,\n estimatedTranscriptTokens: estTokens,\n approxWindowUsage: cw > 0 ? estTokens / cw : null,\n thinkingDefault: defaults?.thinkingDefault,\n reasoningDefault: defaults?.reasoningDefault,\n verboseDefault: defaults?.verboseDefault,\n compaction,\n toolsFlagsOn: toolsSummary,\n windowStats: stats.windowStats,\n compactionRunStats: stats.compactionStats,\n };\n\n if (mode === 'json') {\n return JSON.stringify(payload, null, 2);\n }\n\n const lines: string[] = [\n '📎 *Context overview*',\n '',\n `• Session: \\`${sessionKey}\\``,\n `• Model: \\`${model}\\``,\n `• Agent profile: \\`${profile.agentId}\\``,\n `• Workspace: \\`${workspace}\\``,\n `• Messages: ${messages.length}`,\n `• Est. transcript tokens (rough): ${estTokens}`,\n `• Nominal context budget (4× maxTokens): ${cw}`,\n ];\n if (cw > 0) {\n lines.push(`• Approx. usage vs budget: ${((estTokens / cw) * 100).toFixed(1)}%`);\n }\n lines.push(\n `• Thinking / reasoning / verbose defaults: ${defaults?.thinkingDefault ?? '—'} / ${defaults?.reasoningDefault ?? '—'} / ${defaults?.verboseDefault ?? '—'}`,\n `• Tools (true flags): ${toolsSummary}`,\n '',\n '_Full system prompt, skills, and memory blocks are assembled at agent runtime; use Web settings or logs for deep inspection._',\n );\n\n if (mode === 'detail') {\n lines.push('', '*Compaction config (agents.defaults.compaction):*', '```json');\n lines.push(JSON.stringify(compaction ?? {}, null, 2));\n lines.push('```', '', '*Window stats:*', '```json');\n lines.push(JSON.stringify(stats.windowStats ?? {}, null, 2));\n lines.push('```');\n }\n\n return lines.join('\\n');\n }\n\n getSessionStats(sessionKey: string, messages: AgentMessage[]) {\n return {\n windowStats: this.sessionStore.getWindowStats(messages),\n compactionStats: this.sessionStore.getCompactionStats(sessionKey),\n tokenEstimate: this.sessionStore.estimateTokenUsage(sessionKey, messages),\n };\n }\n\n private async applyResolvedThinkingLevel(sessionKey: string, requestOverride?: string | null): Promise<void> {\n const def = this.effectiveAppConfig()?.agents?.defaults?.thinkingDefault;\n const level = await resolveEffectiveThinkingLevel(\n this.sessionConfigStore,\n sessionKey,\n requestOverride,\n def,\n );\n this.agentManager.setThinkingLevel(sessionKey, level);\n }\n\n /** Resolved thinking level and effective model ref for a session (Web UI). */\n async getSessionAgentConfig(sessionKey: string): Promise<{\n thinkingLevel: ThinkingLevel;\n model: string;\n reasoningLevel: ReasoningLevel;\n effectiveWorkspacePath: string;\n workingDirectoryLocked: boolean;\n }> {\n await this.hydrateSessionModelFromStore(sessionKey);\n const cfg = this.effectiveAppConfig()!;\n const sc = await this.sessionConfigStore.get(sessionKey);\n\n // Ensure model display matches the effective agent profile even before an Agent instance exists.\n // Otherwise, `ModelManager.getModelForSession()` falls back to the global default until the first turn creates the agent.\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const profileModelRef = profile.primaryModelRef?.trim();\n if (profileModelRef) {\n this.modelManager.setSessionProfileDefault(sessionKey, profileModelRef);\n }\n\n const defThink = cfg.agents?.defaults?.thinkingDefault ?? 'medium';\n const level = await resolveEffectiveThinkingLevel(this.sessionConfigStore, sessionKey, null, defThink);\n const defReason = (cfg.agents?.defaults?.reasoningDefault ?? 'off') as ReasoningLevel;\n const reasoningLevel = await resolveEffectiveReasoningLevel(this.sessionConfigStore, sessionKey, defReason);\n const model = this.modelManager.getModelForSession(sessionKey);\n return {\n thinkingLevel: level,\n model,\n reasoningLevel,\n effectiveWorkspacePath: effectiveWorkspacePathForSession(cfg, sessionKey, sc),\n workingDirectoryLocked: Boolean(sc?.workingDirectoryOverride?.trim()),\n };\n }\n\n /**\n * Load session working directory override into AgentManager, ensure directory exists.\n * Call before AgentManager.getOrCreateAgent for this session.\n */\n async hydrateSessionWorkspaceFromStore(sessionKey: string): Promise<void> {\n const cfg = this.config.config;\n if (!cfg) {\n return;\n }\n const loaded = await this.sessionConfigStore.get(sessionKey);\n if (loaded?.workingDirectoryOverride?.trim()) {\n const wdStored = normalizeWorkingDirectoryInput(loaded.workingDirectoryOverride);\n if (wdStored.ok) {\n this.agentManager.setSessionWorkspaceOverride(sessionKey, wdStored.path);\n } else {\n log.warn({ sessionKey }, 'Invalid stored workingDirectoryOverride; ignoring');\n this.agentManager.setSessionWorkspaceOverride(sessionKey, null);\n }\n } else {\n this.agentManager.setSessionWorkspaceOverride(sessionKey, null);\n }\n const effective = effectiveWorkspacePathForSession(cfg, sessionKey, loaded);\n await mkdir(effective, { recursive: true });\n }\n\n /**\n * Sync persisted session workspace override for an isolated cron run (runs may change when the job is edited).\n * Omit or pass empty `workingDirectory` to use the effective agent default workspace for this session key.\n */\n async applyCronJobWorkingDirectory(sessionKey: string, workingDirectory: string | undefined): Promise<void> {\n const raw = workingDirectory?.trim();\n if (raw) {\n const wdNorm = normalizeWorkingDirectoryInput(raw);\n if (wdNorm.ok === false) {\n log.warn({ sessionKey, error: wdNorm.error }, 'Cron job working directory invalid; using agent default');\n await this.clearCronSessionWorkingDirectoryOverride(sessionKey);\n return;\n }\n await mkdir(wdNorm.path, { recursive: true });\n await this.sessionConfigStore.update(sessionKey, { workingDirectoryOverride: wdNorm.path });\n this.agentManager.setSessionWorkspaceOverride(sessionKey, wdNorm.path);\n return;\n }\n await this.clearCronSessionWorkingDirectoryOverride(sessionKey);\n }\n\n private async clearCronSessionWorkingDirectoryOverride(sessionKey: string): Promise<void> {\n const existing = await this.sessionConfigStore.get(sessionKey);\n if (existing?.workingDirectoryOverride) {\n const { workingDirectoryOverride: _removed, ...rest } = existing;\n await this.sessionConfigStore.set(sessionKey, rest);\n }\n this.agentManager.setSessionWorkspaceOverride(sessionKey, null);\n }\n\n /** Workspace root for UI file tree / editor (same as agent tools after hydration). */\n async getEffectiveWorkspacePathForSession(sessionKey: string): Promise<string> {\n await this.hydrateSessionWorkspaceFromStore(sessionKey);\n const cfg = this.config.config!;\n const sc = await this.sessionConfigStore.get(sessionKey);\n return effectiveWorkspacePathForSession(cfg, sessionKey, sc);\n }\n\n async patchSessionAgentConfig(\n sessionKey: string,\n partial: {\n thinkingLevel?: string;\n model?: string | null;\n reasoningLevel?: string;\n workingDirectory?: string;\n },\n ): Promise<{ ok: boolean; error?: string }> {\n if (partial.model !== undefined) {\n if (partial.model === null || partial.model === '') {\n await this.clearSessionModelOverride(sessionKey);\n } else {\n const ok = await this.modelManager.switchModelForSession(sessionKey, partial.model);\n if (!ok) {\n return { ok: false, error: 'Invalid model' };\n }\n await this.sessionConfigStore.update(sessionKey, { modelOverride: partial.model });\n this.agentManager.setModelForSession(sessionKey, partial.model);\n }\n }\n\n if (partial.thinkingLevel !== undefined) {\n const normalized = normalizeThinkLevel(partial.thinkingLevel);\n if (!normalized) {\n return { ok: false, error: 'Invalid thinking level' };\n }\n await this.sessionConfigStore.update(sessionKey, { thinkingLevel: normalized });\n this.agentManager.setThinkingLevel(sessionKey, normalized as ThinkingLevel);\n }\n\n if (partial.reasoningLevel !== undefined) {\n const normalized = normalizeReasoningLevel(partial.reasoningLevel);\n if (!normalized) {\n return { ok: false, error: 'Invalid reasoning level' };\n }\n await this.sessionConfigStore.update(sessionKey, { reasoningLevel: normalized });\n }\n\n if (partial.workingDirectory !== undefined) {\n const cfg = this.config.config;\n if (!cfg) {\n return { ok: false, error: 'Config not loaded' };\n }\n const existing = await this.sessionConfigStore.get(sessionKey);\n const existingRaw = existing?.workingDirectoryOverride?.trim();\n const incoming = partial.workingDirectory.trim();\n\n const priorMessages = await this.sessionStore.load(sessionKey);\n\n if (priorMessages.length > 0) {\n if (!incoming) {\n return { ok: false, error: 'workingDirectory is empty' };\n }\n if (!existingRaw) {\n return {\n ok: false,\n error: 'Working directory can only be set before the first message in this conversation',\n };\n }\n const prev = normalizeWorkingDirectoryInput(existingRaw);\n const next = normalizeWorkingDirectoryInput(incoming);\n if (prev.ok && next.ok && prev.path === next.path) {\n /* idempotent */\n } else {\n return { ok: false, error: 'Working directory is already set for this session' };\n }\n } else {\n if (!incoming) {\n return { ok: false, error: 'workingDirectory is empty' };\n }\n const wdNorm = normalizeWorkingDirectoryInput(incoming);\n switch (wdNorm.ok) {\n case true:\n if (existingRaw) {\n const prev = normalizeWorkingDirectoryInput(existingRaw);\n if (prev.ok && prev.path === wdNorm.path) {\n break;\n }\n }\n await mkdir(wdNorm.path, { recursive: true });\n await this.sessionConfigStore.update(sessionKey, { workingDirectoryOverride: wdNorm.path });\n this.agentManager.setSessionWorkspaceOverride(sessionKey, wdNorm.path);\n this.agentManager.removeAgent(sessionKey);\n break;\n case false:\n return { ok: false, error: wdNorm.error };\n default:\n return { ok: false, error: 'Invalid working directory' };\n }\n }\n }\n\n return { ok: true };\n }\n\n async *processDirectStreaming(\n content: string,\n sessionKey = 'cli:direct',\n attachments?: Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>,\n thinking?: string,\n options?: { signal?: AbortSignal },\n ): AsyncGenerator<{ type: string; [key: string]: unknown }, void, unknown> {\n yield* runProcessDirectStreaming(this.createProcessDirectStreamingDeps(), {\n content,\n sessionKey,\n attachments,\n thinking,\n signal: options?.signal,\n });\n }\n\n /**\n * Best-effort timezone resolution for webchat envelope timestamps.\n * Reads `USER.md` in the resolved workspace and extracts a `Timezone:` line.\n */\n resolveUserTimezoneForSession(sessionKey: string): string | undefined {\n try {\n const workspace = this.agentManager.getResolvedWorkspaceForSession(sessionKey);\n const userPath = join(workspace, 'USER.md');\n if (!existsSync(userPath)) return undefined;\n const raw = readFileSync(userPath, 'utf-8');\n const match = raw.match(/Timezone:\\s*(.+)/i);\n const tz = match?.[1]?.trim();\n return tz || undefined;\n } catch {\n return undefined;\n }\n }\n\n private createProcessDirectStreamingDeps(): ProcessDirectStreamingDeps {\n return {\n log,\n parseSessionKey: (sk) => this.parseSessionKey(sk),\n initDirectStreamingSession: (sk, channel, chatId) => this.initSessionContext(sk, channel, chatId),\n registerWebchatSsePublisher: (sk, publisher) => {\n this.webchatSseEnqueueBySession.set(sk, publisher);\n },\n unregisterWebchatSsePublisher: (sk) => {\n this.webchatSseEnqueueBySession.delete(sk);\n },\n agentManager: this.agentManager,\n hydrateSessionWorkspaceFromStore: (sk) => this.hydrateSessionWorkspaceFromStore(sk),\n hydrateSessionModelFromStore: (sk) => this.hydrateSessionModelFromStore(sk),\n agentEventHandler: this.agentEventHandler,\n sessionStore: this.sessionStore,\n prepareLoadedSessionMessages: (sk, msgs) => this.prepareLoadedSessionMessages(sk, msgs),\n modelManager: this.modelManager,\n applyResolvedThinkingLevel: (sk, t) => this.applyResolvedThinkingLevel(sk, t),\n getConfig: () => this.effectiveAppConfig(),\n sessionConfigStore: this.sessionConfigStore,\n attachmentRootsForSession: (sk) => this.attachmentRootsForSession(sk),\n agentOrchestrator: this.agentOrchestrator,\n commandHandler: this.commandHandler,\n prepareInboundAttachments: (sk, att) => this.prepareInboundAttachments(sk, att),\n buildMessageContent: (text, prepared, sk) => this.buildMessageContent(text, prepared, sk),\n persistAgentSessionMessages: (sk) => this.persistAgentSessionMessages(sk),\n maybeEmitWebchatTts: (sk, hadVoice) => this.maybeEmitWebchatTts(sk, hadVoice),\n endDirectRequestContext: () => this.endDirectRequestContext(),\n };\n }\n\n /**\n * Inject an SSE event into an in-flight webchat stream (same queue as tokens/tools).\n */\n enqueueWebchatSseEvent(sessionKey: string, event: { type: string; [key: string]: unknown }): void {\n const pub = this.webchatSseEnqueueBySession.get(sessionKey);\n if (pub) {\n pub(event);\n }\n }\n\n /**\n * Queue a steering user message into pi-agent's in-flight run (delivered after current tool work, before the next LLM call).\n * See `Agent.steer` in `@mariozechner/pi-agent-core`.\n */\n async steerWebchatSession(sessionKey: string, text: string): Promise<boolean> {\n const trimmed = text.trim();\n if (!trimmed) return false;\n try {\n await this.hydrateSessionWorkspaceFromStore(sessionKey);\n const agent = this.agentManager.getOrCreateAgent(sessionKey);\n const msg: AgentMessage = {\n role: 'user',\n content: [{ type: 'text', text: trimmed }],\n timestamp: Date.now(),\n };\n agent.steer(msg);\n return true;\n } catch (err) {\n log.warn({ err, sessionKey }, 'steerWebchatSession failed');\n return false;\n }\n }\n\n /**\n * Generate TTS for webchat when config allows, persist under agent home `tts/`, attach to last assistant turn.\n */\n private async maybeEmitWebchatTts(\n sessionKey: string,\n hadInboundVoice: boolean,\n ): Promise<{ type: 'tts_audio'; workspaceRelativePath: string; mimeType: string; name: string } | null> {\n const ttsConfig = mergeTtsConfigFromAppConfig(this.config.config?.tts);\n if (!isTTSAvailable(ttsConfig)) {\n return null;\n }\n const decision = shouldUseTTS(ttsConfig, hadInboundVoice);\n if (!decision.useTTS) {\n return null;\n }\n const text = this.agentManager.getLastAssistantContent(sessionKey)?.trim();\n if (!text) {\n return null;\n }\n try {\n const webOut = getChannelOutputFormat('webchat');\n const fmt = webOut.format as 'opus' | 'mp3' | 'wav';\n const ttsResult = await speak(text, ttsConfig, {\n appConfig: this.config.config,\n tts: { format: fmt },\n });\n const { buffer, format } = await compressAudio(\n Buffer.from(ttsResult.audio),\n ttsResult.format,\n webOut.format === 'mp3' ? 'mp3' : 'opus',\n );\n const normalizedMime =\n format === 'opus' || format === 'ogg'\n ? 'audio/ogg'\n : format === 'mp3' || format === 'mpeg'\n ? 'audio/mpeg'\n : format === 'wav'\n ? 'audio/wav'\n : `audio/${format}`;\n const persisted = await persistOutboundTtsAudio(\n resolveAgentHomeDir(this.config.config!, extractProfileAgentId(sessionKey, this.config.config!)),\n sessionKey,\n buffer,\n format,\n );\n await this.appendAttachmentToLastAssistant(sessionKey, {\n type: 'audio',\n mimeType: normalizedMime,\n name: persisted.name,\n size: persisted.size,\n workspaceRelativePath: persisted.workspaceRelativePath,\n });\n return {\n type: 'tts_audio',\n workspaceRelativePath: persisted.workspaceRelativePath,\n mimeType: normalizedMime,\n name: persisted.name,\n };\n } catch (err) {\n log.warn({ err, sessionKey }, 'Webchat TTS failed');\n return null;\n }\n }\n\n private async appendAttachmentToLastAssistant(\n sessionKey: string,\n att: {\n type: string;\n mimeType: string;\n name: string;\n size: number;\n workspaceRelativePath: string;\n },\n ): Promise<void> {\n const loaded = await this.sessionStore.load(sessionKey);\n for (let i = loaded.length - 1; i >= 0; i--) {\n const m = loaded[i] as { role?: string; attachments?: unknown[] };\n if (m.role === 'assistant') {\n const prev = (m.attachments ?? []) as Array<{ workspaceRelativePath?: string }>;\n if (prev.some((x) => x.workspaceRelativePath === att.workspaceRelativePath)) {\n return;\n }\n const next = [...prev, att];\n loaded[i] = { ...m, attachments: next } as unknown as AgentMessage;\n await this.sessionStore.save(sessionKey, loaded);\n return;\n }\n }\n }\n\n async processDirect(\n content: string,\n sessionKey = 'cli:direct',\n attachments?: Array<{\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n workspaceRelativePath?: string;\n }>,\n thinking?: string,\n ): Promise<string> {\n const { channel, chatId } = this.parseSessionKey(sessionKey);\n this.initSessionContext(sessionKey, channel, chatId);\n\n try {\n await this.hydrateSessionWorkspaceFromStore(sessionKey);\n // Get or create agent for this session\n const agent = this.agentManager.getOrCreateAgent(sessionKey);\n\n await this.hydrateSessionModelFromStore(sessionKey);\n\n const loaded = await this.sessionStore.load(sessionKey);\n agent.state.messages = this.prepareLoadedSessionMessages(sessionKey, loaded);\n\n await this.modelManager.applyModelForSession(agent, sessionKey);\n await this.applyResolvedThinkingLevel(sessionKey, thinking);\n\n const prepared = await this.prepareInboundAttachments(sessionKey, attachments);\n\n const cmd = parseSlashCommand(content);\n if (cmd && commandRegistry.has(cmd.command)) {\n const { aggregatedText } = await this.commandHandler.executeCommandAndAggregateReply(cmd.command, cmd.args, {\n sessionKey,\n channel,\n chatId,\n senderId: '',\n isGroup: false,\n });\n await this.persistAgentSessionMessages(sessionKey);\n return aggregatedText;\n }\n\n const textForDirect = content.trimStart().startsWith('/skill:')\n ? this.agentManager.expandSkillUserText(content)\n : content;\n const messageContent = await this.buildMessageContent(textForDirect, prepared, sessionKey);\n\n const userMessage = {\n role: 'user' as const,\n content: messageContent,\n timestamp: Date.now(),\n };\n const userPlain = extractAgentUserPlainText(userMessage);\n const userMessageForModel = await this.agentManager.applyMemoryPrefetchToUserMessage(\n userMessage,\n sessionKey,\n );\n\n await runAgentTurnWithModelFallbacks({\n agent,\n sessionKey,\n modelManager: this.modelManager,\n userMessage: userMessageForModel,\n log,\n getConfig: () => this.config.config,\n beforeUserPrompt: () => this.agentManager.beginBackgroundReviewUserTurn(sessionKey),\n });\n\n this.agentManager.afterAgentTurn(sessionKey, userPlain);\n this.agentManager.scheduleBackgroundReviewAfterUserTurn(sessionKey);\n\n const response = this.agentManager.getLastAssistantContent(sessionKey) || '';\n await this.persistAgentSessionMessages(sessionKey);\n\n return response;\n } finally {\n this.endDirectRequestContext();\n // Don't unsubscribe here - keep the session agent alive for future messages\n }\n }\n\n private async handleInboundMessage(msg: InboundMessage): Promise<void> {\n const requestId = inboundMessageLogRequestId(msg);\n\n await runWithLogContext({ requestId }, async () => {\n const routing = await this.messageRouter.routeMessage(msg);\n const { context, isCommand, command, commandArgs } = routing;\n\n const sessionContext: SessionContext = {\n sessionKey: context.sessionKey,\n channel: context.channel,\n chatId: context.chatId,\n senderId: context.senderId || '',\n isGroup: context.isGroup || false,\n metadata: {\n transcribedVoice: msg.metadata?.transcribedVoice === true,\n },\n };\n\n updateAsyncLogContext({ sessionId: sessionContext.sessionKey });\n\n this.sessionContextManager.setContext(sessionContext);\n this.feedbackCoordinator.setContext(sessionContext);\n\n // Setup event handling for this session\n this.setupSessionEventHandling(sessionContext.sessionKey);\n\n await this.sessionLifecycleManager.startSession(sessionContext);\n\n /** Declared on the function so `finally` can clear typing after outbound (TTS + send). */\n let typingController: TypingController | null = null;\n\n try {\n if (msg.channel === 'system') {\n await this.handleSystemMessage(msg, sessionContext);\n return;\n }\n\n if (isCommand && command) {\n const handled = await this.commandHandler.executeCommand(command, commandArgs || '', {\n sessionKey: sessionContext.sessionKey,\n channel: sessionContext.channel,\n chatId: sessionContext.chatId,\n senderId: sessionContext.senderId,\n isGroup: sessionContext.isGroup,\n });\n\n if (handled) {\n return;\n }\n }\n\n // Start continuous typing indicator (renews every 5 seconds)\n if (msg.channel !== 'cli') {\n typingController = createTypingController({\n intervalSeconds: 5,\n onStart: async () => {\n await this.bus.publishOutbound({\n channel: msg.channel,\n chat_id: msg.chat_id,\n content: '',\n type: 'typing_on',\n metadata: {\n accountId: msg.metadata?.accountId,\n threadId: msg.metadata?.threadId,\n },\n });\n },\n onStop: async () => {\n await this.bus.publishOutbound({\n channel: msg.channel,\n chat_id: msg.chat_id,\n content: '',\n type: 'typing_off',\n metadata: {\n accountId: msg.metadata?.accountId,\n threadId: msg.metadata?.threadId,\n },\n });\n },\n });\n typingController.start();\n }\n\n if (this.channelManagerRef && msg.channel !== 'cli') {\n const meta = msg.metadata as Record<string, unknown> | undefined;\n const streamHandle = this.channelManagerRef.startStream(\n msg.channel,\n msg.chat_id,\n meta?.accountId as string | undefined,\n {\n threadId: meta?.threadId as string | undefined,\n replyToMessageId: meta?.messageId as string | undefined,\n },\n );\n\n if (streamHandle) {\n this.setStreamHandle(streamHandle as StreamHandle);\n }\n }\n\n await this.agentOrchestrator.process(msg, sessionContext);\n } finally {\n await this.sessionLifecycleManager.endSession(sessionContext);\n await this.streamManager.end();\n try {\n await this.sendFinalResponse(msg, sessionContext);\n } finally {\n // After outbound (incl. TTS); previously we cleared typing right after LLM finished, so Weixin showed typing_off before the message.\n await typingController?.stop();\n }\n this.feedbackCoordinator.endTask();\n this.sessionContextManager.clearContext();\n this.feedbackCoordinator.clearContext();\n }\n });\n }\n\n private async handleSystemMessage(msg: InboundMessage, context: SessionContext): Promise<void> {\n log.debug({ sessionKey: context.sessionKey }, 'Processing system message');\n\n await this.hydrateSessionWorkspaceFromStore(context.sessionKey);\n\n // Get or create agent for this session\n const agent = this.agentManager.getOrCreateAgent(context.sessionKey);\n\n const messages = await this.sessionStore.load(context.sessionKey);\n await this.checkAndCompact(context.sessionKey, messages);\n const refreshedMessages = await this.sessionStore.load(context.sessionKey);\n agent.state.messages = this.prepareLoadedSessionMessages(context.sessionKey, refreshedMessages);\n\n const systemMessage: AgentMessage = {\n role: 'user',\n content: [{ type: 'text', text: `[System: ${msg.sender_id}] ${msg.content}` }],\n timestamp: Date.now(),\n };\n\n try {\n await agent.prompt(systemMessage);\n await agent.waitForIdle();\n\n const finalContent = this.agentManager.getLastAssistantContent(context.sessionKey);\n if (finalContent) {\n const hookResult = await this.hookHandler.runMessageSending(\n context.chatId,\n finalContent,\n context.channel,\n );\n if (hookResult.send) {\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: hookResult.content || finalContent,\n type: 'message',\n });\n }\n }\n\n await this.persistAgentSessionMessages(context.sessionKey);\n } catch (error) {\n const em = error instanceof Error ? error.message : String(error);\n log.error(\n {\n err: error,\n errorMessage: em,\n sessionKey: context.sessionKey,\n channel: context.channel,\n chatId: context.chatId,\n senderId: msg.sender_id,\n },\n `System message handling failed: ${em}`,\n );\n await this.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: '❌ An error occurred while processing the system message.',\n type: 'message',\n });\n }\n }\n\n /**\n * Setup event handling for a specific session\n */\n private setupSessionEventHandling(sessionKey: string): void {\n // If already subscribed, skip\n if (this.sessionUnsubscribers.has(sessionKey)) {\n return;\n }\n\n const unsubscribe = this.agentManager.subscribeToSession(sessionKey, (event) => {\n this.handleSessionEvent(sessionKey, event);\n });\n\n if (unsubscribe) {\n this.sessionUnsubscribers.set(sessionKey, unsubscribe);\n }\n }\n\n /**\n * Handle events from a specific session's agent\n */\n private handleSessionEvent(sessionKey: string, event: AgentEvent): void {\n const currentContext = this.sessionContextManager.getContext();\n if (!currentContext) {\n // Inbound `finally` clears context before trailing agent `message_update` events finish — ignore (not a bug).\n return;\n }\n\n if (currentContext.sessionKey !== sessionKey) {\n // Event from a different session — still process with current context where applicable\n this.agentEventHandler.handle(event, currentContext);\n return;\n }\n\n // Handle streaming updates for the current session\n if (event.type === 'message_update') {\n const msgEvent = event as Extract<AgentEvent, { type: 'message_update' }>;\n if (msgEvent.message?.role === 'assistant') {\n const content = msgEvent.message.content;\n const text = Array.isArray(content)\n ? extractTextContent(content as Array<{ type: string; text?: string }>)\n : String(content);\n\n this.streamManager.update(text);\n }\n }\n\n this.agentEventHandler.handle(event, currentContext);\n }\n\n private async checkAndCompact(sessionKey: string, messages: AgentMessage[]): Promise<void> {\n const contextWindow = this.getContextWindow();\n const prep = this.sessionStore.prepareCompaction(sessionKey, messages, contextWindow);\n if (!prep.needsCompaction) return;\n\n log.info({ sessionKey, reason: prep.stats?.reason, usagePercent: prep.stats?.usagePercent }, 'Session needs compaction');\n\n const result = await this.sessionStore.compact(sessionKey, messages, contextWindow, undefined, false);\n await this.hookHandler.trigger('after_compaction', {\n messageCount: messages.length,\n tokenCount: result.tokensBefore,\n compactedCount: messages.length - result.firstKeptIndex,\n });\n log.info({ sessionKey, tokensBefore: result.tokensBefore, tokensAfter: result.tokensAfter }, 'Session compacted');\n }\n\n private getContextWindow(): number {\n const defaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n return defaults?.maxTokens ? defaults.maxTokens * 4 : 128000;\n }\n\n private async sendFinalResponse(\n msg: InboundMessage,\n sessionContext: SessionContext\n ): Promise<void> {\n if (this.streamManager.consumeSkipFinalOutbound()) {\n return;\n }\n\n const finalContent = this.agentManager.getLastAssistantContent(sessionContext.sessionKey);\n if (!finalContent?.trim()) return;\n\n const ackMax =\n this.config.config?.gateway?.heartbeat?.ackMaxChars ?? DEFAULT_ACK_MAX_CHARS;\n if (shouldSilence(finalContent, ackMax) || finalContent.trim() === NO_REPLY) {\n log.debug(\n { sessionKey: sessionContext.sessionKey },\n 'Silent reply — skipping outbound',\n );\n return;\n }\n\n const hookResult = await this.hookHandler.runMessageSending(\n sessionContext.chatId,\n finalContent,\n sessionContext.channel,\n );\n if (!hookResult.send) return;\n\n // TTS is handled by ChannelManager, just send text message here\n await this.bus.publishOutbound({\n channel: sessionContext.channel,\n chat_id: sessionContext.chatId,\n content: hookResult.content || finalContent,\n type: 'message',\n metadata: {\n accountId: msg.metadata?.accountId,\n threadId: msg.metadata?.threadId,\n transcribedVoice: sessionContext.metadata?.transcribedVoice,\n },\n });\n }\n\n /** Extension hooks for ChannelManager outbound pipeline (Gateway). */\n async invokeOutboundMessageSending(\n to: string,\n content: string,\n channel: string,\n ): Promise<{ send: boolean; content?: string; reason?: string }> {\n return this.hookHandler.runMessageSending(to, content, channel);\n }\n\n async invokeOutboundMessageSent(\n to: string,\n content: string,\n success: boolean,\n error: string | undefined,\n channel: string,\n ): Promise<void> {\n return this.hookHandler.runMessageSent(to, content, success, error, channel);\n }\n\n private dispose(): void {\n this.sessionTracker.dispose();\n\n // Unsubscribe from all session agents\n for (const unsubscribe of this.sessionUnsubscribers.values()) {\n unsubscribe();\n }\n this.sessionUnsubscribers.clear();\n\n // Dispose all agent instances\n this.agentManager.dispose();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { AgentTool } from '@mariozechner/pi-agent-core';
2
+ import type { Config } from '../../config/schema.js';
3
+ export interface DreamingToolDeps {
4
+ getWorkspace: () => string;
5
+ getConfig: () => Config | undefined;
6
+ }
7
+ export declare function createDreamingTool(deps: DreamingToolDeps): AgentTool;