@xopcai/xopc 0.0.14 → 0.0.16

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 (161) hide show
  1. package/dist/extensions/feishu/src/adapters/onboard-cli.d.ts +7 -0
  2. package/dist/extensions/feishu/src/adapters/onboard-cli.js +432 -0
  3. package/dist/extensions/feishu/src/adapters/onboard-cli.js.map +1 -0
  4. package/dist/extensions/feishu/src/auth/pairing.d.ts +7 -0
  5. package/dist/extensions/feishu/src/auth/pairing.js +45 -0
  6. package/dist/extensions/feishu/src/auth/pairing.js.map +1 -0
  7. package/dist/extensions/feishu/src/auth/paths.d.ts +2 -0
  8. package/dist/extensions/feishu/src/auth/paths.js +18 -0
  9. package/dist/extensions/feishu/src/auth/paths.js.map +1 -0
  10. package/dist/extensions/feishu/src/directory/directory-adapter.d.ts +2 -0
  11. package/dist/extensions/feishu/src/directory/directory-adapter.js +27 -0
  12. package/dist/extensions/feishu/src/directory/directory-adapter.js.map +1 -0
  13. package/dist/extensions/feishu/src/format.d.ts +21 -0
  14. package/dist/extensions/feishu/src/format.js +99 -0
  15. package/dist/extensions/feishu/src/format.js.map +1 -0
  16. package/dist/extensions/feishu/src/index.d.ts +5 -0
  17. package/dist/extensions/feishu/src/index.js +3 -0
  18. package/dist/extensions/feishu/src/outbound/actions.d.ts +51 -0
  19. package/dist/extensions/feishu/src/outbound/actions.js +62 -0
  20. package/dist/extensions/feishu/src/outbound/actions.js.map +1 -0
  21. package/dist/extensions/feishu/src/outbound/media-load.d.ts +12 -0
  22. package/dist/extensions/feishu/src/outbound/media-load.js +125 -0
  23. package/dist/extensions/feishu/src/outbound/media-load.js.map +1 -0
  24. package/dist/extensions/feishu/src/outbound/outbound-adapter.d.ts +2 -0
  25. package/dist/extensions/feishu/src/outbound/outbound-adapter.js +201 -0
  26. package/dist/extensions/feishu/src/outbound/outbound-adapter.js.map +1 -0
  27. package/dist/extensions/feishu/src/plugin.d.ts +70 -0
  28. package/dist/extensions/feishu/src/plugin.js +313 -0
  29. package/dist/extensions/feishu/src/plugin.js.map +1 -0
  30. package/dist/extensions/feishu/src/schema/config-schema.d.ts +215 -0
  31. package/dist/extensions/feishu/src/schema/config-schema.js +198 -0
  32. package/dist/extensions/feishu/src/schema/config-schema.js.map +1 -0
  33. package/dist/extensions/feishu/src/state/accounts.d.ts +38 -0
  34. package/dist/extensions/feishu/src/state/accounts.js +96 -0
  35. package/dist/extensions/feishu/src/state/accounts.js.map +1 -0
  36. package/dist/extensions/feishu/src/state/message-bindings.d.ts +11 -0
  37. package/dist/extensions/feishu/src/state/message-bindings.js +41 -0
  38. package/dist/extensions/feishu/src/state/message-bindings.js.map +1 -0
  39. package/dist/extensions/feishu/src/state/thread-bindings.js +46 -0
  40. package/dist/extensions/feishu/src/state/thread-bindings.js.map +1 -0
  41. package/dist/extensions/feishu/src/status/doctor.d.ts +2 -0
  42. package/dist/extensions/feishu/src/status/doctor.js +38 -0
  43. package/dist/extensions/feishu/src/status/doctor.js.map +1 -0
  44. package/dist/extensions/feishu/src/status/status-adapter.d.ts +3 -0
  45. package/dist/extensions/feishu/src/status/status-adapter.js +45 -0
  46. package/dist/extensions/feishu/src/status/status-adapter.js.map +1 -0
  47. package/dist/extensions/feishu/src/streaming/streaming-adapter.d.ts +3 -0
  48. package/dist/extensions/feishu/src/streaming/streaming-adapter.js +242 -0
  49. package/dist/extensions/feishu/src/streaming/streaming-adapter.js.map +1 -0
  50. package/dist/extensions/feishu/src/subagent-hooks.js +52 -0
  51. package/dist/extensions/feishu/src/subagent-hooks.js.map +1 -0
  52. package/dist/extensions/feishu/src/tools/docx/docx-batch-insert.js +95 -0
  53. package/dist/extensions/feishu/src/tools/docx/docx-batch-insert.js.map +1 -0
  54. package/dist/extensions/feishu/src/tools/docx/docx-color-text.js +75 -0
  55. package/dist/extensions/feishu/src/tools/docx/docx-color-text.js.map +1 -0
  56. package/dist/extensions/feishu/src/tools/docx/docx-table-ops.js +173 -0
  57. package/dist/extensions/feishu/src/tools/docx/docx-table-ops.js.map +1 -0
  58. package/dist/extensions/feishu/src/tools/docx/docx-types.js +1 -0
  59. package/dist/extensions/feishu/src/tools/tools.d.ts +5 -0
  60. package/dist/extensions/feishu/src/tools/tools.js +46 -0
  61. package/dist/extensions/feishu/src/tools/tools.js.map +1 -0
  62. package/dist/extensions/feishu/src/transport/client/client.d.ts +6 -0
  63. package/dist/extensions/feishu/src/transport/client/client.js +41 -0
  64. package/dist/extensions/feishu/src/transport/client/client.js.map +1 -0
  65. package/dist/extensions/feishu/src/transport/client/lark-sdk-logger.d.ts +13 -0
  66. package/dist/extensions/feishu/src/transport/client/lark-sdk-logger.js +104 -0
  67. package/dist/extensions/feishu/src/transport/client/lark-sdk-logger.js.map +1 -0
  68. package/dist/extensions/feishu/src/transport/reliability/dedupe.d.ts +7 -0
  69. package/dist/extensions/feishu/src/transport/reliability/dedupe.js +30 -0
  70. package/dist/extensions/feishu/src/transport/reliability/dedupe.js.map +1 -0
  71. package/dist/extensions/feishu/src/transport/socket-mode/monitor.d.ts +19 -0
  72. package/dist/extensions/feishu/src/transport/socket-mode/monitor.js +326 -0
  73. package/dist/extensions/feishu/src/transport/socket-mode/monitor.js.map +1 -0
  74. package/dist/extensions/feishu/src/transport/socket-mode/retry.d.ts +1 -0
  75. package/dist/extensions/feishu/src/transport/socket-mode/retry.js +10 -0
  76. package/dist/extensions/feishu/src/transport/socket-mode/retry.js.map +1 -0
  77. package/dist/extensions/feishu/src/transport/text/mentions.d.ts +1 -0
  78. package/dist/extensions/feishu/src/transport/text/mentions.js +9 -0
  79. package/dist/extensions/feishu/src/transport/text/mentions.js.map +1 -0
  80. package/dist/extensions/feishu/src/transport/webhook/monitor.d.ts +19 -0
  81. package/dist/extensions/feishu/src/transport/webhook/monitor.js +271 -0
  82. package/dist/extensions/feishu/src/transport/webhook/monitor.js.map +1 -0
  83. package/dist/extensions/feishu/src/ui/config-surface.d.ts +2 -0
  84. package/dist/extensions/feishu/src/ui/config-surface.js +6 -0
  85. package/dist/extensions/feishu/src/ui/config-surface.js.map +1 -0
  86. package/dist/extensions/feishu/xopc.extension.json +18 -0
  87. package/dist/extensions/telegram/xopc.extension.json +20 -0
  88. package/dist/extensions/weixin/xopc.extension.json +17 -0
  89. package/dist/gateway/static/root/assets/{agents-C2blSFQk.js → agents-Dy5cGVVQ.js} +2 -2
  90. package/dist/gateway/static/root/assets/{agents-C2blSFQk.js.map → agents-Dy5cGVVQ.js.map} +1 -1
  91. package/dist/gateway/static/root/assets/{apps-page-Dg7InQ41.js → apps-page-BOpDR0Lz.js} +2 -2
  92. package/dist/gateway/static/root/assets/{apps-page-Dg7InQ41.js.map → apps-page-BOpDR0Lz.js.map} +1 -1
  93. package/dist/gateway/static/root/assets/channels-settings-CrCesccB.js +9 -0
  94. package/dist/gateway/static/root/assets/channels-settings-CrCesccB.js.map +1 -0
  95. package/dist/gateway/static/root/assets/{cron-page-B-7O4_QQ.js → cron-page-B_XY0gPt.js} +2 -2
  96. package/dist/gateway/static/root/assets/{cron-page-B-7O4_QQ.js.map → cron-page-B_XY0gPt.js.map} +1 -1
  97. package/dist/gateway/static/root/assets/{cron-utils-DvRPM814.js → cron-utils-BYdnLwhl.js} +2 -2
  98. package/dist/gateway/static/root/assets/{cron-utils-DvRPM814.js.map → cron-utils-BYdnLwhl.js.map} +1 -1
  99. package/dist/gateway/static/root/assets/{dist-DYzyRkRh.js → dist-DvaA5uNp.js} +2 -2
  100. package/dist/gateway/static/root/assets/{dist-DYzyRkRh.js.map → dist-DvaA5uNp.js.map} +1 -1
  101. package/dist/gateway/static/root/assets/{extension-debug-page-DV-NwlaY.js → extension-debug-page-CPSk7gFW.js} +2 -2
  102. package/dist/gateway/static/root/assets/{extension-debug-page-DV-NwlaY.js.map → extension-debug-page-CPSk7gFW.js.map} +1 -1
  103. package/dist/gateway/static/root/assets/{extension-page-UUKLJwpo.js → extension-page-COdbk9I6.js} +2 -2
  104. package/dist/gateway/static/root/assets/{extension-page-UUKLJwpo.js.map → extension-page-COdbk9I6.js.map} +1 -1
  105. package/dist/gateway/static/root/assets/{extension-settings-page-R4VlbZOj.js → extension-settings-page-BlEz2Ily.js} +2 -2
  106. package/dist/gateway/static/root/assets/{extension-settings-page-R4VlbZOj.js.map → extension-settings-page-BlEz2Ily.js.map} +1 -1
  107. package/dist/gateway/static/root/assets/index-BQNdJlkw.css +1 -0
  108. package/dist/gateway/static/root/assets/index-tm9ZY35l.js +144 -0
  109. package/dist/gateway/static/root/assets/{index-BCVqNi5T.js.map → index-tm9ZY35l.js.map} +1 -1
  110. package/dist/gateway/static/root/assets/{logs-page-BgUDjMge.js → logs-page-LSa0jmLO.js} +2 -2
  111. package/dist/gateway/static/root/assets/{logs-page-BgUDjMge.js.map → logs-page-LSa0jmLO.js.map} +1 -1
  112. package/dist/gateway/static/root/assets/sessions-page-cn2fi_V3.js +2 -0
  113. package/dist/gateway/static/root/assets/sessions-page-cn2fi_V3.js.map +1 -0
  114. package/dist/gateway/static/root/assets/{settings-page-Doa_lzdW.js → settings-page-CyHd5szQ.js} +2 -2
  115. package/dist/gateway/static/root/assets/{settings-page-Doa_lzdW.js.map → settings-page-CyHd5szQ.js.map} +1 -1
  116. package/dist/gateway/static/root/assets/{skills-page-BdNqg2NG.js → skills-page-irjxwW9u.js} +2 -2
  117. package/dist/gateway/static/root/assets/{skills-page-BdNqg2NG.js.map → skills-page-irjxwW9u.js.map} +1 -1
  118. package/dist/gateway/static/root/channel-icons/feishu.svg +12 -0
  119. package/dist/gateway/static/root/channel-icons/lark.svg +12 -0
  120. package/dist/gateway/static/root/channel-icons/telegram.svg +1 -0
  121. package/dist/gateway/static/root/channel-icons/wechat.svg +1 -0
  122. package/dist/gateway/static/root/channel-icons/weixin.svg +1 -0
  123. package/dist/gateway/static/root/index.html +2 -2
  124. package/dist/package.js +1 -1
  125. package/dist/src/agent/agent-manager.d.ts +1 -0
  126. package/dist/src/agent/agent-manager.js +1 -0
  127. package/dist/src/agent/agent-manager.js.map +1 -1
  128. package/dist/src/agent/service.js +3 -0
  129. package/dist/src/agent/service.js.map +1 -1
  130. package/dist/src/agent/tools/delegate-tool.d.ts +8 -0
  131. package/dist/src/agent/tools/delegate-tool.js +60 -2
  132. package/dist/src/agent/tools/delegate-tool.js.map +1 -1
  133. package/dist/src/agent/tools/factory.d.ts +1 -0
  134. package/dist/src/agent/tools/factory.js +2 -0
  135. package/dist/src/agent/tools/factory.js.map +1 -1
  136. package/dist/src/channels/envelope-timestamp.d.ts +5 -0
  137. package/dist/src/channels/envelope-timestamp.js +10 -1
  138. package/dist/src/channels/envelope-timestamp.js.map +1 -1
  139. package/dist/src/channels/feishu/index.d.ts +5 -0
  140. package/dist/src/channels/feishu/index.js +4 -0
  141. package/dist/src/chat-commands/types.d.ts +1 -1
  142. package/dist/src/extensions/types/hooks.d.ts +46 -1
  143. package/dist/src/extensions/types/hooks.js +3 -0
  144. package/dist/src/extensions/types/hooks.js.map +1 -1
  145. package/dist/src/gateway/service.js +1 -1
  146. package/dist/src/generated/bundled-channel-plugins.d.ts +2 -1
  147. package/dist/src/generated/bundled-channel-plugins.js +8 -2
  148. package/dist/src/generated/bundled-channel-plugins.js.map +1 -1
  149. package/dist/src/providers/env-keys.js +1 -0
  150. package/dist/src/providers/env-keys.js.map +1 -1
  151. package/dist/src/providers/index.js +5 -0
  152. package/dist/src/providers/index.js.map +1 -1
  153. package/dist/src/session/session-title.js +2 -1
  154. package/dist/src/session/session-title.js.map +1 -1
  155. package/package.json +2 -2
  156. package/dist/gateway/static/root/assets/channels-settings-CHOL7G_P.js +0 -9
  157. package/dist/gateway/static/root/assets/channels-settings-CHOL7G_P.js.map +0 -1
  158. package/dist/gateway/static/root/assets/index-BCVqNi5T.js +0 -144
  159. package/dist/gateway/static/root/assets/index-XbYityMf.css +0 -1
  160. package/dist/gateway/static/root/assets/sessions-page-L2VUocA4.js +0 -2
  161. package/dist/gateway/static/root/assets/sessions-page-L2VUocA4.js.map +0 -1
@@ -164,6 +164,8 @@ var AgentToolsFactory = class {
164
164
  },
165
165
  bus: this.deps.bus,
166
166
  getConfig: () => this.deps.getConfig?.(),
167
+ getCurrentContext: () => this.deps.getCurrentContext?.() ?? null,
168
+ hookRunner: this.deps.hookRunner,
167
169
  toolExecutorConfig: this.deps.toolExecutorConfig
168
170
  })] : [],
169
171
  ...optionalTools
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","names":["parseRoutingSessionKey"],"sources":["../../../../src/agent/tools/factory.ts"],"sourcesContent":["/**\n * Agent Tools Factory - Creates and configures agent tools\n *\n * Centralizes tool creation logic to keep service.ts focused on orchestration.\n *\n * TTS: auto TTS is applied at the ChannelManager via maybeApplyTtsToPayload().\n * Optional \\`text_to_speech\\` tool sends explicit voice when TTS is enabled.\n */\n\nimport type { AgentTool } from '@mariozechner/pi-agent-core';\nimport type { Model, Api } from '@mariozechner/pi-ai';\nimport type { Config } from '../../config/schema.js';\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport {\n createReadFileTool,\n createWriteFileTool,\n createEditFileTool,\n createListDirTool,\n createGrepTool,\n createFindTool,\n createShellTool,\n createWebSearchTool,\n webFetchTool,\n createWebExtractTool,\n createMessageTool,\n createSendMediaTool,\n createMemorySearchTool,\n createMemoryGetTool,\n createTodoTool,\n createSessionStatusTool,\n createClarifyTool,\n} from './index.js';\nimport { createCuratedMemoryTool } from './curated-memory-tool.js';\nimport { createSessionSearchTool } from './session-search-tool.js';\nimport type { BuiltinMemoryStore } from '../memory/builtin-memory-store.js';\nimport type { MemoryManager } from '../memory/manager.js';\nimport { shouldRegisterCuratedMemoryTool } from '../memory/memory-config.js';\nimport type { SessionStore } from '../../session/store.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../../routing/session-key.js';\nimport type { GatewayClarifyRequestFn } from './clarify-tool.js';\nimport { createImageTool } from './image-tool.js';\nimport { createImageGenerateTool } from './image-generate-tool.js';\nimport { BrowserManager, createBrowserTools } from './browser/index.js';\nimport { createDelegateTool } from './delegate-tool.js';\nimport { buildSandboxToolMap, createExecuteCodeTool } from './execute-code-tool.js';\nimport { createCronjobTool } from './cronjob-tool.js';\nimport type { CronService } from '../../cron/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { SkillManager } from '../skills/skill-manager.js';\nimport { wrapToolsWithProtection, type ToolExecutorConfig } from './executor.js';\nimport { createSkillsListTool, createSkillViewTool } from './skills-tools.js';\nimport { createSkillManageTool } from './skill-manage-tool.js';\nimport { createTextToSpeechTool } from './tts-tool.js';\nimport { mergeTtsConfigFromAppConfig } from '../../voice/tts/merge-config.js';\n\nconst log = createLogger('AgentToolsFactory');\n\n/** Channels where `clarify` can block for a user answer (web UI, Telegram, CLI readline). */\nconst CLARIFY_SUPPORTED_CHANNELS = new Set(['webchat', 'telegram', 'cli']);\n\nfunction clarifyTransportSource(sessionKey: string): string | undefined {\n const parsed = parseRoutingSessionKey(sessionKey);\n if (parsed) return parsed.source;\n // Fallback for simple `<channel>:<chatId>` keys used by webchat and CLI.\n const first = sessionKey.split(':').filter(Boolean)[0] ?? '';\n if (first === 'cli' || first === 'webchat') return first;\n return undefined;\n}\n\nexport interface ToolFactoryDeps {\n workspace: string;\n extensionRegistry?: any;\n getCurrentContext: () => { channel: string; chatId: string; sessionKey: string } | null;\n bus: MessageBus;\n toolExecutorConfig?: Partial<ToolExecutorConfig>;\n /** Agent defaults (image tools, etc.); use getter so hot-reloaded config applies. */\n getConfig?: () => Config | undefined;\n /** Session / default chat model for vision tool description. */\n getPrimaryModel?: () => Model<Api>;\n /** Built-in curated memory store (agent home `memories/`). */\n getBuiltinMemoryStore?: () => BuiltinMemoryStore;\n /** Memory orchestration (prefetch/sync + external tools). */\n getMemoryManager?: () => MemoryManager;\n /** Session store for `session_search`. */\n getSessionStore?: () => SessionStore;\n /** When set (gateway webchat), enables the `clarify` tool. */\n gatewayClarify?: { requestClarification: GatewayClarifyRequestFn };\n /** Gateway: enables the `cronjob` tool. */\n getCronService?: () => CronService | undefined;\n /** Current session skill indexing (tool gating + allowlist); used by skills_list / skill_view. */\n getSkillIndexingContext?: () =>\n | { registeredToolNames: string[]; skillAllowlist?: string[] }\n | undefined;\n /** After skill_manage mutates disk, reload skills + refresh agent prompts (optional). */\n onSkillsFilesystemMutate?: () => void;\n /** Names registered via skill_view for shell env passthrough. */\n getSkillPassthroughEnvVarNames?: () => string[];\n /** Add declared env names for the current session (no values stored). */\n registerSkillEnvPassthrough?: (names: string[]) => void;\n}\n\nexport interface CreateCoreToolsOptions {\n /** Workspace root for file/shell tools (defaults to factory workspace). */\n workspace?: string;\n /** `…/agents/<id>/bootstrap` — used so `read_file` can find SOUL.md etc. by filename. */\n bootstrapDir?: string;\n /** Tool `name` values to omit (e.g. `shell`, `extensions` for extension tools). */\n disabledTools?: Set<string>;\n /** Optional primary model for image tool heuristics. */\n getPrimaryModel?: () => Model<Api>;\n getBuiltinMemoryStore?: () => BuiltinMemoryStore;\n getMemoryManager?: () => MemoryManager;\n /** When set, registers `skills_list` and `skill_view` bound to this workspace\\'s skills. */\n getSkillManager?: () => SkillManager;\n}\n\nexport class AgentToolsFactory {\n private browserManager: BrowserManager | null = null;\n\n constructor(private deps: ToolFactoryDeps) {}\n\n private ensureBrowserManager(): BrowserManager {\n if (!this.browserManager) {\n this.browserManager = new BrowserManager({\n getHeadless: () => this.deps.getConfig?.()?.agents?.defaults?.browser?.headless !== false,\n });\n }\n return this.browserManager;\n }\n\n /** Close Playwright and all pages (gateway stop, agent manager dispose, or config hot-reload). */\n async shutdownBrowser(): Promise<void> {\n if (!this.browserManager) {\n return;\n }\n await this.browserManager.shutdown();\n this.browserManager = null;\n }\n\n /** Drop the tab for a session when its agent instance is removed. */\n async closeBrowserPageForSession(sessionKey: string): Promise<void> {\n await this.browserManager?.closePage(sessionKey);\n }\n\n createCoreTools(options?: CreateCoreToolsOptions): AgentTool<any, any>[] {\n const workspace = options?.workspace ?? this.deps.workspace;\n const { bus } = this.deps;\n const getPrimary = options?.getPrimaryModel ?? this.deps.getPrimaryModel;\n const getBuiltin = options?.getBuiltinMemoryStore ?? this.deps.getBuiltinMemoryStore;\n const getMemMgr = options?.getMemoryManager ?? this.deps.getMemoryManager;\n const getSkillMgr = options?.getSkillManager;\n const disabled = options?.disabledTools;\n\n const primary = getPrimary?.();\n const modelHasVision = primary?.input?.includes('image') ?? false;\n const cfg = this.deps.getConfig?.();\n const imageTool = createImageTool({\n config: cfg,\n workspace,\n modelHasVision,\n });\n const imageGenerateTool = createImageGenerateTool({\n config: cfg,\n workspace,\n });\n\n const optionalTools = [imageTool, imageGenerateTool].filter((t) => t != null) as any[];\n\n const readTool = createReadFileTool(workspace, { bootstrapDir: options?.bootstrapDir });\n const writeTool = createWriteFileTool(workspace);\n const editTool = createEditFileTool(workspace);\n const listDir = createListDirTool(workspace);\n const grep = createGrepTool(workspace);\n const find = createFindTool(workspace);\n\n const core: AgentTool<any, any>[] = [\n createSessionStatusTool(),\n createClarifyTool({\n resolveAskUser: () => {\n const req = this.deps.gatewayClarify?.requestClarification;\n if (!req) return null;\n const ctx = this.deps.getCurrentContext();\n if (!ctx?.sessionKey) return null;\n const source = clarifyTransportSource(ctx.sessionKey);\n if (!source || !CLARIFY_SUPPORTED_CHANNELS.has(source)) return null;\n return (r) => req(ctx.sessionKey, r);\n },\n }),\n createTodoTool({\n getSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\n }),\n ...(getSkillMgr\n ? [\n createSkillsListTool({\n getSkillManager: getSkillMgr,\n getSkillIndexingContext: this.deps.getSkillIndexingContext,\n }),\n createSkillViewTool({\n getSkillManager: getSkillMgr,\n getSkillIndexingContext: this.deps.getSkillIndexingContext,\n registerSkillEnvPassthrough: this.deps.registerSkillEnvPassthrough,\n }),\n createSkillManageTool({\n getSkillManager: getSkillMgr,\n getWorkspace: () => workspace,\n onSkillsFilesystemMutate: this.deps.onSkillsFilesystemMutate,\n }),\n ]\n : []),\n readTool,\n writeTool,\n editTool,\n listDir,\n grep,\n find,\n createShellTool(workspace, {\n getSkillPassthroughEnvVarNames: this.deps.getSkillPassthroughEnvVarNames,\n }),\n createWebSearchTool(() => this.deps.getConfig?.()),\n webFetchTool,\n createWebExtractTool({ getConfig: () => this.deps.getConfig?.() }),\n // Note: TTS is NOT handled by send_message tool anymore\n // TTS is applied at the ChannelManager dispatch layer\n createMessageTool(bus, () => this.deps.getCurrentContext()),\n ...(mergeTtsConfigFromAppConfig(cfg?.tts).enabled\n ? [\n createTextToSpeechTool({\n bus,\n getContext: () => this.deps.getCurrentContext(),\n getConfig: () => this.deps.getConfig?.(),\n }),\n ]\n : []),\n createSendMediaTool(workspace, bus, () => this.deps.getCurrentContext()),\n createMemorySearchTool(workspace),\n createMemoryGetTool(workspace),\n ...(getBuiltin && shouldRegisterCuratedMemoryTool(this.deps.getConfig?.())\n ? [\n createCuratedMemoryTool(getBuiltin, {\n onMemoryWrite: (action, target, content) => {\n getMemMgr?.().onMemoryWrite(action, target, content);\n },\n }),\n ]\n : []),\n ...(getMemMgr?.().getAdditionalTools() ?? []),\n ...(this.deps.getSessionStore\n ? [\n createSessionSearchTool({\n getSessionStore: this.deps.getSessionStore,\n getConfig: this.deps.getConfig,\n getCurrentSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\n }),\n ]\n : []),\n ...(this.deps.getCronService\n ? [\n createCronjobTool({\n getCronService: this.deps.getCronService,\n }),\n ]\n : []),\n ...(cfg?.agents?.defaults?.browser?.enabled === true\n ? createBrowserTools({\n getManager: () => this.ensureBrowserManager(),\n getTaskId: () => this.deps.getCurrentContext()?.sessionKey ?? 'default',\n getConfig: () => this.deps.getConfig?.(),\n })\n : []),\n ...(cfg?.agents?.defaults?.delegate?.enabled === true && primary\n ? [\n createDelegateTool({\n workspace,\n getSubagentModel: () => {\n const gp = options?.getPrimaryModel ?? this.deps.getPrimaryModel;\n const m = gp?.();\n if (!m) {\n throw new Error('No primary model configured for delegate_task');\n }\n return m;\n },\n bus: this.deps.bus,\n getConfig: () => this.deps.getConfig?.(),\n toolExecutorConfig: this.deps.toolExecutorConfig,\n }),\n ]\n : []),\n ...optionalTools,\n ];\n\n return filterToolsByDisabledSet(core, disabled);\n }\n\n createAllTools(coreOptions?: CreateCoreToolsOptions): AgentTool<any, any>[] {\n const coreTools = this.createCoreTools(coreOptions);\n const disableExtensions = coreOptions?.disabledTools?.has('extensions');\n const cfg = this.deps.getConfig?.();\n\n let bundled: AgentTool<any, any>[];\n if (!this.deps.extensionRegistry || disableExtensions) {\n bundled = coreTools;\n } else {\n const extensionTools = this.deps.extensionRegistry.getAllTools();\n log.info({ count: extensionTools.length }, 'Loaded extension tools');\n bundled = [...coreTools, ...extensionTools];\n }\n\n const wrapped = wrapToolsWithProtection(bundled, this.deps.toolExecutorConfig);\n\n const executeEnabled =\n cfg?.agents?.defaults?.executeCode?.enabled === true &&\n !coreOptions?.disabledTools?.has('execute_code');\n\n if (executeEnabled) {\n const sandboxMap = buildSandboxToolMap(wrapped);\n const executeTool = createExecuteCodeTool({ getSandboxToolMap: () => sandboxMap });\n const wrappedExecute = wrapToolsWithProtection([executeTool as any], this.deps.toolExecutorConfig);\n return [...wrapped, ...wrappedExecute];\n }\n\n return wrapped;\n }\n}\n\nfunction filterToolsByDisabledSet(\n tools: any[],\n disabled: Set<string> | undefined,\n): any[] {\n if (!disabled || disabled.size === 0) {\n return tools;\n }\n return tools.filter((t) => !disabled.has(t.name));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"factory.js","names":["parseRoutingSessionKey"],"sources":["../../../../src/agent/tools/factory.ts"],"sourcesContent":["/**\n * Agent Tools Factory - Creates and configures agent tools\n *\n * Centralizes tool creation logic to keep service.ts focused on orchestration.\n *\n * TTS: auto TTS is applied at the ChannelManager via maybeApplyTtsToPayload().\n * Optional \\`text_to_speech\\` tool sends explicit voice when TTS is enabled.\n */\n\nimport type { AgentTool } from '@mariozechner/pi-agent-core';\nimport type { Model, Api } from '@mariozechner/pi-ai';\nimport type { Config } from '../../config/schema.js';\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport {\n createReadFileTool,\n createWriteFileTool,\n createEditFileTool,\n createListDirTool,\n createGrepTool,\n createFindTool,\n createShellTool,\n createWebSearchTool,\n webFetchTool,\n createWebExtractTool,\n createMessageTool,\n createSendMediaTool,\n createMemorySearchTool,\n createMemoryGetTool,\n createTodoTool,\n createSessionStatusTool,\n createClarifyTool,\n} from './index.js';\nimport { createCuratedMemoryTool } from './curated-memory-tool.js';\nimport { createSessionSearchTool } from './session-search-tool.js';\nimport type { BuiltinMemoryStore } from '../memory/builtin-memory-store.js';\nimport type { MemoryManager } from '../memory/manager.js';\nimport { shouldRegisterCuratedMemoryTool } from '../memory/memory-config.js';\nimport type { SessionStore } from '../../session/store.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../../routing/session-key.js';\nimport type { GatewayClarifyRequestFn } from './clarify-tool.js';\nimport { createImageTool } from './image-tool.js';\nimport { createImageGenerateTool } from './image-generate-tool.js';\nimport { BrowserManager, createBrowserTools } from './browser/index.js';\nimport { createDelegateTool } from './delegate-tool.js';\nimport { buildSandboxToolMap, createExecuteCodeTool } from './execute-code-tool.js';\nimport { createCronjobTool } from './cronjob-tool.js';\nimport type { CronService } from '../../cron/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { SkillManager } from '../skills/skill-manager.js';\nimport { wrapToolsWithProtection, type ToolExecutorConfig } from './executor.js';\nimport { createSkillsListTool, createSkillViewTool } from './skills-tools.js';\nimport { createSkillManageTool } from './skill-manage-tool.js';\nimport { createTextToSpeechTool } from './tts-tool.js';\nimport { mergeTtsConfigFromAppConfig } from '../../voice/tts/merge-config.js';\n\nconst log = createLogger('AgentToolsFactory');\n\n/** Channels where `clarify` can block for a user answer (web UI, Telegram, CLI readline). */\nconst CLARIFY_SUPPORTED_CHANNELS = new Set(['webchat', 'telegram', 'cli']);\n\nfunction clarifyTransportSource(sessionKey: string): string | undefined {\n const parsed = parseRoutingSessionKey(sessionKey);\n if (parsed) return parsed.source;\n // Fallback for simple `<channel>:<chatId>` keys used by webchat and CLI.\n const first = sessionKey.split(':').filter(Boolean)[0] ?? '';\n if (first === 'cli' || first === 'webchat') return first;\n return undefined;\n}\n\nexport interface ToolFactoryDeps {\n workspace: string;\n extensionRegistry?: any;\n getCurrentContext: () => { channel: string; chatId: string; sessionKey: string } | null;\n hookRunner?: import('../../extensions/index.js').ExtensionHookRunner;\n bus: MessageBus;\n toolExecutorConfig?: Partial<ToolExecutorConfig>;\n /** Agent defaults (image tools, etc.); use getter so hot-reloaded config applies. */\n getConfig?: () => Config | undefined;\n /** Session / default chat model for vision tool description. */\n getPrimaryModel?: () => Model<Api>;\n /** Built-in curated memory store (agent home `memories/`). */\n getBuiltinMemoryStore?: () => BuiltinMemoryStore;\n /** Memory orchestration (prefetch/sync + external tools). */\n getMemoryManager?: () => MemoryManager;\n /** Session store for `session_search`. */\n getSessionStore?: () => SessionStore;\n /** When set (gateway webchat), enables the `clarify` tool. */\n gatewayClarify?: { requestClarification: GatewayClarifyRequestFn };\n /** Gateway: enables the `cronjob` tool. */\n getCronService?: () => CronService | undefined;\n /** Current session skill indexing (tool gating + allowlist); used by skills_list / skill_view. */\n getSkillIndexingContext?: () =>\n | { registeredToolNames: string[]; skillAllowlist?: string[] }\n | undefined;\n /** After skill_manage mutates disk, reload skills + refresh agent prompts (optional). */\n onSkillsFilesystemMutate?: () => void;\n /** Names registered via skill_view for shell env passthrough. */\n getSkillPassthroughEnvVarNames?: () => string[];\n /** Add declared env names for the current session (no values stored). */\n registerSkillEnvPassthrough?: (names: string[]) => void;\n}\n\nexport interface CreateCoreToolsOptions {\n /** Workspace root for file/shell tools (defaults to factory workspace). */\n workspace?: string;\n /** `…/agents/<id>/bootstrap` — used so `read_file` can find SOUL.md etc. by filename. */\n bootstrapDir?: string;\n /** Tool `name` values to omit (e.g. `shell`, `extensions` for extension tools). */\n disabledTools?: Set<string>;\n /** Optional primary model for image tool heuristics. */\n getPrimaryModel?: () => Model<Api>;\n getBuiltinMemoryStore?: () => BuiltinMemoryStore;\n getMemoryManager?: () => MemoryManager;\n /** When set, registers `skills_list` and `skill_view` bound to this workspace\\'s skills. */\n getSkillManager?: () => SkillManager;\n}\n\nexport class AgentToolsFactory {\n private browserManager: BrowserManager | null = null;\n\n constructor(private deps: ToolFactoryDeps) {}\n\n private ensureBrowserManager(): BrowserManager {\n if (!this.browserManager) {\n this.browserManager = new BrowserManager({\n getHeadless: () => this.deps.getConfig?.()?.agents?.defaults?.browser?.headless !== false,\n });\n }\n return this.browserManager;\n }\n\n /** Close Playwright and all pages (gateway stop, agent manager dispose, or config hot-reload). */\n async shutdownBrowser(): Promise<void> {\n if (!this.browserManager) {\n return;\n }\n await this.browserManager.shutdown();\n this.browserManager = null;\n }\n\n /** Drop the tab for a session when its agent instance is removed. */\n async closeBrowserPageForSession(sessionKey: string): Promise<void> {\n await this.browserManager?.closePage(sessionKey);\n }\n\n createCoreTools(options?: CreateCoreToolsOptions): AgentTool<any, any>[] {\n const workspace = options?.workspace ?? this.deps.workspace;\n const { bus } = this.deps;\n const getPrimary = options?.getPrimaryModel ?? this.deps.getPrimaryModel;\n const getBuiltin = options?.getBuiltinMemoryStore ?? this.deps.getBuiltinMemoryStore;\n const getMemMgr = options?.getMemoryManager ?? this.deps.getMemoryManager;\n const getSkillMgr = options?.getSkillManager;\n const disabled = options?.disabledTools;\n\n const primary = getPrimary?.();\n const modelHasVision = primary?.input?.includes('image') ?? false;\n const cfg = this.deps.getConfig?.();\n const imageTool = createImageTool({\n config: cfg,\n workspace,\n modelHasVision,\n });\n const imageGenerateTool = createImageGenerateTool({\n config: cfg,\n workspace,\n });\n\n const optionalTools = [imageTool, imageGenerateTool].filter((t) => t != null) as any[];\n\n const readTool = createReadFileTool(workspace, { bootstrapDir: options?.bootstrapDir });\n const writeTool = createWriteFileTool(workspace);\n const editTool = createEditFileTool(workspace);\n const listDir = createListDirTool(workspace);\n const grep = createGrepTool(workspace);\n const find = createFindTool(workspace);\n\n const core: AgentTool<any, any>[] = [\n createSessionStatusTool(),\n createClarifyTool({\n resolveAskUser: () => {\n const req = this.deps.gatewayClarify?.requestClarification;\n if (!req) return null;\n const ctx = this.deps.getCurrentContext();\n if (!ctx?.sessionKey) return null;\n const source = clarifyTransportSource(ctx.sessionKey);\n if (!source || !CLARIFY_SUPPORTED_CHANNELS.has(source)) return null;\n return (r) => req(ctx.sessionKey, r);\n },\n }),\n createTodoTool({\n getSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\n }),\n ...(getSkillMgr\n ? [\n createSkillsListTool({\n getSkillManager: getSkillMgr,\n getSkillIndexingContext: this.deps.getSkillIndexingContext,\n }),\n createSkillViewTool({\n getSkillManager: getSkillMgr,\n getSkillIndexingContext: this.deps.getSkillIndexingContext,\n registerSkillEnvPassthrough: this.deps.registerSkillEnvPassthrough,\n }),\n createSkillManageTool({\n getSkillManager: getSkillMgr,\n getWorkspace: () => workspace,\n onSkillsFilesystemMutate: this.deps.onSkillsFilesystemMutate,\n }),\n ]\n : []),\n readTool,\n writeTool,\n editTool,\n listDir,\n grep,\n find,\n createShellTool(workspace, {\n getSkillPassthroughEnvVarNames: this.deps.getSkillPassthroughEnvVarNames,\n }),\n createWebSearchTool(() => this.deps.getConfig?.()),\n webFetchTool,\n createWebExtractTool({ getConfig: () => this.deps.getConfig?.() }),\n // Note: TTS is NOT handled by send_message tool anymore\n // TTS is applied at the ChannelManager dispatch layer\n createMessageTool(bus, () => this.deps.getCurrentContext()),\n ...(mergeTtsConfigFromAppConfig(cfg?.tts).enabled\n ? [\n createTextToSpeechTool({\n bus,\n getContext: () => this.deps.getCurrentContext(),\n getConfig: () => this.deps.getConfig?.(),\n }),\n ]\n : []),\n createSendMediaTool(workspace, bus, () => this.deps.getCurrentContext()),\n createMemorySearchTool(workspace),\n createMemoryGetTool(workspace),\n ...(getBuiltin && shouldRegisterCuratedMemoryTool(this.deps.getConfig?.())\n ? [\n createCuratedMemoryTool(getBuiltin, {\n onMemoryWrite: (action, target, content) => {\n getMemMgr?.().onMemoryWrite(action, target, content);\n },\n }),\n ]\n : []),\n ...(getMemMgr?.().getAdditionalTools() ?? []),\n ...(this.deps.getSessionStore\n ? [\n createSessionSearchTool({\n getSessionStore: this.deps.getSessionStore,\n getConfig: this.deps.getConfig,\n getCurrentSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\n }),\n ]\n : []),\n ...(this.deps.getCronService\n ? [\n createCronjobTool({\n getCronService: this.deps.getCronService,\n }),\n ]\n : []),\n ...(cfg?.agents?.defaults?.browser?.enabled === true\n ? createBrowserTools({\n getManager: () => this.ensureBrowserManager(),\n getTaskId: () => this.deps.getCurrentContext()?.sessionKey ?? 'default',\n getConfig: () => this.deps.getConfig?.(),\n })\n : []),\n ...(cfg?.agents?.defaults?.delegate?.enabled === true && primary\n ? [\n createDelegateTool({\n workspace,\n getSubagentModel: () => {\n const gp = options?.getPrimaryModel ?? this.deps.getPrimaryModel;\n const m = gp?.();\n if (!m) {\n throw new Error('No primary model configured for delegate_task');\n }\n return m;\n },\n bus: this.deps.bus,\n getConfig: () => this.deps.getConfig?.(),\n getCurrentContext: () => this.deps.getCurrentContext?.() ?? null,\n hookRunner: this.deps.hookRunner,\n toolExecutorConfig: this.deps.toolExecutorConfig,\n }),\n ]\n : []),\n ...optionalTools,\n ];\n\n return filterToolsByDisabledSet(core, disabled);\n }\n\n createAllTools(coreOptions?: CreateCoreToolsOptions): AgentTool<any, any>[] {\n const coreTools = this.createCoreTools(coreOptions);\n const disableExtensions = coreOptions?.disabledTools?.has('extensions');\n const cfg = this.deps.getConfig?.();\n\n let bundled: AgentTool<any, any>[];\n if (!this.deps.extensionRegistry || disableExtensions) {\n bundled = coreTools;\n } else {\n const extensionTools = this.deps.extensionRegistry.getAllTools();\n log.info({ count: extensionTools.length }, 'Loaded extension tools');\n bundled = [...coreTools, ...extensionTools];\n }\n\n const wrapped = wrapToolsWithProtection(bundled, this.deps.toolExecutorConfig);\n\n const executeEnabled =\n cfg?.agents?.defaults?.executeCode?.enabled === true &&\n !coreOptions?.disabledTools?.has('execute_code');\n\n if (executeEnabled) {\n const sandboxMap = buildSandboxToolMap(wrapped);\n const executeTool = createExecuteCodeTool({ getSandboxToolMap: () => sandboxMap });\n const wrappedExecute = wrapToolsWithProtection([executeTool as any], this.deps.toolExecutorConfig);\n return [...wrapped, ...wrappedExecute];\n }\n\n return wrapped;\n }\n}\n\nfunction filterToolsByDisabledSet(\n tools: any[],\n disabled: Set<string> | undefined,\n): any[] {\n if (!disabled || disabled.size === 0) {\n return tools;\n }\n return tools.filter((t) => !disabled.has(t.name));\n}\n"],"mappings":"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"}
@@ -1,2 +1,7 @@
1
1
  export declare function formatEnvelopeTimestamp(timezone?: string, now?: Date): string;
2
2
  export declare function prependEnvelopeTimestamp(content: string, timezone?: string): string;
3
+ /**
4
+ * Remove a single leading envelope timestamp prefix from inbound text (session auto-title, etc.).
5
+ * Does not strip arbitrary `[…]` — only the date+time-shaped prefix from {@link formatEnvelopeTimestamp}.
6
+ */
7
+ export declare function stripEnvelopeTimestampPrefix(text: string): string;
@@ -30,7 +30,16 @@ function prependEnvelopeTimestamp(content, timezone) {
30
30
  if (!content.trim()) return content;
31
31
  return `[${formatEnvelopeTimestamp(timezone)}] ${content}`;
32
32
  }
33
+ /** Matches `[YYYY-MM-DD HH:MM]` plus optional ` TZ` inside brackets, as produced by {@link prependEnvelopeTimestamp}. */
34
+ const ENVELOPE_TIMESTAMP_PREFIX_RE = /^\[\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}(?:\s+[^\]]+)?\]\s*/;
35
+ /**
36
+ * Remove a single leading envelope timestamp prefix from inbound text (session auto-title, etc.).
37
+ * Does not strip arbitrary `[…]` — only the date+time-shaped prefix from {@link formatEnvelopeTimestamp}.
38
+ */
39
+ function stripEnvelopeTimestampPrefix(text) {
40
+ return text.replace(ENVELOPE_TIMESTAMP_PREFIX_RE, "");
41
+ }
33
42
  //#endregion
34
- export { formatEnvelopeTimestamp, prependEnvelopeTimestamp };
43
+ export { formatEnvelopeTimestamp, prependEnvelopeTimestamp, stripEnvelopeTimestampPrefix };
35
44
 
36
45
  //# sourceMappingURL=envelope-timestamp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"envelope-timestamp.js","names":[],"sources":["../../../src/channels/envelope-timestamp.ts"],"sourcesContent":["export function formatEnvelopeTimestamp(timezone?: string, now: Date = new Date()): string {\n try {\n const resolvedTimezone =\n timezone?.trim() || Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: resolvedTimezone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n timeZoneName: 'short',\n }).formatToParts(now);\n\n const map: Record<string, string> = {};\n for (const part of parts) {\n if (part.type !== 'literal') {\n map[part.type] = part.value;\n }\n }\n\n const year = map.year ?? '';\n const month = map.month ?? '';\n const day = map.day ?? '';\n const hour = map.hour ?? '';\n const minute = map.minute ?? '';\n const tzName = map.timeZoneName ?? '';\n\n if (!year || !month || !day || !hour || !minute) {\n return `${now.toISOString().slice(0, 16).replace('T', ' ')} UTC`;\n }\n\n return `${year}-${month}-${day} ${hour}:${minute}${tzName ? ` ${tzName}` : ''}`;\n } catch {\n return `${now.toISOString().slice(0, 16).replace('T', ' ')} UTC`;\n }\n}\n\nexport function prependEnvelopeTimestamp(content: string, timezone?: string): string {\n const text = content.trim();\n if (!text) {\n return content;\n }\n const timestamp = formatEnvelopeTimestamp(timezone);\n return `[${timestamp}] ${content}`;\n}\n\n"],"mappings":";AAAA,SAAgB,wBAAwB,UAAmB,sBAAY,IAAI,MAAM,EAAU;AACzF,KAAI;EACF,MAAM,mBACJ,UAAU,MAAM,IAAI,KAAK,gBAAgB,CAAC,iBAAiB,CAAC,YAAY;EAC1E,MAAM,QAAQ,IAAI,KAAK,eAAe,SAAS;GAC7C,UAAU;GACV,MAAM;GACN,OAAO;GACP,KAAK;GACL,MAAM;GACN,QAAQ;GACR,QAAQ;GACR,cAAc;GACf,CAAC,CAAC,cAAc,IAAI;EAErB,MAAM,MAA8B,EAAE;AACtC,OAAK,MAAM,QAAQ,MACjB,KAAI,KAAK,SAAS,UAChB,KAAI,KAAK,QAAQ,KAAK;EAI1B,MAAM,OAAO,IAAI,QAAQ;EACzB,MAAM,QAAQ,IAAI,SAAS;EAC3B,MAAM,MAAM,IAAI,OAAO;EACvB,MAAM,OAAO,IAAI,QAAQ;EACzB,MAAM,SAAS,IAAI,UAAU;EAC7B,MAAM,SAAS,IAAI,gBAAgB;AAEnC,MAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,OACvC,QAAO,GAAG,IAAI,aAAa,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC;AAG7D,SAAO,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,GAAG,SAAS,SAAS,IAAI,WAAW;SACrE;AACN,SAAO,GAAG,IAAI,aAAa,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC;;;AAI/D,SAAgB,yBAAyB,SAAiB,UAA2B;AAEnF,KAAI,CADS,QAAQ,MACZ,CACP,QAAO;AAGT,QAAO,IADW,wBAAwB,SACtB,CAAC,IAAI"}
1
+ {"version":3,"file":"envelope-timestamp.js","names":[],"sources":["../../../src/channels/envelope-timestamp.ts"],"sourcesContent":["export function formatEnvelopeTimestamp(timezone?: string, now: Date = new Date()): string {\n try {\n const resolvedTimezone =\n timezone?.trim() || Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: resolvedTimezone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n timeZoneName: 'short',\n }).formatToParts(now);\n\n const map: Record<string, string> = {};\n for (const part of parts) {\n if (part.type !== 'literal') {\n map[part.type] = part.value;\n }\n }\n\n const year = map.year ?? '';\n const month = map.month ?? '';\n const day = map.day ?? '';\n const hour = map.hour ?? '';\n const minute = map.minute ?? '';\n const tzName = map.timeZoneName ?? '';\n\n if (!year || !month || !day || !hour || !minute) {\n return `${now.toISOString().slice(0, 16).replace('T', ' ')} UTC`;\n }\n\n return `${year}-${month}-${day} ${hour}:${minute}${tzName ? ` ${tzName}` : ''}`;\n } catch {\n return `${now.toISOString().slice(0, 16).replace('T', ' ')} UTC`;\n }\n}\n\nexport function prependEnvelopeTimestamp(content: string, timezone?: string): string {\n const text = content.trim();\n if (!text) {\n return content;\n }\n const timestamp = formatEnvelopeTimestamp(timezone);\n return `[${timestamp}] ${content}`;\n}\n\n/** Matches `[YYYY-MM-DD HH:MM]` plus optional ` TZ` inside brackets, as produced by {@link prependEnvelopeTimestamp}. */\nconst ENVELOPE_TIMESTAMP_PREFIX_RE =\n /^\\[\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}(?:\\s+[^\\]]+)?\\]\\s*/;\n\n/**\n * Remove a single leading envelope timestamp prefix from inbound text (session auto-title, etc.).\n * Does not strip arbitrary `[…]` — only the date+time-shaped prefix from {@link formatEnvelopeTimestamp}.\n */\nexport function stripEnvelopeTimestampPrefix(text: string): string {\n return text.replace(ENVELOPE_TIMESTAMP_PREFIX_RE, '');\n}\n\n"],"mappings":";AAAA,SAAgB,wBAAwB,UAAmB,sBAAY,IAAI,MAAM,EAAU;AACzF,KAAI;EACF,MAAM,mBACJ,UAAU,MAAM,IAAI,KAAK,gBAAgB,CAAC,iBAAiB,CAAC,YAAY;EAC1E,MAAM,QAAQ,IAAI,KAAK,eAAe,SAAS;GAC7C,UAAU;GACV,MAAM;GACN,OAAO;GACP,KAAK;GACL,MAAM;GACN,QAAQ;GACR,QAAQ;GACR,cAAc;GACf,CAAC,CAAC,cAAc,IAAI;EAErB,MAAM,MAA8B,EAAE;AACtC,OAAK,MAAM,QAAQ,MACjB,KAAI,KAAK,SAAS,UAChB,KAAI,KAAK,QAAQ,KAAK;EAI1B,MAAM,OAAO,IAAI,QAAQ;EACzB,MAAM,QAAQ,IAAI,SAAS;EAC3B,MAAM,MAAM,IAAI,OAAO;EACvB,MAAM,OAAO,IAAI,QAAQ;EACzB,MAAM,SAAS,IAAI,UAAU;EAC7B,MAAM,SAAS,IAAI,gBAAgB;AAEnC,MAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,OACvC,QAAO,GAAG,IAAI,aAAa,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC;AAG7D,SAAO,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,GAAG,SAAS,SAAS,IAAI,WAAW;SACrE;AACN,SAAO,GAAG,IAAI,aAAa,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC;;;AAI/D,SAAgB,yBAAyB,SAAiB,UAA2B;AAEnF,KAAI,CADS,QAAQ,MACZ,CACP,QAAO;AAGT,QAAO,IADW,wBAAwB,SACtB,CAAC,IAAI;;;AAI3B,MAAM,+BACJ;;;;;AAMF,SAAgB,6BAA6B,MAAsB;AACjE,QAAO,KAAK,QAAQ,8BAA8B,GAAG"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Stable imports for the bundled Feishu channel (implementation under extensions/feishu).
3
+ */
4
+ export { feishuPlugin, FeishuChannelPlugin } from '../../../extensions/feishu/src/plugin.js';
5
+ export { defineChannelPluginEntry } from '../../../extensions/feishu/src/index.js';
@@ -0,0 +1,4 @@
1
+ import { defineChannelPluginEntry } from "../../extensions/sdk/channel-entry.js";
2
+ import { FeishuChannelPlugin, feishuPlugin } from "../../../extensions/feishu/src/plugin.js";
3
+ import "../../../extensions/feishu/src/index.js";
4
+ export { FeishuChannelPlugin, defineChannelPluginEntry, feishuPlugin };
@@ -8,7 +8,7 @@ import type { Config } from '../config/schema.js';
8
8
  import type { AgentMessage } from '@mariozechner/pi-agent-core';
9
9
  import type { ThinkLevel, ReasoningLevel, VerboseLevel } from '../agent/transcript/thinking-types.js';
10
10
  import type { SessionConfigStore } from '../session/index.js';
11
- export type MessageSource = 'telegram' | 'weixin' | 'webui' | 'cli' | 'api' | 'system' | 'gateway';
11
+ export type MessageSource = 'telegram' | 'weixin' | 'feishu' | 'webui' | 'cli' | 'api' | 'system' | 'gateway';
12
12
  export interface UnifiedMessage {
13
13
  /** Message source platform */
14
14
  source: MessageSource;
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import type { AgentMessage } from '@mariozechner/pi-agent-core';
8
8
  export type { AgentMessage };
9
- export type ExtensionHookEvent = 'before_agent_start' | 'agent_end' | 'before_compaction' | 'after_compaction' | 'message_received' | 'message_sending' | 'message_sent' | 'before_tool_call' | 'after_tool_call' | 'session_start' | 'session_end' | 'gateway_start' | 'gateway_stop' | 'context' | 'input' | 'turn_start' | 'turn_end' | 'tool_execution_start' | 'tool_execution_update' | 'tool_execution_end' | 'before_model_resolve' | 'before_prompt_build' | 'llm_input' | 'llm_output' | 'inbound_claim' | 'before_reset' | 'before_message_write';
9
+ export type ExtensionHookEvent = 'before_agent_start' | 'agent_end' | 'before_compaction' | 'after_compaction' | 'message_received' | 'message_sending' | 'message_sent' | 'before_tool_call' | 'after_tool_call' | 'session_start' | 'session_end' | 'gateway_start' | 'gateway_stop' | 'context' | 'input' | 'turn_start' | 'turn_end' | 'tool_execution_start' | 'tool_execution_update' | 'tool_execution_end' | 'before_model_resolve' | 'before_prompt_build' | 'llm_input' | 'llm_output' | 'inbound_claim' | 'before_reset' | 'before_message_write' | 'subagent_spawning' | 'subagent_delivery_target' | 'subagent_ended';
10
10
  /**
11
11
  * Hook execution mode determines how handlers are processed:
12
12
  * - void: Fire-and-forget, parallel execution (for notifications)
@@ -132,6 +132,48 @@ export interface HookToolExecutionEndEvent {
132
132
  error?: string;
133
133
  durationMs?: number;
134
134
  }
135
+ export interface HookSubagentSpawningEvent {
136
+ childSessionKey: string;
137
+ requester?: {
138
+ channel?: string;
139
+ accountId?: string;
140
+ to?: string;
141
+ threadId?: string | number;
142
+ };
143
+ threadRequested?: boolean;
144
+ agentId?: string;
145
+ label?: string;
146
+ }
147
+ export type HookSubagentSpawningResult = {
148
+ status: 'ok';
149
+ threadBindingReady?: boolean;
150
+ } | {
151
+ status: 'error';
152
+ error: string;
153
+ } | void;
154
+ export interface HookSubagentDeliveryTargetEvent {
155
+ childSessionKey: string;
156
+ requesterSessionKey?: string;
157
+ requesterOrigin?: {
158
+ channel?: string;
159
+ accountId?: string;
160
+ to?: string;
161
+ threadId?: string | number;
162
+ };
163
+ expectsCompletionMessage?: boolean;
164
+ }
165
+ export type HookSubagentDeliveryTargetResult = {
166
+ origin: {
167
+ channel: string;
168
+ accountId?: string;
169
+ to?: string;
170
+ threadId?: string | number;
171
+ };
172
+ } | void;
173
+ export interface HookSubagentEndedEvent {
174
+ targetSessionKey: string;
175
+ accountId?: string;
176
+ }
135
177
  /**
136
178
  * Strongly typed handler map - each hook has precise event/result types
137
179
  */
@@ -154,6 +196,9 @@ export type HookHandlerMap = {
154
196
  tool_execution_start: (event: HookToolExecutionStartEvent, ctx: HookAgentContext) => Promise<void> | void;
155
197
  tool_execution_update: (event: HookToolExecutionUpdateEvent, ctx: HookAgentContext) => Promise<void> | void;
156
198
  tool_execution_end: (event: HookToolExecutionEndEvent, ctx: HookAgentContext) => Promise<void> | void;
199
+ subagent_spawning: (event: HookSubagentSpawningEvent, ctx: HookAgentContext) => Promise<HookSubagentSpawningResult> | HookSubagentSpawningResult;
200
+ subagent_delivery_target: (event: HookSubagentDeliveryTargetEvent, ctx: HookAgentContext) => Promise<HookSubagentDeliveryTargetResult> | HookSubagentDeliveryTargetResult;
201
+ subagent_ended: (event: HookSubagentEndedEvent, ctx: HookAgentContext) => Promise<void> | void;
157
202
  session_start: (event: SessionStartContext, ctx: HookAgentContext) => Promise<void> | void;
158
203
  session_end: (event: SessionEndContext, ctx: HookAgentContext) => Promise<void> | void;
159
204
  gateway_start: (event: GatewayStartContext, ctx: HookAgentContext) => Promise<void> | void;
@@ -21,6 +21,7 @@ const HOOK_EXECUTION_MODES = {
21
21
  "tool_execution_update": "void",
22
22
  "tool_execution_end": "void",
23
23
  "before_reset": "void",
24
+ "subagent_ended": "void",
24
25
  "before_agent_start": "modifying",
25
26
  "before_model_resolve": "modifying",
26
27
  "before_prompt_build": "modifying",
@@ -29,6 +30,8 @@ const HOOK_EXECUTION_MODES = {
29
30
  "context": "modifying",
30
31
  "input": "modifying",
31
32
  "before_message_write": "modifying",
33
+ "subagent_spawning": "modifying",
34
+ "subagent_delivery_target": "modifying",
32
35
  "inbound_claim": "claiming"
33
36
  };
34
37
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","names":[],"sources":["../../../../src/extensions/types/hooks.ts"],"sourcesContent":["/**\n * Extension System - Hook Types\n * \n * Hook events, handlers, and context types.\n * Strongly typed hook system with execution modes.\n */\n\nimport type { AgentMessage } from '@mariozechner/pi-agent-core';\n\n// Re-export AgentMessage for use in hook system\nexport type { AgentMessage };\n\n// ============================================================================\n// Hook Event Types ( Strongly Typed)\n// ============================================================================\n\nexport type ExtensionHookEvent = \n // Existing hooks\n | 'before_agent_start'\n | 'agent_end'\n | 'before_compaction'\n | 'after_compaction'\n | 'message_received'\n | 'message_sending'\n | 'message_sent'\n | 'before_tool_call'\n | 'after_tool_call'\n | 'session_start'\n | 'session_end'\n | 'gateway_start'\n | 'gateway_stop'\n // Enhanced hooks\n | 'context'\n | 'input'\n | 'turn_start'\n | 'turn_end'\n // Tool execution lifecycle\n | 'tool_execution_start'\n | 'tool_execution_update'\n | 'tool_execution_end'\n // LLM Observation hooks\n | 'before_model_resolve'\n | 'before_prompt_build'\n | 'llm_input'\n | 'llm_output'\n // Inbound claim hook\n | 'inbound_claim'\n // Reset hook\n | 'before_reset'\n // Message write hook\n | 'before_message_write';\n\n// ============================================================================\n// Hook Execution Modes \n// ============================================================================\n\n/**\n * Hook execution mode determines how handlers are processed:\n * - void: Fire-and-forget, parallel execution (for notifications)\n * - modifying: Sequential execution, results merge (for modifications)\n * - claiming: First handler to return handled:true wins (for exclusive handling)\n */\nexport type HookExecutionMode = 'void' | 'modifying' | 'claiming';\n\n/**\n * Maps each hook event to its execution mode\n */\nexport const HOOK_EXECUTION_MODES: Record<ExtensionHookEvent, HookExecutionMode> = {\n // Void: Fire-and-forget, parallel execution\n 'message_received': 'void',\n 'message_sent': 'void',\n 'agent_end': 'void',\n 'llm_input': 'void',\n 'llm_output': 'void',\n 'after_tool_call': 'void',\n 'session_start': 'void',\n 'session_end': 'void',\n 'gateway_start': 'void',\n 'gateway_stop': 'void',\n 'before_compaction': 'void',\n 'after_compaction': 'void',\n 'turn_start': 'void',\n 'turn_end': 'void',\n 'tool_execution_start': 'void',\n 'tool_execution_update': 'void',\n 'tool_execution_end': 'void',\n 'before_reset': 'void',\n \n // Modifying: Sequential execution, results merge\n 'before_agent_start': 'modifying',\n 'before_model_resolve': 'modifying',\n 'before_prompt_build': 'modifying',\n 'message_sending': 'modifying',\n 'before_tool_call': 'modifying',\n 'context': 'modifying',\n 'input': 'modifying',\n 'before_message_write': 'modifying',\n \n // Claiming: First handler with handled:true wins\n 'inbound_claim': 'claiming',\n};\n\n// ============================================================================\n// Untyped hook handler (extensions may use before wiring strong types)\n// ============================================================================\n\nexport type ExtensionHookHandler = (event: unknown, context?: unknown) => unknown | Promise<unknown>;\n\nexport interface HookOptions {\n priority?: number;\n once?: boolean;\n}\n\n// ============================================================================\n// Strongly Typed Hook Handler Map\n// ============================================================================\n\n// Forward declarations for circular references\nexport interface HookAgentContext {\n timestamp?: Date;\n extensionId?: string;\n sessionKey?: string;\n agentId?: string;\n}\n\n// LLM Observation Hook Types\nexport interface HookBeforeModelResolveEvent {\n prompt: string;\n model?: string;\n provider?: string;\n}\n\nexport interface HookBeforeModelResolveResult {\n modelOverride?: string;\n providerOverride?: string;\n}\n\nexport interface HookBeforePromptBuildEvent {\n prompt: string;\n messages?: Array<{ role: string; content: string }>;\n}\n\nexport interface HookBeforePromptBuildResult {\n prompt?: string;\n prependContext?: string;\n}\n\nexport interface HookLlmInputEvent {\n runId: string;\n provider: string;\n model: string;\n prompt: string;\n systemPrompt?: string;\n messages?: Array<{ role: string; content: string }>;\n temperature?: number;\n maxTokens?: number;\n}\n\nexport interface HookLlmOutputEvent {\n runId: string;\n provider: string;\n model: string;\n content: string;\n usage?: {\n input?: number;\n output?: number;\n total?: number;\n };\n finishReason?: string;\n}\n\n// Inbound Claim Hook Types\nexport interface HookInboundClaimEvent {\n channelId: string;\n from: string;\n content: string;\n timestamp?: Date;\n}\n\nexport interface HookInboundClaimResult {\n handled: boolean;\n response?: string;\n}\n\n// Before Reset Hook Types\nexport interface HookBeforeResetEvent {\n sessionKey: string;\n reason?: 'user_request' | 'timeout' | 'error' | 'manual';\n}\n\nexport interface HookBeforeResetResult {\n allowReset: boolean;\n reason?: string;\n}\n\n// Before Message Write Hook Types\nexport interface HookBeforeMessageWriteEvent {\n channelId: string;\n to: string;\n content: string;\n}\n\nexport interface HookBeforeMessageWriteResult {\n content?: string;\n cancel?: boolean;\n reason?: string;\n}\n\n// Turn Hook Types\nexport interface HookTurnStartEvent {\n turnId: string;\n prompt?: string;\n agentId?: string;\n sessionKey?: string;\n}\n\nexport interface HookTurnEndEvent {\n turnId: string;\n response?: string;\n error?: string;\n durationMs?: number;\n}\n\n// Tool Execution Hook Types\nexport interface HookToolExecutionStartEvent {\n toolName: string;\n params: Record<string, unknown>;\n executionId: string;\n}\n\nexport interface HookToolExecutionUpdateEvent {\n toolName: string;\n executionId: string;\n progress?: number;\n message?: string;\n}\n\nexport interface HookToolExecutionEndEvent {\n toolName: string;\n executionId: string;\n result?: unknown;\n error?: string;\n durationMs?: number;\n}\n\n// ============================================================================\n// Strongly Typed Handler Map \n// ============================================================================\n\n/**\n * Strongly typed handler map - each hook has precise event/result types\n */\nexport type HookHandlerMap = {\n // Agent lifecycle\n before_agent_start: (\n event: BeforeAgentStartContext,\n ctx: HookAgentContext,\n ) => Promise<BeforeAgentStartResult | void> | BeforeAgentStartResult | void;\n \n before_model_resolve: (\n event: HookBeforeModelResolveEvent,\n ctx: HookAgentContext,\n ) => Promise<HookBeforeModelResolveResult | void> | HookBeforeModelResolveResult | void;\n \n before_prompt_build: (\n event: HookBeforePromptBuildEvent,\n ctx: HookAgentContext,\n ) => Promise<HookBeforePromptBuildResult | void> | HookBeforePromptBuildResult | void;\n \n llm_input: (\n event: HookLlmInputEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n llm_output: (\n event: HookLlmOutputEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n agent_end: (\n event: AgentEndContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n // Compaction\n before_compaction: (\n event: BeforeCompactionContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n after_compaction: (\n event: AfterCompactionContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n // Messages\n message_received: (\n event: MessageReceivedContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n message_sending: (\n event: MessageSendingContext,\n ctx: HookAgentContext,\n ) => Promise<MessageSendingResult | void> | MessageSendingResult | void;\n \n message_sent: (\n event: MessageSentContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n inbound_claim: (\n event: HookInboundClaimEvent,\n ctx: HookAgentContext,\n ) => Promise<HookInboundClaimResult | void> | HookInboundClaimResult | void;\n \n before_message_write: (\n event: HookBeforeMessageWriteEvent,\n ctx: HookAgentContext,\n ) => Promise<HookBeforeMessageWriteResult | void> | HookBeforeMessageWriteResult | void;\n \n // Tools\n before_tool_call: (\n event: BeforeToolCallContext,\n ctx: HookAgentContext,\n ) => Promise<BeforeToolCallResult | void> | BeforeToolCallResult | void;\n \n after_tool_call: (\n event: AfterToolCallContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n tool_execution_start: (\n event: HookToolExecutionStartEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n tool_execution_update: (\n event: HookToolExecutionUpdateEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n tool_execution_end: (\n event: HookToolExecutionEndEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n // Session\n session_start: (\n event: SessionStartContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n session_end: (\n event: SessionEndContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n // Gateway\n gateway_start: (\n event: GatewayStartContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n gateway_stop: (\n event: GatewayStopContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n // Enhanced (xopc-specific)\n context: (\n event: ContextEvent,\n ctx: HookAgentContext,\n ) => Promise<ContextResult | void> | ContextResult | void;\n \n input: (\n event: InputEvent,\n ctx: HookAgentContext,\n ) => Promise<InputResult | void> | InputResult | void;\n \n // Turn lifecycle\n turn_start: (\n event: HookTurnStartEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n turn_end: (\n event: HookTurnEndEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n // Reset\n before_reset: (\n event: HookBeforeResetEvent,\n ctx: HookAgentContext,\n ) => Promise<HookBeforeResetResult | void> | HookBeforeResetResult | void;\n};\n\n// ============================================================================\n// Shared hook context fields\n// ============================================================================\n\nexport interface HookContext {\n timestamp?: Date;\n extensionId?: string;\n sessionKey?: string;\n agentId?: string;\n}\n\nexport interface BeforeAgentStartContext extends HookContext {\n prompt: string;\n messages?: unknown[];\n}\n\nexport interface BeforeAgentStartResult {\n systemPrompt?: string;\n prependContext?: string;\n}\n\nexport interface AgentEndContext extends HookContext {\n messages: unknown[];\n success: boolean;\n error?: string;\n durationMs?: number;\n}\n\nexport interface BeforeCompactionContext extends HookContext {\n messageCount: number;\n tokenCount?: number;\n}\n\nexport interface AfterCompactionContext extends HookContext {\n messageCount: number;\n tokenCount?: number;\n compactedCount: number;\n}\n\nexport interface MessageReceivedContext extends HookContext {\n channelId: string;\n from: string;\n content: string;\n timestamp?: Date;\n metadata?: Record<string, unknown>;\n}\n\nexport interface MessageSendingContext extends HookContext {\n to: string;\n content: string;\n /** Channel id (e.g. telegram) when sending through ChannelManager. */\n channel?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface MessageSendingResult {\n content?: string;\n cancel?: boolean;\n cancelReason?: string;\n}\n\nexport interface MessageSentContext extends HookContext {\n to: string;\n content: string;\n success: boolean;\n error?: string;\n channel?: string;\n}\n\nexport interface BeforeToolCallContext extends HookContext {\n toolName: string;\n params: Record<string, unknown>;\n}\n\nexport interface BeforeToolCallResult {\n params?: Record<string, unknown>;\n block?: boolean;\n blockReason?: string;\n}\n\nexport interface AfterToolCallContext extends HookContext {\n toolName: string;\n params: Record<string, unknown>;\n result?: unknown;\n error?: string;\n durationMs?: number;\n}\n\nexport interface SessionStartContext extends HookContext {\n sessionId: string;\n resumedFrom?: string;\n}\n\nexport interface SessionEndContext extends HookContext {\n sessionId: string;\n reason?: 'completed' | 'error' | 'timeout' | 'user_request';\n}\n\nexport interface GatewayStartContext extends HookContext {\n port: number;\n host: string;\n}\n\nexport interface GatewayStopContext extends HookContext {\n port: number;\n reason?: string;\n}\n\n// ============================================================================\n// Enhanced Hook Event Types\n// ============================================================================\n\nexport interface ContextEvent {\n messages: Array<{ role: string; content: string }>;\n agentId?: string;\n sessionKey?: string;\n}\n\nexport interface ContextResult {\n messages: Array<{ role: string; content: string }>;\n}\n\nexport interface InputEvent {\n text: string;\n images?: string[];\n channelId?: string;\n from?: string;\n}\n\nexport interface InputResult {\n action: 'continue' | 'handled' | 'blocked';\n text?: string;\n images?: string[];\n response?: string;\n skipAgent?: boolean;\n}\n\nexport interface TurnEvent {\n turnId: string;\n prompt?: string;\n agentId?: string;\n sessionKey?: string;\n}\n\nexport interface TurnResult {\n context?: string;\n skipTurn?: boolean;\n}\n"],"mappings":";;;;AAmEA,MAAa,uBAAsE;CAEjF,oBAAoB;CACpB,gBAAgB;CAChB,aAAa;CACb,aAAa;CACb,cAAc;CACd,mBAAmB;CACnB,iBAAiB;CACjB,eAAe;CACf,iBAAiB;CACjB,gBAAgB;CAChB,qBAAqB;CACrB,oBAAoB;CACpB,cAAc;CACd,YAAY;CACZ,wBAAwB;CACxB,yBAAyB;CACzB,sBAAsB;CACtB,gBAAgB;CAGhB,sBAAsB;CACtB,wBAAwB;CACxB,uBAAuB;CACvB,mBAAmB;CACnB,oBAAoB;CACpB,WAAW;CACX,SAAS;CACT,wBAAwB;CAGxB,iBAAiB;CAClB"}
1
+ {"version":3,"file":"hooks.js","names":[],"sources":["../../../../src/extensions/types/hooks.ts"],"sourcesContent":["/**\n * Extension System - Hook Types\n * \n * Hook events, handlers, and context types.\n * Strongly typed hook system with execution modes.\n */\n\nimport type { AgentMessage } from '@mariozechner/pi-agent-core';\n\n// Re-export AgentMessage for use in hook system\nexport type { AgentMessage };\n\n// ============================================================================\n// Hook Event Types ( Strongly Typed)\n// ============================================================================\n\nexport type ExtensionHookEvent = \n // Existing hooks\n | 'before_agent_start'\n | 'agent_end'\n | 'before_compaction'\n | 'after_compaction'\n | 'message_received'\n | 'message_sending'\n | 'message_sent'\n | 'before_tool_call'\n | 'after_tool_call'\n | 'session_start'\n | 'session_end'\n | 'gateway_start'\n | 'gateway_stop'\n // Enhanced hooks\n | 'context'\n | 'input'\n | 'turn_start'\n | 'turn_end'\n // Tool execution lifecycle\n | 'tool_execution_start'\n | 'tool_execution_update'\n | 'tool_execution_end'\n // LLM Observation hooks\n | 'before_model_resolve'\n | 'before_prompt_build'\n | 'llm_input'\n | 'llm_output'\n // Inbound claim hook\n | 'inbound_claim'\n // Reset hook\n | 'before_reset'\n // Message write hook\n | 'before_message_write'\n // Sub-agent lifecycle (openclaw parity surface)\n | 'subagent_spawning'\n | 'subagent_delivery_target'\n | 'subagent_ended';\n\n// ============================================================================\n// Hook Execution Modes \n// ============================================================================\n\n/**\n * Hook execution mode determines how handlers are processed:\n * - void: Fire-and-forget, parallel execution (for notifications)\n * - modifying: Sequential execution, results merge (for modifications)\n * - claiming: First handler to return handled:true wins (for exclusive handling)\n */\nexport type HookExecutionMode = 'void' | 'modifying' | 'claiming';\n\n/**\n * Maps each hook event to its execution mode\n */\nexport const HOOK_EXECUTION_MODES: Record<ExtensionHookEvent, HookExecutionMode> = {\n // Void: Fire-and-forget, parallel execution\n 'message_received': 'void',\n 'message_sent': 'void',\n 'agent_end': 'void',\n 'llm_input': 'void',\n 'llm_output': 'void',\n 'after_tool_call': 'void',\n 'session_start': 'void',\n 'session_end': 'void',\n 'gateway_start': 'void',\n 'gateway_stop': 'void',\n 'before_compaction': 'void',\n 'after_compaction': 'void',\n 'turn_start': 'void',\n 'turn_end': 'void',\n 'tool_execution_start': 'void',\n 'tool_execution_update': 'void',\n 'tool_execution_end': 'void',\n 'before_reset': 'void',\n 'subagent_ended': 'void',\n \n // Modifying: Sequential execution, results merge\n 'before_agent_start': 'modifying',\n 'before_model_resolve': 'modifying',\n 'before_prompt_build': 'modifying',\n 'message_sending': 'modifying',\n 'before_tool_call': 'modifying',\n 'context': 'modifying',\n 'input': 'modifying',\n 'before_message_write': 'modifying',\n 'subagent_spawning': 'modifying',\n 'subagent_delivery_target': 'modifying',\n \n // Claiming: First handler with handled:true wins\n 'inbound_claim': 'claiming',\n};\n\n// ============================================================================\n// Untyped hook handler (extensions may use before wiring strong types)\n// ============================================================================\n\nexport type ExtensionHookHandler = (event: unknown, context?: unknown) => unknown | Promise<unknown>;\n\nexport interface HookOptions {\n priority?: number;\n once?: boolean;\n}\n\n// ============================================================================\n// Strongly Typed Hook Handler Map\n// ============================================================================\n\n// Forward declarations for circular references\nexport interface HookAgentContext {\n timestamp?: Date;\n extensionId?: string;\n sessionKey?: string;\n agentId?: string;\n}\n\n// LLM Observation Hook Types\nexport interface HookBeforeModelResolveEvent {\n prompt: string;\n model?: string;\n provider?: string;\n}\n\nexport interface HookBeforeModelResolveResult {\n modelOverride?: string;\n providerOverride?: string;\n}\n\nexport interface HookBeforePromptBuildEvent {\n prompt: string;\n messages?: Array<{ role: string; content: string }>;\n}\n\nexport interface HookBeforePromptBuildResult {\n prompt?: string;\n prependContext?: string;\n}\n\nexport interface HookLlmInputEvent {\n runId: string;\n provider: string;\n model: string;\n prompt: string;\n systemPrompt?: string;\n messages?: Array<{ role: string; content: string }>;\n temperature?: number;\n maxTokens?: number;\n}\n\nexport interface HookLlmOutputEvent {\n runId: string;\n provider: string;\n model: string;\n content: string;\n usage?: {\n input?: number;\n output?: number;\n total?: number;\n };\n finishReason?: string;\n}\n\n// Inbound Claim Hook Types\nexport interface HookInboundClaimEvent {\n channelId: string;\n from: string;\n content: string;\n timestamp?: Date;\n}\n\nexport interface HookInboundClaimResult {\n handled: boolean;\n response?: string;\n}\n\n// Before Reset Hook Types\nexport interface HookBeforeResetEvent {\n sessionKey: string;\n reason?: 'user_request' | 'timeout' | 'error' | 'manual';\n}\n\nexport interface HookBeforeResetResult {\n allowReset: boolean;\n reason?: string;\n}\n\n// Before Message Write Hook Types\nexport interface HookBeforeMessageWriteEvent {\n channelId: string;\n to: string;\n content: string;\n}\n\nexport interface HookBeforeMessageWriteResult {\n content?: string;\n cancel?: boolean;\n reason?: string;\n}\n\n// Turn Hook Types\nexport interface HookTurnStartEvent {\n turnId: string;\n prompt?: string;\n agentId?: string;\n sessionKey?: string;\n}\n\nexport interface HookTurnEndEvent {\n turnId: string;\n response?: string;\n error?: string;\n durationMs?: number;\n}\n\n// Tool Execution Hook Types\nexport interface HookToolExecutionStartEvent {\n toolName: string;\n params: Record<string, unknown>;\n executionId: string;\n}\n\nexport interface HookToolExecutionUpdateEvent {\n toolName: string;\n executionId: string;\n progress?: number;\n message?: string;\n}\n\nexport interface HookToolExecutionEndEvent {\n toolName: string;\n executionId: string;\n result?: unknown;\n error?: string;\n durationMs?: number;\n}\n\n// ============================================================================\n// Sub-agent hook types (delegate_task + future subagent sessions)\n// ============================================================================\n\nexport interface HookSubagentSpawningEvent {\n childSessionKey: string;\n requester?: {\n channel?: string;\n accountId?: string;\n to?: string;\n threadId?: string | number;\n };\n threadRequested?: boolean;\n agentId?: string;\n label?: string;\n}\n\nexport type HookSubagentSpawningResult =\n | { status: 'ok'; threadBindingReady?: boolean }\n | { status: 'error'; error: string }\n | void;\n\nexport interface HookSubagentDeliveryTargetEvent {\n childSessionKey: string;\n requesterSessionKey?: string;\n requesterOrigin?: {\n channel?: string;\n accountId?: string;\n to?: string;\n threadId?: string | number;\n };\n expectsCompletionMessage?: boolean;\n}\n\nexport type HookSubagentDeliveryTargetResult =\n | {\n origin: {\n channel: string;\n accountId?: string;\n to?: string;\n threadId?: string | number;\n };\n }\n | void;\n\nexport interface HookSubagentEndedEvent {\n targetSessionKey: string;\n accountId?: string;\n}\n\n// ============================================================================\n// Strongly Typed Handler Map \n// ============================================================================\n\n/**\n * Strongly typed handler map - each hook has precise event/result types\n */\nexport type HookHandlerMap = {\n // Agent lifecycle\n before_agent_start: (\n event: BeforeAgentStartContext,\n ctx: HookAgentContext,\n ) => Promise<BeforeAgentStartResult | void> | BeforeAgentStartResult | void;\n \n before_model_resolve: (\n event: HookBeforeModelResolveEvent,\n ctx: HookAgentContext,\n ) => Promise<HookBeforeModelResolveResult | void> | HookBeforeModelResolveResult | void;\n \n before_prompt_build: (\n event: HookBeforePromptBuildEvent,\n ctx: HookAgentContext,\n ) => Promise<HookBeforePromptBuildResult | void> | HookBeforePromptBuildResult | void;\n \n llm_input: (\n event: HookLlmInputEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n llm_output: (\n event: HookLlmOutputEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n agent_end: (\n event: AgentEndContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n // Compaction\n before_compaction: (\n event: BeforeCompactionContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n after_compaction: (\n event: AfterCompactionContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n // Messages\n message_received: (\n event: MessageReceivedContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n message_sending: (\n event: MessageSendingContext,\n ctx: HookAgentContext,\n ) => Promise<MessageSendingResult | void> | MessageSendingResult | void;\n \n message_sent: (\n event: MessageSentContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n inbound_claim: (\n event: HookInboundClaimEvent,\n ctx: HookAgentContext,\n ) => Promise<HookInboundClaimResult | void> | HookInboundClaimResult | void;\n \n before_message_write: (\n event: HookBeforeMessageWriteEvent,\n ctx: HookAgentContext,\n ) => Promise<HookBeforeMessageWriteResult | void> | HookBeforeMessageWriteResult | void;\n \n // Tools\n before_tool_call: (\n event: BeforeToolCallContext,\n ctx: HookAgentContext,\n ) => Promise<BeforeToolCallResult | void> | BeforeToolCallResult | void;\n \n after_tool_call: (\n event: AfterToolCallContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n tool_execution_start: (\n event: HookToolExecutionStartEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n tool_execution_update: (\n event: HookToolExecutionUpdateEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n tool_execution_end: (\n event: HookToolExecutionEndEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n\n subagent_spawning: (\n event: HookSubagentSpawningEvent,\n ctx: HookAgentContext,\n ) => Promise<HookSubagentSpawningResult> | HookSubagentSpawningResult;\n\n subagent_delivery_target: (\n event: HookSubagentDeliveryTargetEvent,\n ctx: HookAgentContext,\n ) => Promise<HookSubagentDeliveryTargetResult> | HookSubagentDeliveryTargetResult;\n\n subagent_ended: (\n event: HookSubagentEndedEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n // Session\n session_start: (\n event: SessionStartContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n session_end: (\n event: SessionEndContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n // Gateway\n gateway_start: (\n event: GatewayStartContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n gateway_stop: (\n event: GatewayStopContext,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n // Enhanced (xopc-specific)\n context: (\n event: ContextEvent,\n ctx: HookAgentContext,\n ) => Promise<ContextResult | void> | ContextResult | void;\n \n input: (\n event: InputEvent,\n ctx: HookAgentContext,\n ) => Promise<InputResult | void> | InputResult | void;\n \n // Turn lifecycle\n turn_start: (\n event: HookTurnStartEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n turn_end: (\n event: HookTurnEndEvent,\n ctx: HookAgentContext,\n ) => Promise<void> | void;\n \n // Reset\n before_reset: (\n event: HookBeforeResetEvent,\n ctx: HookAgentContext,\n ) => Promise<HookBeforeResetResult | void> | HookBeforeResetResult | void;\n};\n\n// ============================================================================\n// Shared hook context fields\n// ============================================================================\n\nexport interface HookContext {\n timestamp?: Date;\n extensionId?: string;\n sessionKey?: string;\n agentId?: string;\n}\n\nexport interface BeforeAgentStartContext extends HookContext {\n prompt: string;\n messages?: unknown[];\n}\n\nexport interface BeforeAgentStartResult {\n systemPrompt?: string;\n prependContext?: string;\n}\n\nexport interface AgentEndContext extends HookContext {\n messages: unknown[];\n success: boolean;\n error?: string;\n durationMs?: number;\n}\n\nexport interface BeforeCompactionContext extends HookContext {\n messageCount: number;\n tokenCount?: number;\n}\n\nexport interface AfterCompactionContext extends HookContext {\n messageCount: number;\n tokenCount?: number;\n compactedCount: number;\n}\n\nexport interface MessageReceivedContext extends HookContext {\n channelId: string;\n from: string;\n content: string;\n timestamp?: Date;\n metadata?: Record<string, unknown>;\n}\n\nexport interface MessageSendingContext extends HookContext {\n to: string;\n content: string;\n /** Channel id (e.g. telegram) when sending through ChannelManager. */\n channel?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface MessageSendingResult {\n content?: string;\n cancel?: boolean;\n cancelReason?: string;\n}\n\nexport interface MessageSentContext extends HookContext {\n to: string;\n content: string;\n success: boolean;\n error?: string;\n channel?: string;\n}\n\nexport interface BeforeToolCallContext extends HookContext {\n toolName: string;\n params: Record<string, unknown>;\n}\n\nexport interface BeforeToolCallResult {\n params?: Record<string, unknown>;\n block?: boolean;\n blockReason?: string;\n}\n\nexport interface AfterToolCallContext extends HookContext {\n toolName: string;\n params: Record<string, unknown>;\n result?: unknown;\n error?: string;\n durationMs?: number;\n}\n\nexport interface SessionStartContext extends HookContext {\n sessionId: string;\n resumedFrom?: string;\n}\n\nexport interface SessionEndContext extends HookContext {\n sessionId: string;\n reason?: 'completed' | 'error' | 'timeout' | 'user_request';\n}\n\nexport interface GatewayStartContext extends HookContext {\n port: number;\n host: string;\n}\n\nexport interface GatewayStopContext extends HookContext {\n port: number;\n reason?: string;\n}\n\n// ============================================================================\n// Enhanced Hook Event Types\n// ============================================================================\n\nexport interface ContextEvent {\n messages: Array<{ role: string; content: string }>;\n agentId?: string;\n sessionKey?: string;\n}\n\nexport interface ContextResult {\n messages: Array<{ role: string; content: string }>;\n}\n\nexport interface InputEvent {\n text: string;\n images?: string[];\n channelId?: string;\n from?: string;\n}\n\nexport interface InputResult {\n action: 'continue' | 'handled' | 'blocked';\n text?: string;\n images?: string[];\n response?: string;\n skipAgent?: boolean;\n}\n\nexport interface TurnEvent {\n turnId: string;\n prompt?: string;\n agentId?: string;\n sessionKey?: string;\n}\n\nexport interface TurnResult {\n context?: string;\n skipTurn?: boolean;\n}\n"],"mappings":";;;;AAuEA,MAAa,uBAAsE;CAEjF,oBAAoB;CACpB,gBAAgB;CAChB,aAAa;CACb,aAAa;CACb,cAAc;CACd,mBAAmB;CACnB,iBAAiB;CACjB,eAAe;CACf,iBAAiB;CACjB,gBAAgB;CAChB,qBAAqB;CACrB,oBAAoB;CACpB,cAAc;CACd,YAAY;CACZ,wBAAwB;CACxB,yBAAyB;CACzB,sBAAsB;CACtB,gBAAgB;CAChB,kBAAkB;CAGlB,sBAAsB;CACtB,wBAAwB;CACxB,uBAAuB;CACvB,mBAAmB;CACnB,oBAAoB;CACpB,WAAW;CACX,SAAS;CACT,wBAAwB;CACxB,qBAAqB;CACrB,4BAA4B;CAG5B,iBAAiB;CAClB"}
@@ -18,6 +18,7 @@ import { removeSkillsLockEntry } from "../agent/skills/hub-lock.js";
18
18
  import { deleteManagedSkill, installSkillFromZip, listManagedSkillDirs } from "../agent/skills/managed-store.js";
19
19
  import { MessageBus, MessageBusShutdownError } from "../infra/bus/queue.js";
20
20
  import "../infra/bus/index.js";
21
+ import { prependEnvelopeTimestamp } from "../channels/envelope-timestamp.js";
21
22
  import { SessionManager } from "../session/manager.js";
22
23
  import "../session/index.js";
23
24
  import { registerClarifyBridge } from "./clarify-runtime.js";
@@ -25,7 +26,6 @@ import { CHAT_CHANNEL_ORDER } from "../channels/registry.js";
25
26
  import { ExtensionLoader } from "../extensions/loader.js";
26
27
  import "../extensions/index.js";
27
28
  import { AgentService } from "../agent/service.js";
28
- import { prependEnvelopeTimestamp } from "../channels/envelope-timestamp.js";
29
29
  import { ChannelManager } from "../channels/manager.js";
30
30
  import { ConfigHotReloader } from "../config/reload.js";
31
31
  import "../config/index.js";
@@ -5,5 +5,6 @@
5
5
  import type { ChannelPlugin } from '../channels/plugin-types.js';
6
6
  import { telegramPlugin } from '../../extensions/telegram/src/index.js';
7
7
  import { weixinPlugin } from '../../extensions/weixin/src/index.js';
8
- export { telegramPlugin, weixinPlugin };
8
+ import { feishuPlugin } from '../../extensions/feishu/src/index.js';
9
+ export { telegramPlugin, weixinPlugin, feishuPlugin };
9
10
  export declare const bundledChannelPlugins: ChannelPlugin[];
@@ -2,9 +2,15 @@ import { telegramPlugin } from "../../extensions/telegram/src/plugin.js";
2
2
  import "../../extensions/telegram/src/index.js";
3
3
  import { weixinPlugin } from "../../extensions/weixin/src/plugin.js";
4
4
  import "../../extensions/weixin/src/index.js";
5
+ import { feishuPlugin } from "../../extensions/feishu/src/plugin.js";
6
+ import "../../extensions/feishu/src/index.js";
5
7
  //#region src/generated/bundled-channel-plugins.ts
6
- const bundledChannelPlugins = [telegramPlugin, weixinPlugin];
8
+ const bundledChannelPlugins = [
9
+ telegramPlugin,
10
+ weixinPlugin,
11
+ feishuPlugin
12
+ ];
7
13
  //#endregion
8
- export { bundledChannelPlugins, telegramPlugin, weixinPlugin };
14
+ export { bundledChannelPlugins, feishuPlugin, telegramPlugin, weixinPlugin };
9
15
 
10
16
  //# sourceMappingURL=bundled-channel-plugins.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bundled-channel-plugins.js","names":[],"sources":["../../../src/generated/bundled-channel-plugins.ts"],"sourcesContent":["/**\n * Built-in channel plugins: sources under extensions/*, compiled to dist/extensions/*.\n * Regenerate: pnpm run generate:bundled-channels\n */\n\nimport type { ChannelPlugin } from '../channels/plugin-types.js';\nimport { telegramPlugin } from '../../extensions/telegram/src/index.js';\nimport { weixinPlugin } from '../../extensions/weixin/src/index.js';\n\nexport { telegramPlugin, weixinPlugin };\nexport const bundledChannelPlugins: ChannelPlugin[] = [telegramPlugin, weixinPlugin];\n"],"mappings":";;;;;AAUA,MAAa,wBAAyC,CAAC,gBAAgB,aAAa"}
1
+ {"version":3,"file":"bundled-channel-plugins.js","names":[],"sources":["../../../src/generated/bundled-channel-plugins.ts"],"sourcesContent":["/**\n * Built-in channel plugins: sources under extensions/*, compiled to dist/extensions/*.\n * Regenerate: pnpm run generate:bundled-channels\n */\n\nimport type { ChannelPlugin } from '../channels/plugin-types.js';\nimport { telegramPlugin } from '../../extensions/telegram/src/index.js';\nimport { weixinPlugin } from '../../extensions/weixin/src/index.js';\nimport { feishuPlugin } from '../../extensions/feishu/src/index.js';\n\nexport { telegramPlugin, weixinPlugin, feishuPlugin };\nexport const bundledChannelPlugins: ChannelPlugin[] = [telegramPlugin, weixinPlugin, feishuPlugin];\n"],"mappings":";;;;;;;AAWA,MAAa,wBAAyC;CAAC;CAAgB;CAAc;CAAa"}
@@ -15,6 +15,7 @@ var init_env_keys = __esmMin((() => {
15
15
  PROVIDER_ENV_MAP = {
16
16
  openai: ["OPENAI_API_KEY"],
17
17
  anthropic: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"],
18
+ deepseek: ["DEEPSEEK_API_KEY"],
18
19
  google: ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
19
20
  "google-vertex": ["GOOGLE_CLOUD_PROJECT", "GOOGLE_CLOUD_LOCATION"],
20
21
  groq: ["GROQ_API_KEY"],
@@ -1 +1 @@
1
- {"version":3,"file":"env-keys.js","names":[],"sources":["../../../src/providers/env-keys.ts"],"sourcesContent":["/**\n * Provider → environment variable resolution (no credentials module dependency).\n * Keys align with pi-ai KnownProvider + @mariozechner/pi-ai getEnvApiKey where applicable.\n */\n\nexport const PROVIDER_ENV_MAP: Record<string, string[]> = {\n\topenai: ['OPENAI_API_KEY'],\n\tanthropic: ['ANTHROPIC_OAUTH_TOKEN', 'ANTHROPIC_API_KEY'],\n\tgoogle: ['GEMINI_API_KEY', 'GOOGLE_API_KEY'],\n\t'google-vertex': ['GOOGLE_CLOUD_PROJECT', 'GOOGLE_CLOUD_LOCATION'],\n\tgroq: ['GROQ_API_KEY'],\n\txai: ['XAI_API_KEY'],\n\tcerebras: ['CEREBRAS_API_KEY'],\n\tmistral: ['MISTRAL_API_KEY'],\n\topenrouter: ['OPENROUTER_API_KEY'],\n\t'azure-openai-responses': ['AZURE_OPENAI_API_KEY', 'AZURE_OPENAI_BASE_URL'],\n\t'amazon-bedrock': ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_REGION'],\n\tminimax: ['MINIMAX_API_KEY'],\n\t'minimax-cn': ['MINIMAX_CN_API_KEY', 'MINIMAX_API_KEY'],\n\t'kimi-coding': ['KIMI_API_KEY', 'MOONSHOT_API_KEY'],\n\thuggingface: ['HF_TOKEN', 'HUGGINGFACE_TOKEN'],\n\topencode: ['OPENCODE_API_KEY'],\n\t'opencode-go': ['OPENCODE_API_KEY'],\n\tzai: ['ZAI_API_KEY'],\n\t'google-gemini-cli': ['GEMINI_CLI_TOKEN', 'GOOGLE_TOKEN'],\n\t'google-antigravity': ['ANTIGRAVITY_API_KEY'],\n\t'vercel-ai-gateway': ['AI_GATEWAY_API_KEY', 'VERCEL_AI_GATEWAY_API_KEY'],\n\t'github-copilot': ['COPILOT_GITHUB_TOKEN', 'GH_TOKEN', 'GITHUB_TOKEN', 'GITHUB_COPILOT_TOKEN'],\n\t/** DashScope HTTP APIs (image gen, STT, TTS) — not an LLM KnownProvider. */\n\tdashscope: ['DASHSCOPE_API_KEY'],\n};\n\n/**\n * Get API key from environment variables for a provider\n */\nexport function getApiKeyFromEnv(provider: string): string | undefined {\n\tconst envVar = provider.toUpperCase().replace(/-/g, '_') + '_API_KEY';\n\tconst envKey = process.env[envVar];\n\tif (envKey) return envKey;\n\n\tconst keys = PROVIDER_ENV_MAP[provider] || [];\n\tfor (const key of keys) {\n\t\tif (process.env[key]) return process.env[key];\n\t}\n\n\treturn undefined;\n}\n"],"mappings":";;;;;AAmCA,SAAgB,iBAAiB,UAAsC;CACtE,MAAM,SAAS,SAAS,aAAa,CAAC,QAAQ,MAAM,IAAI,GAAG;CAC3D,MAAM,SAAS,QAAQ,IAAI;AAC3B,KAAI,OAAQ,QAAO;CAEnB,MAAM,OAAO,iBAAiB,aAAa,EAAE;AAC7C,MAAK,MAAM,OAAO,KACjB,KAAI,QAAQ,IAAI,KAAM,QAAO,QAAQ,IAAI;;;;AArC9B,oBAA6C;EACzD,QAAQ,CAAC,iBAAiB;EAC1B,WAAW,CAAC,yBAAyB,oBAAoB;EACzD,QAAQ,CAAC,kBAAkB,iBAAiB;EAC5C,iBAAiB,CAAC,wBAAwB,wBAAwB;EAClE,MAAM,CAAC,eAAe;EACtB,KAAK,CAAC,cAAc;EACpB,UAAU,CAAC,mBAAmB;EAC9B,SAAS,CAAC,kBAAkB;EAC5B,YAAY,CAAC,qBAAqB;EAClC,0BAA0B,CAAC,wBAAwB,wBAAwB;EAC3E,kBAAkB;GAAC;GAAqB;GAAyB;GAAa;EAC9E,SAAS,CAAC,kBAAkB;EAC5B,cAAc,CAAC,sBAAsB,kBAAkB;EACvD,eAAe,CAAC,gBAAgB,mBAAmB;EACnD,aAAa,CAAC,YAAY,oBAAoB;EAC9C,UAAU,CAAC,mBAAmB;EAC9B,eAAe,CAAC,mBAAmB;EACnC,KAAK,CAAC,cAAc;EACpB,qBAAqB,CAAC,oBAAoB,eAAe;EACzD,sBAAsB,CAAC,sBAAsB;EAC7C,qBAAqB,CAAC,sBAAsB,4BAA4B;EACxE,kBAAkB;GAAC;GAAwB;GAAY;GAAgB;GAAuB;;EAE9F,WAAW,CAAC,oBAAoB;EAChC"}
1
+ {"version":3,"file":"env-keys.js","names":[],"sources":["../../../src/providers/env-keys.ts"],"sourcesContent":["/**\n * Provider → environment variable resolution (no credentials module dependency).\n * Keys align with pi-ai KnownProvider + @mariozechner/pi-ai getEnvApiKey where applicable.\n */\n\nexport const PROVIDER_ENV_MAP: Record<string, string[]> = {\n\topenai: ['OPENAI_API_KEY'],\n\tanthropic: ['ANTHROPIC_OAUTH_TOKEN', 'ANTHROPIC_API_KEY'],\n\tdeepseek: ['DEEPSEEK_API_KEY'],\n\tgoogle: ['GEMINI_API_KEY', 'GOOGLE_API_KEY'],\n\t'google-vertex': ['GOOGLE_CLOUD_PROJECT', 'GOOGLE_CLOUD_LOCATION'],\n\tgroq: ['GROQ_API_KEY'],\n\txai: ['XAI_API_KEY'],\n\tcerebras: ['CEREBRAS_API_KEY'],\n\tmistral: ['MISTRAL_API_KEY'],\n\topenrouter: ['OPENROUTER_API_KEY'],\n\t'azure-openai-responses': ['AZURE_OPENAI_API_KEY', 'AZURE_OPENAI_BASE_URL'],\n\t'amazon-bedrock': ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_REGION'],\n\tminimax: ['MINIMAX_API_KEY'],\n\t'minimax-cn': ['MINIMAX_CN_API_KEY', 'MINIMAX_API_KEY'],\n\t'kimi-coding': ['KIMI_API_KEY', 'MOONSHOT_API_KEY'],\n\thuggingface: ['HF_TOKEN', 'HUGGINGFACE_TOKEN'],\n\topencode: ['OPENCODE_API_KEY'],\n\t'opencode-go': ['OPENCODE_API_KEY'],\n\tzai: ['ZAI_API_KEY'],\n\t'google-gemini-cli': ['GEMINI_CLI_TOKEN', 'GOOGLE_TOKEN'],\n\t'google-antigravity': ['ANTIGRAVITY_API_KEY'],\n\t'vercel-ai-gateway': ['AI_GATEWAY_API_KEY', 'VERCEL_AI_GATEWAY_API_KEY'],\n\t'github-copilot': ['COPILOT_GITHUB_TOKEN', 'GH_TOKEN', 'GITHUB_TOKEN', 'GITHUB_COPILOT_TOKEN'],\n\t/** DashScope HTTP APIs (image gen, STT, TTS) — not an LLM KnownProvider. */\n\tdashscope: ['DASHSCOPE_API_KEY'],\n};\n\n/**\n * Get API key from environment variables for a provider\n */\nexport function getApiKeyFromEnv(provider: string): string | undefined {\n\tconst envVar = provider.toUpperCase().replace(/-/g, '_') + '_API_KEY';\n\tconst envKey = process.env[envVar];\n\tif (envKey) return envKey;\n\n\tconst keys = PROVIDER_ENV_MAP[provider] || [];\n\tfor (const key of keys) {\n\t\tif (process.env[key]) return process.env[key];\n\t}\n\n\treturn undefined;\n}\n"],"mappings":";;;;;AAoCA,SAAgB,iBAAiB,UAAsC;CACtE,MAAM,SAAS,SAAS,aAAa,CAAC,QAAQ,MAAM,IAAI,GAAG;CAC3D,MAAM,SAAS,QAAQ,IAAI;AAC3B,KAAI,OAAQ,QAAO;CAEnB,MAAM,OAAO,iBAAiB,aAAa,EAAE;AAC7C,MAAK,MAAM,OAAO,KACjB,KAAI,QAAQ,IAAI,KAAM,QAAO,QAAQ,IAAI;;;;AAtC9B,oBAA6C;EACzD,QAAQ,CAAC,iBAAiB;EAC1B,WAAW,CAAC,yBAAyB,oBAAoB;EACzD,UAAU,CAAC,mBAAmB;EAC9B,QAAQ,CAAC,kBAAkB,iBAAiB;EAC5C,iBAAiB,CAAC,wBAAwB,wBAAwB;EAClE,MAAM,CAAC,eAAe;EACtB,KAAK,CAAC,cAAc;EACpB,UAAU,CAAC,mBAAmB;EAC9B,SAAS,CAAC,kBAAkB;EAC5B,YAAY,CAAC,qBAAqB;EAClC,0BAA0B,CAAC,wBAAwB,wBAAwB;EAC3E,kBAAkB;GAAC;GAAqB;GAAyB;GAAa;EAC9E,SAAS,CAAC,kBAAkB;EAC5B,cAAc,CAAC,sBAAsB,kBAAkB;EACvD,eAAe,CAAC,gBAAgB,mBAAmB;EACnD,aAAa,CAAC,YAAY,oBAAoB;EAC9C,UAAU,CAAC,mBAAmB;EAC9B,eAAe,CAAC,mBAAmB;EACnC,KAAK,CAAC,cAAc;EACpB,qBAAqB,CAAC,oBAAoB,eAAe;EACzD,sBAAsB,CAAC,sBAAsB;EAC7C,qBAAqB,CAAC,sBAAsB,4BAA4B;EACxE,kBAAkB;GAAC;GAAwB;GAAY;GAAgB;GAAuB;;EAE9F,WAAW,CAAC,oBAAoB;EAChC"}
@@ -270,6 +270,11 @@ var init_providers = __esmMin((() => {
270
270
  supportsApiKey: true,
271
271
  supportsOAuth: true
272
272
  },
273
+ "deepseek": {
274
+ name: "DeepSeek",
275
+ category: "common",
276
+ supportsApiKey: true
277
+ },
273
278
  "google": {
274
279
  name: "Google Gemini",
275
280
  category: "common",
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["getPiAiModel","getPiAiProviders","getPiAiModels"],"sources":["../../../src/providers/index.ts"],"sourcesContent":["/**\n * Model provider module - integrates built-in models with custom models from models.json\n */\n\nimport {\n\tgetModel as getPiAiModel,\n\tgetModels as getPiAiModels,\n\tgetProviders as getPiAiProviders,\n\ttype Model,\n\ttype Api,\n} from '@mariozechner/pi-ai';\nimport type { Config } from '../config/schema.js';\nimport { getModelRegistry } from './model-registry.js';\nimport { CredentialResolver, resolveApiKey, hasCredentials } from '../auth/credentials.js';\nimport { hasProviderAuthOnDiskSync } from '../auth/sync-provider-auth.js';\nimport { getApiKeyFromEnv } from './env-keys.js';\nimport { getProviderRegistry } from './plugin-registry.js';\nimport type { ProviderModelDefinition } from '../extensions/types/providers.js';\n\nexport { getApiKeyFromEnv, PROVIDER_ENV_MAP } from './env-keys.js';\n\n/** Sentinel base URL: model is served by an extension {@link ProviderPluginRegistry} provider. */\nexport const EXTENSION_PROVIDER_BASE_URL = 'extension://provider-plugin';\n\n/** Map a plugin registry model to the pi-ai {@link Model} shape. */\nexport function pluginModelToModel(providerId: string, definition: ProviderModelDefinition): Model<Api> {\n\treturn {\n\t\tprovider: providerId,\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\tapi: 'openai-completions' as Api,\n\t\tbaseUrl: EXTENSION_PROVIDER_BASE_URL,\n\t\treasoning: false,\n\t\tinput: definition.supportsImages ? (['text', 'image'] as ('text' | 'image')[]) : (['text'] as ('text' | 'image')[]),\n\t\tcontextWindow: definition.contextWindow ?? 128000,\n\t\tmaxTokens: definition.maxOutputTokens ?? 4096,\n\t\tcost: {\n\t\t\tinput: definition.pricing?.input ?? 0,\n\t\t\toutput: definition.pricing?.output ?? 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t},\n\t} as Model<Api>;\n}\n\n/**\n * Get API key synchronously: checks registry (models.json) first, then environment variables.\n * Use this for Agent's getApiKey callback which must be synchronous.\n */\nexport function getApiKeySync(provider: string): string | undefined {\n const pluginRegistry = getProviderRegistry();\n if (pluginRegistry.has(provider)) return 'extension-managed';\n\n const registry = getModelRegistry();\n const registryKey = registry.getApiKey(provider);\n if (registryKey) {\n return registryKey;\n }\n return getApiKeyFromEnv(provider);\n}\n\n/**\n * Resolve model reference. Supports:\n * - \"provider/modelId\" format\n * - \"modelId\" auto-detection via pi-ai or custom models\n * @throws if model not found\n */\nexport function resolveModel(ref: string): Model<Api> {\n\t// First try ModelRegistry (includes custom models)\n\tconst registry = getModelRegistry();\n\tconst customModel = registry.resolve(ref);\n\tif (customModel) {\n\t\treturn customModel;\n\t}\n\n\tif (ref.includes('/')) {\n\t\tconst [provider, modelId] = ref.split('/');\n\t\tconst piAiModel = getPiAiModel(provider as any, modelId as any);\n\t\tif (piAiModel) return piAiModel as Model<Api>;\n\n\t\tconst pluginRegistry = getProviderRegistry();\n\t\tconst plugin = pluginRegistry.get(provider);\n\t\tif (plugin) {\n\t\t\tconst pluginModel = plugin.models.find(m => m.id === modelId);\n\t\t\tif (pluginModel) return pluginModelToModel(provider, pluginModel);\n\t\t}\n\t\tthrow new Error(`Model not found: ${ref}`);\n\t}\n\n\tfor (const provider of getPiAiProviders()) {\n\t\ttry {\n\t\t\tconst models = getPiAiModels(provider);\n\t\t\tconst found = models.find(m => m.id === ref);\n\t\t\tif (found) return found as Model<Api>;\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\tconst pluginRegistry = getProviderRegistry();\n\tfor (const plugin of pluginRegistry.listAll()) {\n\t\tconst found = plugin.models.find(m => m.id === ref);\n\t\tif (found) return pluginModelToModel(plugin.id, found);\n\t}\n\n\tthrow new Error(`Model not found: ${ref}. Use format: provider/model-id`);\n}\n\nexport function getModelsByProvider(provider: string): readonly Model<Api>[] {\n\tconst registry = getModelRegistry();\n\tconst fromRegistry = registry.getAll().filter(m => m.provider === provider);\n\tconst plugin = getProviderRegistry().get(provider);\n\tif (!plugin) return fromRegistry;\n\tconst pluginModels = plugin.models.map(m => pluginModelToModel(provider, m));\n\treturn [...fromRegistry, ...pluginModels];\n}\n\nexport function getAllProviders(): string[] {\n\tconst registry = getModelRegistry();\n\tconst providers = new Set<string>();\n\n\t// Add built-in providers\n\tfor (const p of getPiAiProviders()) {\n\t\tproviders.add(p);\n\t}\n\n\t// Add custom providers from registry\n\tfor (const m of registry.getAll()) {\n\t\tproviders.add(m.provider);\n\t}\n\n\tfor (const plugin of getProviderRegistry().listAll()) {\n\t\tproviders.add(plugin.id);\n\t}\n\n\treturn Array.from(providers);\n}\n\nexport async function getApiKey(provider: string): Promise<string | undefined> {\n\tif (getProviderRegistry().has(provider)) return 'extension-managed';\n\n\t// Use new credential resolver first (checks: agent private > global > oauth > env)\n\tconst credentialKey = await resolveApiKey(provider);\n\tif (credentialKey) {\n\t\treturn credentialKey;\n\t}\n\n\t// Check registry for custom providers (from models.json)\n\tconst registry = getModelRegistry();\n\tconst registryKey = registry.getApiKey(provider);\n\tif (registryKey) {\n\t\treturn registryKey;\n\t}\n\n\t// Fallback to environment variables\n\treturn getApiKeyFromEnv(provider);\n}\n\n/**\n * Synchronous version for use in non-async contexts\n * Only checks environment variables and registry, not credential system\n */\nexport function isProviderConfiguredSync(provider: string): boolean {\n\tif (getProviderRegistry().has(provider)) return true;\n\n\t// Check registry for custom providers\n\tconst registry = getModelRegistry();\n\tif (registry.getApiKey(provider)) {\n\t\treturn true;\n\t}\n\t// Check environment variables\n\tif (getApiKeyFromEnv(provider)) {\n\t\treturn true;\n\t}\n\t// Gateway UI / CLI store keys in auth-profiles.json (async CredentialResolver); sync path for fallback list\n\treturn hasProviderAuthOnDiskSync(provider);\n}\n\nexport async function isProviderConfigured(provider: string): Promise<boolean> {\n if (getProviderRegistry().has(provider)) return true;\n\n // Check registry first for custom providers (from models.json)\n const registry = getModelRegistry();\n if (registry.getApiKey(provider)) {\n return true;\n }\n return await hasCredentials(provider);\n}\n\n/** Where runtime {@link getApiKey} resolves the key from (no secret values). */\nexport type ProviderActiveKeySource = 'none' | 'agent' | 'gateway' | 'oauth' | 'env' | 'models_json' | 'extension';\n\nexport async function getProviderActiveKeySource(provider: string): Promise<ProviderActiveKeySource> {\n if (getProviderRegistry().has(provider)) return 'extension';\n\n const resolver = new CredentialResolver();\n const fromCredentials = await resolver.resolveApiKeySource(provider);\n if (fromCredentials === 'agent') return 'agent';\n if (fromCredentials === 'global') return 'gateway';\n if (fromCredentials === 'oauth') return 'oauth';\n if (fromCredentials === 'env') return 'env';\n\n const registry = getModelRegistry();\n if (registry.getApiKey(provider)) {\n return 'models_json';\n }\n\n return 'none';\n}\n\nexport async function getConfiguredProviders(): Promise<string[]> {\n\tconst allProviders = getAllProviders();\n\tconst configured: string[] = [];\n\tfor (const p of allProviders) {\n\t\tif (await isProviderConfigured(p)) {\n\t\t\tconfigured.push(p);\n\t\t}\n\t}\n\treturn configured;\n}\n\nexport function getAllModels(): readonly Model<Api>[] {\n\tconst registry = getModelRegistry();\n\tconst registryModels = registry.getAll();\n\tconst pluginProviders = getProviderRegistry().listAll();\n\tif (pluginProviders.length === 0) return registryModels;\n\n\tconst existingIds = new Set(registryModels.map(m => `${m.provider}/${m.id}`));\n\tconst merged: Model<Api>[] = [...registryModels];\n\tfor (const plugin of pluginProviders) {\n\t\tfor (const model of plugin.models) {\n\t\t\tconst compositeId = `${plugin.id}/${model.id}`;\n\t\t\tif (!existingIds.has(compositeId)) {\n\t\t\t\tmerged.push(pluginModelToModel(plugin.id, model));\n\t\t\t\texistingIds.add(compositeId);\n\t\t\t}\n\t\t}\n\t}\n\treturn merged;\n}\n\nexport async function getAvailableModels(): Promise<readonly Model<Api>[]> {\n\tconst allModels = getAllModels();\n\tconst pluginRegistry = getProviderRegistry();\n\tconst available: Model<Api>[] = [];\n\n\tfor (const model of allModels) {\n\t\tif (pluginRegistry.has(model.provider)) {\n\t\t\tavailable.push(model);\n\t\t} else if (await isProviderConfigured(model.provider)) {\n\t\t\tavailable.push(model);\n\t\t}\n\t}\n\treturn available;\n}\n\nexport type { Model, Api } from '@mariozechner/pi-ai';\n\nexport type ProviderCategory = 'common' | 'specialty' | 'oauth' | 'enterprise' | 'extension';\n\nexport interface ProviderMeta {\n name: string;\n category: ProviderCategory;\n supportsOAuth?: boolean;\n supportsApiKey?: boolean;\n}\n\nexport const PROVIDER_META: Record<string, ProviderMeta> = {\n 'openai': { name: 'OpenAI (GPT-4, o1, o3)', category: 'common', supportsApiKey: true },\n 'anthropic': { name: 'Anthropic Claude', category: 'common', supportsApiKey: true, supportsOAuth: true },\n 'google': { name: 'Google Gemini', category: 'common', supportsApiKey: true },\n 'groq': { name: 'Groq (Fast Inference)', category: 'common', supportsApiKey: true },\n 'minimax': { name: 'MiniMax', category: 'common', supportsApiKey: true, supportsOAuth: true },\n 'minimax-cn': { name: 'MiniMax CN', category: 'common', supportsApiKey: true, supportsOAuth: true },\n 'kimi-coding': { name: 'Kimi For Coding', category: 'common', supportsApiKey: true, supportsOAuth: true },\n 'xai': { name: 'xAI (Grok)', category: 'specialty', supportsApiKey: true },\n 'mistral': { name: 'Mistral AI', category: 'specialty', supportsApiKey: true },\n 'cerebras': { name: 'Cerebras', category: 'specialty', supportsApiKey: true },\n 'openrouter': { name: 'OpenRouter (Multi-provider)', category: 'specialty', supportsApiKey: true },\n 'huggingface': { name: 'Hugging Face', category: 'specialty', supportsApiKey: true },\n 'opencode': { name: 'OpenCode', category: 'specialty', supportsApiKey: true },\n 'opencode-go': { name: 'OpenCode Go', category: 'specialty', supportsApiKey: true },\n /** DashScope (Alibaba) — image, speech, STT; not an LLM KnownProvider. */\n 'dashscope': { name: 'DashScope (Alibaba)', category: 'specialty', supportsApiKey: true },\n /** International GLM (api.z.ai). Auth: API key (ZAI_API_KEY); no published OAuth for this HTTP API. */\n 'zai': { name: 'Zhipu GLM (International · z.ai)', category: 'common', supportsApiKey: true },\n 'amazon-bedrock': { name: 'Amazon Bedrock', category: 'enterprise', supportsApiKey: true },\n 'azure-openai-responses': { name: 'Azure OpenAI', category: 'enterprise', supportsApiKey: true },\n 'google-vertex': { name: 'Google Vertex AI', category: 'enterprise', supportsApiKey: true },\n 'vercel-ai-gateway': { name: 'Vercel AI Gateway', category: 'enterprise', supportsApiKey: true },\n 'github-copilot': { name: 'GitHub Copilot (OAuth)', category: 'oauth', supportsOAuth: true },\n 'openai-codex': { name: 'OpenAI Codex (OAuth)', category: 'oauth', supportsOAuth: true },\n 'google-gemini-cli': { name: 'Google Gemini CLI (OAuth)', category: 'oauth', supportsOAuth: true },\n 'google-antigravity': { name: 'Google Antigravity (OAuth)', category: 'oauth', supportsOAuth: true },\n};\n\nexport function getSortedProviders(): string[] {\n const all = getAllProviders();\n const catOrder: Record<ProviderCategory, number> = { common: 0, specialty: 1, enterprise: 2, oauth: 3, extension: 4 };\n const pluginRegistry = getProviderRegistry();\n\n return [...all].sort((a, b) => {\n const catA = pluginRegistry.has(a) ? 'extension' : (PROVIDER_META[a]?.category ?? 'specialty');\n const catB = pluginRegistry.has(b) ? 'extension' : (PROVIDER_META[b]?.category ?? 'specialty');\n if (catOrder[catA] !== catOrder[catB]) {\n return catOrder[catA] - catOrder[catB];\n }\n return a.localeCompare(b);\n });\n}\n\nexport function getProviderDisplayName(provider: string): string {\n const plugin = getProviderRegistry().get(provider);\n if (plugin) return plugin.name;\n return PROVIDER_META[provider]?.name || provider;\n}\n\nexport function providerSupportsOAuth(provider: string): boolean {\n return PROVIDER_META[provider]?.supportsOAuth ?? false;\n}\n\nexport function providerSupportsApiKey(provider: string): boolean {\n return PROVIDER_META[provider]?.supportsApiKey ?? true;\n}\n\n// ============================================\n// Dynamic Default Model Resolution\n// ============================================\n\n/**\n * Get a default model reference.\n * Priority:\n * 1. First available model with configured API key\n * 2. First model from pi-ai catalog\n * 3. Fallback to anthropic/claude-sonnet-4-5 as last resort\n */\nexport async function getDefaultModel(config?: Config | null | undefined): Promise<string> {\n const availableModels = await getAvailableModels();\n \n // Try to find configured default model first\n const defaultModel = config?.agents?.defaults?.model;\n if (defaultModel) {\n const modelRef = typeof defaultModel === 'string' ? defaultModel : defaultModel.primary;\n if (modelRef) {\n // Check if the configured model has valid API key\n const configured = availableModels.find(m => \n `${m.provider}/${m.id}` === modelRef ||\n m.id === modelRef\n );\n if (configured) {\n return `${configured.provider}/${configured.id}`;\n }\n }\n }\n \n // Return first available model\n if (availableModels.length > 0) {\n return `${availableModels[0].provider}/${availableModels[0].id}`;\n }\n \n // Try to get first model from pi-ai catalog\n for (const provider of getPiAiProviders()) {\n try {\n const models = getPiAiModels(provider);\n if (models.length > 0) {\n return `${provider}/${models[0].id}`;\n }\n } catch {\n continue;\n }\n }\n \n // Last resort fallback\n return 'anthropic/claude-sonnet-4-5';\n}\n\n/**\n * Synchronous default model resolution for constructors and sync code paths.\n * Uses catalog/registry only (no async credential checks).\n */\nexport function getDefaultModelSync(config?: Config | null | undefined): string {\n const defaultModel = config?.agents?.defaults?.model;\n if (defaultModel) {\n const modelRef = typeof defaultModel === 'string' ? defaultModel : defaultModel.primary;\n if (modelRef) {\n return modelRef;\n }\n }\n const all = getAllModels();\n if (all.length > 0) {\n return `${all[0].provider}/${all[0].id}`;\n }\n for (const provider of getPiAiProviders()) {\n try {\n const models = getPiAiModels(provider);\n if (models.length > 0) {\n return `${provider}/${models[0].id}`;\n }\n } catch {\n continue;\n }\n }\n return 'anthropic/claude-sonnet-4-5';\n}\n\n// Re-export ModelRegistry for advanced use cases\nexport { ModelRegistry, getModelRegistry, resetModelRegistry } from './model-registry.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,mBAAmB,YAAoB,YAAiD;AACvG,QAAO;EACN,UAAU;EACV,IAAI,WAAW;EACf,MAAM,WAAW;EACjB,KAAK;EACL,SAAS;EACT,WAAW;EACX,OAAO,WAAW,iBAAkB,CAAC,QAAQ,QAAQ,GAA6B,CAAC,OAAO;EAC1F,eAAe,WAAW,iBAAiB;EAC3C,WAAW,WAAW,mBAAmB;EACzC,MAAM;GACL,OAAO,WAAW,SAAS,SAAS;GACpC,QAAQ,WAAW,SAAS,UAAU;GACtC,WAAW;GACX,YAAY;GACZ;EACD;;;;;;AAOF,SAAgB,cAAc,UAAsC;AAElE,KADuB,qBACL,CAAC,IAAI,SAAS,CAAE,QAAO;CAGzC,MAAM,cADW,kBACW,CAAC,UAAU,SAAS;AAChD,KAAI,YACF,QAAO;AAET,QAAO,iBAAiB,SAAS;;;;;;;;AASnC,SAAgB,aAAa,KAAyB;CAGrD,MAAM,cADW,kBACW,CAAC,QAAQ,IAAI;AACzC,KAAI,YACH,QAAO;AAGR,KAAI,IAAI,SAAS,IAAI,EAAE;EACtB,MAAM,CAAC,UAAU,WAAW,IAAI,MAAM,IAAI;EAC1C,MAAM,YAAYA,SAAa,UAAiB,QAAe;AAC/D,MAAI,UAAW,QAAO;EAGtB,MAAM,SADiB,qBACM,CAAC,IAAI,SAAS;AAC3C,MAAI,QAAQ;GACX,MAAM,cAAc,OAAO,OAAO,MAAK,MAAK,EAAE,OAAO,QAAQ;AAC7D,OAAI,YAAa,QAAO,mBAAmB,UAAU,YAAY;;AAElE,QAAM,IAAI,MAAM,oBAAoB,MAAM;;AAG3C,MAAK,MAAM,YAAYC,cAAkB,CACxC,KAAI;EAEH,MAAM,QADSC,UAAc,SACT,CAAC,MAAK,MAAK,EAAE,OAAO,IAAI;AAC5C,MAAI,MAAO,QAAO;SACX;AACP;;CAIF,MAAM,iBAAiB,qBAAqB;AAC5C,MAAK,MAAM,UAAU,eAAe,SAAS,EAAE;EAC9C,MAAM,QAAQ,OAAO,OAAO,MAAK,MAAK,EAAE,OAAO,IAAI;AACnD,MAAI,MAAO,QAAO,mBAAmB,OAAO,IAAI,MAAM;;AAGvD,OAAM,IAAI,MAAM,oBAAoB,IAAI,iCAAiC;;AAG1E,SAAgB,oBAAoB,UAAyC;CAE5E,MAAM,eADW,kBACY,CAAC,QAAQ,CAAC,QAAO,MAAK,EAAE,aAAa,SAAS;CAC3E,MAAM,SAAS,qBAAqB,CAAC,IAAI,SAAS;AAClD,KAAI,CAAC,OAAQ,QAAO;CACpB,MAAM,eAAe,OAAO,OAAO,KAAI,MAAK,mBAAmB,UAAU,EAAE,CAAC;AAC5E,QAAO,CAAC,GAAG,cAAc,GAAG,aAAa;;AAG1C,SAAgB,kBAA4B;CAC3C,MAAM,WAAW,kBAAkB;CACnC,MAAM,4BAAY,IAAI,KAAa;AAGnC,MAAK,MAAM,KAAKD,cAAkB,CACjC,WAAU,IAAI,EAAE;AAIjB,MAAK,MAAM,KAAK,SAAS,QAAQ,CAChC,WAAU,IAAI,EAAE,SAAS;AAG1B,MAAK,MAAM,UAAU,qBAAqB,CAAC,SAAS,CACnD,WAAU,IAAI,OAAO,GAAG;AAGzB,QAAO,MAAM,KAAK,UAAU;;AAG7B,eAAsB,UAAU,UAA+C;AAC9E,KAAI,qBAAqB,CAAC,IAAI,SAAS,CAAE,QAAO;CAGhD,MAAM,gBAAgB,MAAM,cAAc,SAAS;AACnD,KAAI,cACH,QAAO;CAKR,MAAM,cADW,kBACW,CAAC,UAAU,SAAS;AAChD,KAAI,YACH,QAAO;AAIR,QAAO,iBAAiB,SAAS;;;;;;AAOlC,SAAgB,yBAAyB,UAA2B;AACnE,KAAI,qBAAqB,CAAC,IAAI,SAAS,CAAE,QAAO;AAIhD,KADiB,kBACL,CAAC,UAAU,SAAS,CAC/B,QAAO;AAGR,KAAI,iBAAiB,SAAS,CAC7B,QAAO;AAGR,QAAO,0BAA0B,SAAS;;AAG3C,eAAsB,qBAAqB,UAAoC;AAC7E,KAAI,qBAAqB,CAAC,IAAI,SAAS,CAAE,QAAO;AAIhD,KADiB,kBACL,CAAC,UAAU,SAAS,CAC9B,QAAO;AAET,QAAO,MAAM,eAAe,SAAS;;AAMvC,eAAsB,2BAA2B,UAAoD;AACnG,KAAI,qBAAqB,CAAC,IAAI,SAAS,CAAE,QAAO;CAGhD,MAAM,kBAAkB,MAAM,IADT,oBACiB,CAAC,oBAAoB,SAAS;AACpE,KAAI,oBAAoB,QAAS,QAAO;AACxC,KAAI,oBAAoB,SAAU,QAAO;AACzC,KAAI,oBAAoB,QAAS,QAAO;AACxC,KAAI,oBAAoB,MAAO,QAAO;AAGtC,KADiB,kBACL,CAAC,UAAU,SAAS,CAC9B,QAAO;AAGT,QAAO;;AAGT,eAAsB,yBAA4C;CACjE,MAAM,eAAe,iBAAiB;CACtC,MAAM,aAAuB,EAAE;AAC/B,MAAK,MAAM,KAAK,aACf,KAAI,MAAM,qBAAqB,EAAE,CAChC,YAAW,KAAK,EAAE;AAGpB,QAAO;;AAGR,SAAgB,eAAsC;CAErD,MAAM,iBADW,kBACc,CAAC,QAAQ;CACxC,MAAM,kBAAkB,qBAAqB,CAAC,SAAS;AACvD,KAAI,gBAAgB,WAAW,EAAG,QAAO;CAEzC,MAAM,cAAc,IAAI,IAAI,eAAe,KAAI,MAAK,GAAG,EAAE,SAAS,GAAG,EAAE,KAAK,CAAC;CAC7E,MAAM,SAAuB,CAAC,GAAG,eAAe;AAChD,MAAK,MAAM,UAAU,gBACpB,MAAK,MAAM,SAAS,OAAO,QAAQ;EAClC,MAAM,cAAc,GAAG,OAAO,GAAG,GAAG,MAAM;AAC1C,MAAI,CAAC,YAAY,IAAI,YAAY,EAAE;AAClC,UAAO,KAAK,mBAAmB,OAAO,IAAI,MAAM,CAAC;AACjD,eAAY,IAAI,YAAY;;;AAI/B,QAAO;;AAGR,eAAsB,qBAAqD;CAC1E,MAAM,YAAY,cAAc;CAChC,MAAM,iBAAiB,qBAAqB;CAC5C,MAAM,YAA0B,EAAE;AAElC,MAAK,MAAM,SAAS,UACnB,KAAI,eAAe,IAAI,MAAM,SAAS,CACrC,WAAU,KAAK,MAAM;UACX,MAAM,qBAAqB,MAAM,SAAS,CACpD,WAAU,KAAK,MAAM;AAGvB,QAAO;;AA2CR,SAAgB,qBAA+B;CAC7C,MAAM,MAAM,iBAAiB;CAC7B,MAAM,WAA6C;EAAE,QAAQ;EAAG,WAAW;EAAG,YAAY;EAAG,OAAO;EAAG,WAAW;EAAG;CACrH,MAAM,iBAAiB,qBAAqB;AAE5C,QAAO,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,MAAM;EAC7B,MAAM,OAAO,eAAe,IAAI,EAAE,GAAG,cAAe,cAAc,IAAI,YAAY;EAClF,MAAM,OAAO,eAAe,IAAI,EAAE,GAAG,cAAe,cAAc,IAAI,YAAY;AAClF,MAAI,SAAS,UAAU,SAAS,MAC9B,QAAO,SAAS,QAAQ,SAAS;AAEnC,SAAO,EAAE,cAAc,EAAE;GACzB;;AAGJ,SAAgB,uBAAuB,UAA0B;CAC/D,MAAM,SAAS,qBAAqB,CAAC,IAAI,SAAS;AAClD,KAAI,OAAQ,QAAO,OAAO;AAC1B,QAAO,cAAc,WAAW,QAAQ;;AAG1C,SAAgB,sBAAsB,UAA2B;AAC/D,QAAO,cAAc,WAAW,iBAAiB;;AAGnD,SAAgB,uBAAuB,UAA2B;AAChE,QAAO,cAAc,WAAW,kBAAkB;;;;;;;;;AAcpD,eAAsB,gBAAgB,QAAqD;CACzF,MAAM,kBAAkB,MAAM,oBAAoB;CAGlD,MAAM,eAAe,QAAQ,QAAQ,UAAU;AAC/C,KAAI,cAAc;EAChB,MAAM,WAAW,OAAO,iBAAiB,WAAW,eAAe,aAAa;AAChF,MAAI,UAAU;GAEZ,MAAM,aAAa,gBAAgB,MAAK,MACtC,GAAG,EAAE,SAAS,GAAG,EAAE,SAAS,YAC5B,EAAE,OAAO,SACV;AACD,OAAI,WACF,QAAO,GAAG,WAAW,SAAS,GAAG,WAAW;;;AAMlD,KAAI,gBAAgB,SAAS,EAC3B,QAAO,GAAG,gBAAgB,GAAG,SAAS,GAAG,gBAAgB,GAAG;AAI9D,MAAK,MAAM,YAAYA,cAAkB,CACvC,KAAI;EACF,MAAM,SAASC,UAAc,SAAS;AACtC,MAAI,OAAO,SAAS,EAClB,QAAO,GAAG,SAAS,GAAG,OAAO,GAAG;SAE5B;AACN;;AAKJ,QAAO;;;;;;AAOT,SAAgB,oBAAoB,QAA4C;CAC9E,MAAM,eAAe,QAAQ,QAAQ,UAAU;AAC/C,KAAI,cAAc;EAChB,MAAM,WAAW,OAAO,iBAAiB,WAAW,eAAe,aAAa;AAChF,MAAI,SACF,QAAO;;CAGX,MAAM,MAAM,cAAc;AAC1B,KAAI,IAAI,SAAS,EACf,QAAO,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,GAAG;AAEtC,MAAK,MAAM,YAAYD,cAAkB,CACvC,KAAI;EACF,MAAM,SAASC,UAAc,SAAS;AACtC,MAAI,OAAO,SAAS,EAClB,QAAO,GAAG,SAAS,GAAG,OAAO,GAAG;SAE5B;AACN;;AAGJ,QAAO;;;;sBAtY8C;mBACoC;0BACjB;gBACzB;uBACU;AAM9C,+BAA8B;AAqP9B,iBAA8C;EACzD,UAAU;GAAE,MAAM;GAA0B,UAAU;GAAU,gBAAgB;GAAM;EACtF,aAAa;GAAE,MAAM;GAAoB,UAAU;GAAU,gBAAgB;GAAM,eAAe;GAAM;EACxG,UAAU;GAAE,MAAM;GAAiB,UAAU;GAAU,gBAAgB;GAAM;EAC7E,QAAQ;GAAE,MAAM;GAAyB,UAAU;GAAU,gBAAgB;GAAM;EACnF,WAAW;GAAE,MAAM;GAAW,UAAU;GAAU,gBAAgB;GAAM,eAAe;GAAM;EAC7F,cAAc;GAAE,MAAM;GAAc,UAAU;GAAU,gBAAgB;GAAM,eAAe;GAAM;EACnG,eAAe;GAAE,MAAM;GAAmB,UAAU;GAAU,gBAAgB;GAAM,eAAe;GAAM;EACzG,OAAO;GAAE,MAAM;GAAc,UAAU;GAAa,gBAAgB;GAAM;EAC1E,WAAW;GAAE,MAAM;GAAc,UAAU;GAAa,gBAAgB;GAAM;EAC9E,YAAY;GAAE,MAAM;GAAY,UAAU;GAAa,gBAAgB;GAAM;EAC7E,cAAc;GAAE,MAAM;GAA+B,UAAU;GAAa,gBAAgB;GAAM;EAClG,eAAe;GAAE,MAAM;GAAgB,UAAU;GAAa,gBAAgB;GAAM;EACpF,YAAY;GAAE,MAAM;GAAY,UAAU;GAAa,gBAAgB;GAAM;EAC7E,eAAe;GAAE,MAAM;GAAe,UAAU;GAAa,gBAAgB;GAAM;;EAEnF,aAAa;GAAE,MAAM;GAAuB,UAAU;GAAa,gBAAgB;GAAM;;EAEzF,OAAO;GAAE,MAAM;GAAoC,UAAU;GAAU,gBAAgB;GAAM;EAC7F,kBAAkB;GAAE,MAAM;GAAkB,UAAU;GAAc,gBAAgB;GAAM;EAC1F,0BAA0B;GAAE,MAAM;GAAgB,UAAU;GAAc,gBAAgB;GAAM;EAChG,iBAAiB;GAAE,MAAM;GAAoB,UAAU;GAAc,gBAAgB;GAAM;EAC3F,qBAAqB;GAAE,MAAM;GAAqB,UAAU;GAAc,gBAAgB;GAAM;EAChG,kBAAkB;GAAE,MAAM;GAA0B,UAAU;GAAS,eAAe;GAAM;EAC5F,gBAAgB;GAAE,MAAM;GAAwB,UAAU;GAAS,eAAe;GAAM;EACxF,qBAAqB;GAAE,MAAM;GAA6B,UAAU;GAAS,eAAe;GAAM;EAClG,sBAAsB;GAAE,MAAM;GAA8B,UAAU;GAAS,eAAe;GAAM;EACrG"}
1
+ {"version":3,"file":"index.js","names":["getPiAiModel","getPiAiProviders","getPiAiModels"],"sources":["../../../src/providers/index.ts"],"sourcesContent":["/**\n * Model provider module - integrates built-in models with custom models from models.json\n */\n\nimport {\n\tgetModel as getPiAiModel,\n\tgetModels as getPiAiModels,\n\tgetProviders as getPiAiProviders,\n\ttype Model,\n\ttype Api,\n} from '@mariozechner/pi-ai';\nimport type { Config } from '../config/schema.js';\nimport { getModelRegistry } from './model-registry.js';\nimport { CredentialResolver, resolveApiKey, hasCredentials } from '../auth/credentials.js';\nimport { hasProviderAuthOnDiskSync } from '../auth/sync-provider-auth.js';\nimport { getApiKeyFromEnv } from './env-keys.js';\nimport { getProviderRegistry } from './plugin-registry.js';\nimport type { ProviderModelDefinition } from '../extensions/types/providers.js';\n\nexport { getApiKeyFromEnv, PROVIDER_ENV_MAP } from './env-keys.js';\n\n/** Sentinel base URL: model is served by an extension {@link ProviderPluginRegistry} provider. */\nexport const EXTENSION_PROVIDER_BASE_URL = 'extension://provider-plugin';\n\n/** Map a plugin registry model to the pi-ai {@link Model} shape. */\nexport function pluginModelToModel(providerId: string, definition: ProviderModelDefinition): Model<Api> {\n\treturn {\n\t\tprovider: providerId,\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\tapi: 'openai-completions' as Api,\n\t\tbaseUrl: EXTENSION_PROVIDER_BASE_URL,\n\t\treasoning: false,\n\t\tinput: definition.supportsImages ? (['text', 'image'] as ('text' | 'image')[]) : (['text'] as ('text' | 'image')[]),\n\t\tcontextWindow: definition.contextWindow ?? 128000,\n\t\tmaxTokens: definition.maxOutputTokens ?? 4096,\n\t\tcost: {\n\t\t\tinput: definition.pricing?.input ?? 0,\n\t\t\toutput: definition.pricing?.output ?? 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t},\n\t} as Model<Api>;\n}\n\n/**\n * Get API key synchronously: checks registry (models.json) first, then environment variables.\n * Use this for Agent's getApiKey callback which must be synchronous.\n */\nexport function getApiKeySync(provider: string): string | undefined {\n const pluginRegistry = getProviderRegistry();\n if (pluginRegistry.has(provider)) return 'extension-managed';\n\n const registry = getModelRegistry();\n const registryKey = registry.getApiKey(provider);\n if (registryKey) {\n return registryKey;\n }\n return getApiKeyFromEnv(provider);\n}\n\n/**\n * Resolve model reference. Supports:\n * - \"provider/modelId\" format\n * - \"modelId\" auto-detection via pi-ai or custom models\n * @throws if model not found\n */\nexport function resolveModel(ref: string): Model<Api> {\n\t// First try ModelRegistry (includes custom models)\n\tconst registry = getModelRegistry();\n\tconst customModel = registry.resolve(ref);\n\tif (customModel) {\n\t\treturn customModel;\n\t}\n\n\tif (ref.includes('/')) {\n\t\tconst [provider, modelId] = ref.split('/');\n\t\tconst piAiModel = getPiAiModel(provider as any, modelId as any);\n\t\tif (piAiModel) return piAiModel as Model<Api>;\n\n\t\tconst pluginRegistry = getProviderRegistry();\n\t\tconst plugin = pluginRegistry.get(provider);\n\t\tif (plugin) {\n\t\t\tconst pluginModel = plugin.models.find(m => m.id === modelId);\n\t\t\tif (pluginModel) return pluginModelToModel(provider, pluginModel);\n\t\t}\n\t\tthrow new Error(`Model not found: ${ref}`);\n\t}\n\n\tfor (const provider of getPiAiProviders()) {\n\t\ttry {\n\t\t\tconst models = getPiAiModels(provider);\n\t\t\tconst found = models.find(m => m.id === ref);\n\t\t\tif (found) return found as Model<Api>;\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\tconst pluginRegistry = getProviderRegistry();\n\tfor (const plugin of pluginRegistry.listAll()) {\n\t\tconst found = plugin.models.find(m => m.id === ref);\n\t\tif (found) return pluginModelToModel(plugin.id, found);\n\t}\n\n\tthrow new Error(`Model not found: ${ref}. Use format: provider/model-id`);\n}\n\nexport function getModelsByProvider(provider: string): readonly Model<Api>[] {\n\tconst registry = getModelRegistry();\n\tconst fromRegistry = registry.getAll().filter(m => m.provider === provider);\n\tconst plugin = getProviderRegistry().get(provider);\n\tif (!plugin) return fromRegistry;\n\tconst pluginModels = plugin.models.map(m => pluginModelToModel(provider, m));\n\treturn [...fromRegistry, ...pluginModels];\n}\n\nexport function getAllProviders(): string[] {\n\tconst registry = getModelRegistry();\n\tconst providers = new Set<string>();\n\n\t// Add built-in providers\n\tfor (const p of getPiAiProviders()) {\n\t\tproviders.add(p);\n\t}\n\n\t// Add custom providers from registry\n\tfor (const m of registry.getAll()) {\n\t\tproviders.add(m.provider);\n\t}\n\n\tfor (const plugin of getProviderRegistry().listAll()) {\n\t\tproviders.add(plugin.id);\n\t}\n\n\treturn Array.from(providers);\n}\n\nexport async function getApiKey(provider: string): Promise<string | undefined> {\n\tif (getProviderRegistry().has(provider)) return 'extension-managed';\n\n\t// Use new credential resolver first (checks: agent private > global > oauth > env)\n\tconst credentialKey = await resolveApiKey(provider);\n\tif (credentialKey) {\n\t\treturn credentialKey;\n\t}\n\n\t// Check registry for custom providers (from models.json)\n\tconst registry = getModelRegistry();\n\tconst registryKey = registry.getApiKey(provider);\n\tif (registryKey) {\n\t\treturn registryKey;\n\t}\n\n\t// Fallback to environment variables\n\treturn getApiKeyFromEnv(provider);\n}\n\n/**\n * Synchronous version for use in non-async contexts\n * Only checks environment variables and registry, not credential system\n */\nexport function isProviderConfiguredSync(provider: string): boolean {\n\tif (getProviderRegistry().has(provider)) return true;\n\n\t// Check registry for custom providers\n\tconst registry = getModelRegistry();\n\tif (registry.getApiKey(provider)) {\n\t\treturn true;\n\t}\n\t// Check environment variables\n\tif (getApiKeyFromEnv(provider)) {\n\t\treturn true;\n\t}\n\t// Gateway UI / CLI store keys in auth-profiles.json (async CredentialResolver); sync path for fallback list\n\treturn hasProviderAuthOnDiskSync(provider);\n}\n\nexport async function isProviderConfigured(provider: string): Promise<boolean> {\n if (getProviderRegistry().has(provider)) return true;\n\n // Check registry first for custom providers (from models.json)\n const registry = getModelRegistry();\n if (registry.getApiKey(provider)) {\n return true;\n }\n return await hasCredentials(provider);\n}\n\n/** Where runtime {@link getApiKey} resolves the key from (no secret values). */\nexport type ProviderActiveKeySource = 'none' | 'agent' | 'gateway' | 'oauth' | 'env' | 'models_json' | 'extension';\n\nexport async function getProviderActiveKeySource(provider: string): Promise<ProviderActiveKeySource> {\n if (getProviderRegistry().has(provider)) return 'extension';\n\n const resolver = new CredentialResolver();\n const fromCredentials = await resolver.resolveApiKeySource(provider);\n if (fromCredentials === 'agent') return 'agent';\n if (fromCredentials === 'global') return 'gateway';\n if (fromCredentials === 'oauth') return 'oauth';\n if (fromCredentials === 'env') return 'env';\n\n const registry = getModelRegistry();\n if (registry.getApiKey(provider)) {\n return 'models_json';\n }\n\n return 'none';\n}\n\nexport async function getConfiguredProviders(): Promise<string[]> {\n\tconst allProviders = getAllProviders();\n\tconst configured: string[] = [];\n\tfor (const p of allProviders) {\n\t\tif (await isProviderConfigured(p)) {\n\t\t\tconfigured.push(p);\n\t\t}\n\t}\n\treturn configured;\n}\n\nexport function getAllModels(): readonly Model<Api>[] {\n\tconst registry = getModelRegistry();\n\tconst registryModels = registry.getAll();\n\tconst pluginProviders = getProviderRegistry().listAll();\n\tif (pluginProviders.length === 0) return registryModels;\n\n\tconst existingIds = new Set(registryModels.map(m => `${m.provider}/${m.id}`));\n\tconst merged: Model<Api>[] = [...registryModels];\n\tfor (const plugin of pluginProviders) {\n\t\tfor (const model of plugin.models) {\n\t\t\tconst compositeId = `${plugin.id}/${model.id}`;\n\t\t\tif (!existingIds.has(compositeId)) {\n\t\t\t\tmerged.push(pluginModelToModel(plugin.id, model));\n\t\t\t\texistingIds.add(compositeId);\n\t\t\t}\n\t\t}\n\t}\n\treturn merged;\n}\n\nexport async function getAvailableModels(): Promise<readonly Model<Api>[]> {\n\tconst allModels = getAllModels();\n\tconst pluginRegistry = getProviderRegistry();\n\tconst available: Model<Api>[] = [];\n\n\tfor (const model of allModels) {\n\t\tif (pluginRegistry.has(model.provider)) {\n\t\t\tavailable.push(model);\n\t\t} else if (await isProviderConfigured(model.provider)) {\n\t\t\tavailable.push(model);\n\t\t}\n\t}\n\treturn available;\n}\n\nexport type { Model, Api } from '@mariozechner/pi-ai';\n\nexport type ProviderCategory = 'common' | 'specialty' | 'oauth' | 'enterprise' | 'extension';\n\nexport interface ProviderMeta {\n name: string;\n category: ProviderCategory;\n supportsOAuth?: boolean;\n supportsApiKey?: boolean;\n}\n\nexport const PROVIDER_META: Record<string, ProviderMeta> = {\n 'openai': { name: 'OpenAI (GPT-4, o1, o3)', category: 'common', supportsApiKey: true },\n 'anthropic': { name: 'Anthropic Claude', category: 'common', supportsApiKey: true, supportsOAuth: true },\n 'deepseek': { name: 'DeepSeek', category: 'common', supportsApiKey: true },\n 'google': { name: 'Google Gemini', category: 'common', supportsApiKey: true },\n 'groq': { name: 'Groq (Fast Inference)', category: 'common', supportsApiKey: true },\n 'minimax': { name: 'MiniMax', category: 'common', supportsApiKey: true, supportsOAuth: true },\n 'minimax-cn': { name: 'MiniMax CN', category: 'common', supportsApiKey: true, supportsOAuth: true },\n 'kimi-coding': { name: 'Kimi For Coding', category: 'common', supportsApiKey: true, supportsOAuth: true },\n 'xai': { name: 'xAI (Grok)', category: 'specialty', supportsApiKey: true },\n 'mistral': { name: 'Mistral AI', category: 'specialty', supportsApiKey: true },\n 'cerebras': { name: 'Cerebras', category: 'specialty', supportsApiKey: true },\n 'openrouter': { name: 'OpenRouter (Multi-provider)', category: 'specialty', supportsApiKey: true },\n 'huggingface': { name: 'Hugging Face', category: 'specialty', supportsApiKey: true },\n 'opencode': { name: 'OpenCode', category: 'specialty', supportsApiKey: true },\n 'opencode-go': { name: 'OpenCode Go', category: 'specialty', supportsApiKey: true },\n /** DashScope (Alibaba) — image, speech, STT; not an LLM KnownProvider. */\n 'dashscope': { name: 'DashScope (Alibaba)', category: 'specialty', supportsApiKey: true },\n /** International GLM (api.z.ai). Auth: API key (ZAI_API_KEY); no published OAuth for this HTTP API. */\n 'zai': { name: 'Zhipu GLM (International · z.ai)', category: 'common', supportsApiKey: true },\n 'amazon-bedrock': { name: 'Amazon Bedrock', category: 'enterprise', supportsApiKey: true },\n 'azure-openai-responses': { name: 'Azure OpenAI', category: 'enterprise', supportsApiKey: true },\n 'google-vertex': { name: 'Google Vertex AI', category: 'enterprise', supportsApiKey: true },\n 'vercel-ai-gateway': { name: 'Vercel AI Gateway', category: 'enterprise', supportsApiKey: true },\n 'github-copilot': { name: 'GitHub Copilot (OAuth)', category: 'oauth', supportsOAuth: true },\n 'openai-codex': { name: 'OpenAI Codex (OAuth)', category: 'oauth', supportsOAuth: true },\n 'google-gemini-cli': { name: 'Google Gemini CLI (OAuth)', category: 'oauth', supportsOAuth: true },\n 'google-antigravity': { name: 'Google Antigravity (OAuth)', category: 'oauth', supportsOAuth: true },\n};\n\nexport function getSortedProviders(): string[] {\n const all = getAllProviders();\n const catOrder: Record<ProviderCategory, number> = { common: 0, specialty: 1, enterprise: 2, oauth: 3, extension: 4 };\n const pluginRegistry = getProviderRegistry();\n\n return [...all].sort((a, b) => {\n const catA = pluginRegistry.has(a) ? 'extension' : (PROVIDER_META[a]?.category ?? 'specialty');\n const catB = pluginRegistry.has(b) ? 'extension' : (PROVIDER_META[b]?.category ?? 'specialty');\n if (catOrder[catA] !== catOrder[catB]) {\n return catOrder[catA] - catOrder[catB];\n }\n return a.localeCompare(b);\n });\n}\n\nexport function getProviderDisplayName(provider: string): string {\n const plugin = getProviderRegistry().get(provider);\n if (plugin) return plugin.name;\n return PROVIDER_META[provider]?.name || provider;\n}\n\nexport function providerSupportsOAuth(provider: string): boolean {\n return PROVIDER_META[provider]?.supportsOAuth ?? false;\n}\n\nexport function providerSupportsApiKey(provider: string): boolean {\n return PROVIDER_META[provider]?.supportsApiKey ?? true;\n}\n\n// ============================================\n// Dynamic Default Model Resolution\n// ============================================\n\n/**\n * Get a default model reference.\n * Priority:\n * 1. First available model with configured API key\n * 2. First model from pi-ai catalog\n * 3. Fallback to anthropic/claude-sonnet-4-5 as last resort\n */\nexport async function getDefaultModel(config?: Config | null | undefined): Promise<string> {\n const availableModels = await getAvailableModels();\n \n // Try to find configured default model first\n const defaultModel = config?.agents?.defaults?.model;\n if (defaultModel) {\n const modelRef = typeof defaultModel === 'string' ? defaultModel : defaultModel.primary;\n if (modelRef) {\n // Check if the configured model has valid API key\n const configured = availableModels.find(m => \n `${m.provider}/${m.id}` === modelRef ||\n m.id === modelRef\n );\n if (configured) {\n return `${configured.provider}/${configured.id}`;\n }\n }\n }\n \n // Return first available model\n if (availableModels.length > 0) {\n return `${availableModels[0].provider}/${availableModels[0].id}`;\n }\n \n // Try to get first model from pi-ai catalog\n for (const provider of getPiAiProviders()) {\n try {\n const models = getPiAiModels(provider);\n if (models.length > 0) {\n return `${provider}/${models[0].id}`;\n }\n } catch {\n continue;\n }\n }\n \n // Last resort fallback\n return 'anthropic/claude-sonnet-4-5';\n}\n\n/**\n * Synchronous default model resolution for constructors and sync code paths.\n * Uses catalog/registry only (no async credential checks).\n */\nexport function getDefaultModelSync(config?: Config | null | undefined): string {\n const defaultModel = config?.agents?.defaults?.model;\n if (defaultModel) {\n const modelRef = typeof defaultModel === 'string' ? defaultModel : defaultModel.primary;\n if (modelRef) {\n return modelRef;\n }\n }\n const all = getAllModels();\n if (all.length > 0) {\n return `${all[0].provider}/${all[0].id}`;\n }\n for (const provider of getPiAiProviders()) {\n try {\n const models = getPiAiModels(provider);\n if (models.length > 0) {\n return `${provider}/${models[0].id}`;\n }\n } catch {\n continue;\n }\n }\n return 'anthropic/claude-sonnet-4-5';\n}\n\n// Re-export ModelRegistry for advanced use cases\nexport { ModelRegistry, getModelRegistry, resetModelRegistry } from './model-registry.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,mBAAmB,YAAoB,YAAiD;AACvG,QAAO;EACN,UAAU;EACV,IAAI,WAAW;EACf,MAAM,WAAW;EACjB,KAAK;EACL,SAAS;EACT,WAAW;EACX,OAAO,WAAW,iBAAkB,CAAC,QAAQ,QAAQ,GAA6B,CAAC,OAAO;EAC1F,eAAe,WAAW,iBAAiB;EAC3C,WAAW,WAAW,mBAAmB;EACzC,MAAM;GACL,OAAO,WAAW,SAAS,SAAS;GACpC,QAAQ,WAAW,SAAS,UAAU;GACtC,WAAW;GACX,YAAY;GACZ;EACD;;;;;;AAOF,SAAgB,cAAc,UAAsC;AAElE,KADuB,qBACL,CAAC,IAAI,SAAS,CAAE,QAAO;CAGzC,MAAM,cADW,kBACW,CAAC,UAAU,SAAS;AAChD,KAAI,YACF,QAAO;AAET,QAAO,iBAAiB,SAAS;;;;;;;;AASnC,SAAgB,aAAa,KAAyB;CAGrD,MAAM,cADW,kBACW,CAAC,QAAQ,IAAI;AACzC,KAAI,YACH,QAAO;AAGR,KAAI,IAAI,SAAS,IAAI,EAAE;EACtB,MAAM,CAAC,UAAU,WAAW,IAAI,MAAM,IAAI;EAC1C,MAAM,YAAYA,SAAa,UAAiB,QAAe;AAC/D,MAAI,UAAW,QAAO;EAGtB,MAAM,SADiB,qBACM,CAAC,IAAI,SAAS;AAC3C,MAAI,QAAQ;GACX,MAAM,cAAc,OAAO,OAAO,MAAK,MAAK,EAAE,OAAO,QAAQ;AAC7D,OAAI,YAAa,QAAO,mBAAmB,UAAU,YAAY;;AAElE,QAAM,IAAI,MAAM,oBAAoB,MAAM;;AAG3C,MAAK,MAAM,YAAYC,cAAkB,CACxC,KAAI;EAEH,MAAM,QADSC,UAAc,SACT,CAAC,MAAK,MAAK,EAAE,OAAO,IAAI;AAC5C,MAAI,MAAO,QAAO;SACX;AACP;;CAIF,MAAM,iBAAiB,qBAAqB;AAC5C,MAAK,MAAM,UAAU,eAAe,SAAS,EAAE;EAC9C,MAAM,QAAQ,OAAO,OAAO,MAAK,MAAK,EAAE,OAAO,IAAI;AACnD,MAAI,MAAO,QAAO,mBAAmB,OAAO,IAAI,MAAM;;AAGvD,OAAM,IAAI,MAAM,oBAAoB,IAAI,iCAAiC;;AAG1E,SAAgB,oBAAoB,UAAyC;CAE5E,MAAM,eADW,kBACY,CAAC,QAAQ,CAAC,QAAO,MAAK,EAAE,aAAa,SAAS;CAC3E,MAAM,SAAS,qBAAqB,CAAC,IAAI,SAAS;AAClD,KAAI,CAAC,OAAQ,QAAO;CACpB,MAAM,eAAe,OAAO,OAAO,KAAI,MAAK,mBAAmB,UAAU,EAAE,CAAC;AAC5E,QAAO,CAAC,GAAG,cAAc,GAAG,aAAa;;AAG1C,SAAgB,kBAA4B;CAC3C,MAAM,WAAW,kBAAkB;CACnC,MAAM,4BAAY,IAAI,KAAa;AAGnC,MAAK,MAAM,KAAKD,cAAkB,CACjC,WAAU,IAAI,EAAE;AAIjB,MAAK,MAAM,KAAK,SAAS,QAAQ,CAChC,WAAU,IAAI,EAAE,SAAS;AAG1B,MAAK,MAAM,UAAU,qBAAqB,CAAC,SAAS,CACnD,WAAU,IAAI,OAAO,GAAG;AAGzB,QAAO,MAAM,KAAK,UAAU;;AAG7B,eAAsB,UAAU,UAA+C;AAC9E,KAAI,qBAAqB,CAAC,IAAI,SAAS,CAAE,QAAO;CAGhD,MAAM,gBAAgB,MAAM,cAAc,SAAS;AACnD,KAAI,cACH,QAAO;CAKR,MAAM,cADW,kBACW,CAAC,UAAU,SAAS;AAChD,KAAI,YACH,QAAO;AAIR,QAAO,iBAAiB,SAAS;;;;;;AAOlC,SAAgB,yBAAyB,UAA2B;AACnE,KAAI,qBAAqB,CAAC,IAAI,SAAS,CAAE,QAAO;AAIhD,KADiB,kBACL,CAAC,UAAU,SAAS,CAC/B,QAAO;AAGR,KAAI,iBAAiB,SAAS,CAC7B,QAAO;AAGR,QAAO,0BAA0B,SAAS;;AAG3C,eAAsB,qBAAqB,UAAoC;AAC7E,KAAI,qBAAqB,CAAC,IAAI,SAAS,CAAE,QAAO;AAIhD,KADiB,kBACL,CAAC,UAAU,SAAS,CAC9B,QAAO;AAET,QAAO,MAAM,eAAe,SAAS;;AAMvC,eAAsB,2BAA2B,UAAoD;AACnG,KAAI,qBAAqB,CAAC,IAAI,SAAS,CAAE,QAAO;CAGhD,MAAM,kBAAkB,MAAM,IADT,oBACiB,CAAC,oBAAoB,SAAS;AACpE,KAAI,oBAAoB,QAAS,QAAO;AACxC,KAAI,oBAAoB,SAAU,QAAO;AACzC,KAAI,oBAAoB,QAAS,QAAO;AACxC,KAAI,oBAAoB,MAAO,QAAO;AAGtC,KADiB,kBACL,CAAC,UAAU,SAAS,CAC9B,QAAO;AAGT,QAAO;;AAGT,eAAsB,yBAA4C;CACjE,MAAM,eAAe,iBAAiB;CACtC,MAAM,aAAuB,EAAE;AAC/B,MAAK,MAAM,KAAK,aACf,KAAI,MAAM,qBAAqB,EAAE,CAChC,YAAW,KAAK,EAAE;AAGpB,QAAO;;AAGR,SAAgB,eAAsC;CAErD,MAAM,iBADW,kBACc,CAAC,QAAQ;CACxC,MAAM,kBAAkB,qBAAqB,CAAC,SAAS;AACvD,KAAI,gBAAgB,WAAW,EAAG,QAAO;CAEzC,MAAM,cAAc,IAAI,IAAI,eAAe,KAAI,MAAK,GAAG,EAAE,SAAS,GAAG,EAAE,KAAK,CAAC;CAC7E,MAAM,SAAuB,CAAC,GAAG,eAAe;AAChD,MAAK,MAAM,UAAU,gBACpB,MAAK,MAAM,SAAS,OAAO,QAAQ;EAClC,MAAM,cAAc,GAAG,OAAO,GAAG,GAAG,MAAM;AAC1C,MAAI,CAAC,YAAY,IAAI,YAAY,EAAE;AAClC,UAAO,KAAK,mBAAmB,OAAO,IAAI,MAAM,CAAC;AACjD,eAAY,IAAI,YAAY;;;AAI/B,QAAO;;AAGR,eAAsB,qBAAqD;CAC1E,MAAM,YAAY,cAAc;CAChC,MAAM,iBAAiB,qBAAqB;CAC5C,MAAM,YAA0B,EAAE;AAElC,MAAK,MAAM,SAAS,UACnB,KAAI,eAAe,IAAI,MAAM,SAAS,CACrC,WAAU,KAAK,MAAM;UACX,MAAM,qBAAqB,MAAM,SAAS,CACpD,WAAU,KAAK,MAAM;AAGvB,QAAO;;AA4CR,SAAgB,qBAA+B;CAC7C,MAAM,MAAM,iBAAiB;CAC7B,MAAM,WAA6C;EAAE,QAAQ;EAAG,WAAW;EAAG,YAAY;EAAG,OAAO;EAAG,WAAW;EAAG;CACrH,MAAM,iBAAiB,qBAAqB;AAE5C,QAAO,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,MAAM;EAC7B,MAAM,OAAO,eAAe,IAAI,EAAE,GAAG,cAAe,cAAc,IAAI,YAAY;EAClF,MAAM,OAAO,eAAe,IAAI,EAAE,GAAG,cAAe,cAAc,IAAI,YAAY;AAClF,MAAI,SAAS,UAAU,SAAS,MAC9B,QAAO,SAAS,QAAQ,SAAS;AAEnC,SAAO,EAAE,cAAc,EAAE;GACzB;;AAGJ,SAAgB,uBAAuB,UAA0B;CAC/D,MAAM,SAAS,qBAAqB,CAAC,IAAI,SAAS;AAClD,KAAI,OAAQ,QAAO,OAAO;AAC1B,QAAO,cAAc,WAAW,QAAQ;;AAG1C,SAAgB,sBAAsB,UAA2B;AAC/D,QAAO,cAAc,WAAW,iBAAiB;;AAGnD,SAAgB,uBAAuB,UAA2B;AAChE,QAAO,cAAc,WAAW,kBAAkB;;;;;;;;;AAcpD,eAAsB,gBAAgB,QAAqD;CACzF,MAAM,kBAAkB,MAAM,oBAAoB;CAGlD,MAAM,eAAe,QAAQ,QAAQ,UAAU;AAC/C,KAAI,cAAc;EAChB,MAAM,WAAW,OAAO,iBAAiB,WAAW,eAAe,aAAa;AAChF,MAAI,UAAU;GAEZ,MAAM,aAAa,gBAAgB,MAAK,MACtC,GAAG,EAAE,SAAS,GAAG,EAAE,SAAS,YAC5B,EAAE,OAAO,SACV;AACD,OAAI,WACF,QAAO,GAAG,WAAW,SAAS,GAAG,WAAW;;;AAMlD,KAAI,gBAAgB,SAAS,EAC3B,QAAO,GAAG,gBAAgB,GAAG,SAAS,GAAG,gBAAgB,GAAG;AAI9D,MAAK,MAAM,YAAYA,cAAkB,CACvC,KAAI;EACF,MAAM,SAASC,UAAc,SAAS;AACtC,MAAI,OAAO,SAAS,EAClB,QAAO,GAAG,SAAS,GAAG,OAAO,GAAG;SAE5B;AACN;;AAKJ,QAAO;;;;;;AAOT,SAAgB,oBAAoB,QAA4C;CAC9E,MAAM,eAAe,QAAQ,QAAQ,UAAU;AAC/C,KAAI,cAAc;EAChB,MAAM,WAAW,OAAO,iBAAiB,WAAW,eAAe,aAAa;AAChF,MAAI,SACF,QAAO;;CAGX,MAAM,MAAM,cAAc;AAC1B,KAAI,IAAI,SAAS,EACf,QAAO,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,GAAG;AAEtC,MAAK,MAAM,YAAYD,cAAkB,CACvC,KAAI;EACF,MAAM,SAASC,UAAc,SAAS;AACtC,MAAI,OAAO,SAAS,EAClB,QAAO,GAAG,SAAS,GAAG,OAAO,GAAG;SAE5B;AACN;;AAGJ,QAAO;;;;sBAvY8C;mBACoC;0BACjB;gBACzB;uBACU;AAM9C,+BAA8B;AAqP9B,iBAA8C;EACzD,UAAU;GAAE,MAAM;GAA0B,UAAU;GAAU,gBAAgB;GAAM;EACtF,aAAa;GAAE,MAAM;GAAoB,UAAU;GAAU,gBAAgB;GAAM,eAAe;GAAM;EACxG,YAAY;GAAE,MAAM;GAAY,UAAU;GAAU,gBAAgB;GAAM;EAC1E,UAAU;GAAE,MAAM;GAAiB,UAAU;GAAU,gBAAgB;GAAM;EAC7E,QAAQ;GAAE,MAAM;GAAyB,UAAU;GAAU,gBAAgB;GAAM;EACnF,WAAW;GAAE,MAAM;GAAW,UAAU;GAAU,gBAAgB;GAAM,eAAe;GAAM;EAC7F,cAAc;GAAE,MAAM;GAAc,UAAU;GAAU,gBAAgB;GAAM,eAAe;GAAM;EACnG,eAAe;GAAE,MAAM;GAAmB,UAAU;GAAU,gBAAgB;GAAM,eAAe;GAAM;EACzG,OAAO;GAAE,MAAM;GAAc,UAAU;GAAa,gBAAgB;GAAM;EAC1E,WAAW;GAAE,MAAM;GAAc,UAAU;GAAa,gBAAgB;GAAM;EAC9E,YAAY;GAAE,MAAM;GAAY,UAAU;GAAa,gBAAgB;GAAM;EAC7E,cAAc;GAAE,MAAM;GAA+B,UAAU;GAAa,gBAAgB;GAAM;EAClG,eAAe;GAAE,MAAM;GAAgB,UAAU;GAAa,gBAAgB;GAAM;EACpF,YAAY;GAAE,MAAM;GAAY,UAAU;GAAa,gBAAgB;GAAM;EAC7E,eAAe;GAAE,MAAM;GAAe,UAAU;GAAa,gBAAgB;GAAM;;EAEnF,aAAa;GAAE,MAAM;GAAuB,UAAU;GAAa,gBAAgB;GAAM;;EAEzF,OAAO;GAAE,MAAM;GAAoC,UAAU;GAAU,gBAAgB;GAAM;EAC7F,kBAAkB;GAAE,MAAM;GAAkB,UAAU;GAAc,gBAAgB;GAAM;EAC1F,0BAA0B;GAAE,MAAM;GAAgB,UAAU;GAAc,gBAAgB;GAAM;EAChG,iBAAiB;GAAE,MAAM;GAAoB,UAAU;GAAc,gBAAgB;GAAM;EAC3F,qBAAqB;GAAE,MAAM;GAAqB,UAAU;GAAc,gBAAgB;GAAM;EAChG,kBAAkB;GAAE,MAAM;GAA0B,UAAU;GAAS,eAAe;GAAM;EAC5F,gBAAgB;GAAE,MAAM;GAAwB,UAAU;GAAS,eAAe;GAAM;EACxF,qBAAqB;GAAE,MAAM;GAA6B,UAAU;GAAS,eAAe;GAAM;EAClG,sBAAsB;GAAE,MAAM;GAA8B,UAAU;GAAS,eAAe;GAAM;EACrG"}
@@ -3,6 +3,7 @@ import { createLogger } from "../utils/logger/index.js";
3
3
  import { init_logger } from "../utils/logger.js";
4
4
  import { init_providers, resolveModel } from "../providers/index.js";
5
5
  import { stripInboundFileMetadataFromText } from "../channels/attachments/inbound-persist.js";
6
+ import { stripEnvelopeTimestampPrefix } from "../channels/envelope-timestamp.js";
6
7
  import { complete } from "@mariozechner/pi-ai";
7
8
  //#region src/session/session-title.ts
8
9
  init_session_key();
@@ -28,7 +29,7 @@ function extractTextFromMessage(m) {
28
29
  function firstUserText(messages) {
29
30
  const u = messages.find((m) => m.role === "user");
30
31
  if (!u) return "";
31
- return stripInboundFileMetadataFromText(extractTextFromMessage(u));
32
+ return stripInboundFileMetadataFromText(stripEnvelopeTimestampPrefix(extractTextFromMessage(u)));
32
33
  }
33
34
  /** First assistant message that has visible text (skips tool-only assistant rows). */
34
35
  function firstAssistantText(messages) {