@xopcai/xopc 0.0.104 → 0.0.106

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 (184) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/adapters/gateway-setup.js +2 -2
  3. package/dist/extensions/feishu/src/adapters/gateway-setup.js.map +1 -1
  4. package/dist/extensions/feishu/src/directory/directory-adapter.js +1 -1
  5. package/dist/extensions/feishu/src/directory/directory-adapter.js.map +1 -1
  6. package/dist/extensions/feishu/src/outbound/outbound-adapter.js +4 -3
  7. package/dist/extensions/feishu/src/outbound/outbound-adapter.js.map +1 -1
  8. package/dist/extensions/feishu/src/plugin.d.ts +6 -3
  9. package/dist/extensions/feishu/src/plugin.js +59 -40
  10. package/dist/extensions/feishu/src/plugin.js.map +1 -1
  11. package/dist/extensions/feishu/src/streaming/streaming-adapter.js +20 -16
  12. package/dist/extensions/feishu/src/streaming/streaming-adapter.js.map +1 -1
  13. package/dist/extensions/feishu/src/transport/socket-mode/monitor.js +1 -1
  14. package/dist/extensions/feishu/src/transport/webhook/monitor.js +1 -1
  15. package/dist/extensions/feishu/src/workflow-progress.d.ts +1 -1
  16. package/dist/extensions/feishu/src/workflow-progress.js +2 -2
  17. package/dist/extensions/feishu/src/workflow-progress.js.map +1 -1
  18. package/dist/extensions/telegram/src/adapters/gateway-setup.js +8 -6
  19. package/dist/extensions/telegram/src/adapters/gateway-setup.js.map +1 -1
  20. package/dist/extensions/telegram/src/command-handler.d.ts +1 -1
  21. package/dist/extensions/telegram/src/command-handler.js +11 -9
  22. package/dist/extensions/telegram/src/command-handler.js.map +1 -1
  23. package/dist/extensions/telegram/src/inbound-processor.js +1 -2
  24. package/dist/extensions/telegram/src/inbound-processor.js.map +1 -1
  25. package/dist/extensions/telegram/src/plugin.d.ts +1 -0
  26. package/dist/extensions/telegram/src/plugin.js +6 -6
  27. package/dist/extensions/telegram/src/plugin.js.map +1 -1
  28. package/dist/extensions/telegram/src/workflow-progress.d.ts +1 -1
  29. package/dist/extensions/telegram/src/workflow-progress.js.map +1 -1
  30. package/dist/extensions/telegram/xopc.extension.json +1 -1
  31. package/dist/extensions/weixin/src/cli/gateway-qr-login.d.ts +4 -0
  32. package/dist/extensions/weixin/src/cli/gateway-qr-login.js +34 -5
  33. package/dist/extensions/weixin/src/cli/gateway-qr-login.js.map +1 -1
  34. package/dist/extensions/weixin/src/cli/qr-login.js +4 -3
  35. package/dist/extensions/weixin/src/cli/qr-login.js.map +1 -1
  36. package/dist/extensions/weixin/src/plugin.d.ts +1 -0
  37. package/dist/extensions/weixin/src/plugin.js +7 -6
  38. package/dist/extensions/weixin/src/plugin.js.map +1 -1
  39. package/dist/extensions/weixin/src/workflow-progress.d.ts +1 -1
  40. package/dist/extensions/weixin/src/workflow-progress.js.map +1 -1
  41. package/dist/gateway/static/root/assets/agents-3eP0vqFO.js +396 -0
  42. package/dist/gateway/static/root/assets/{apps-page-L-IwQTe0.js → apps-page-CZ2ZKrko.js} +1 -1
  43. package/dist/gateway/static/root/assets/channels-settings-BbETa93K.js +1 -0
  44. package/dist/gateway/static/root/assets/cron-page-DCtUkHMu.js +1 -0
  45. package/dist/gateway/static/root/assets/{dist-C6vclgEL.js → dist-OM9qGfm5.js} +1 -1
  46. package/dist/gateway/static/root/assets/{extension-debug-page-D4JYAbsX.js → extension-debug-page-D-WHrr_w.js} +1 -1
  47. package/dist/gateway/static/root/assets/{extension-page-_T62gi6e.js → extension-page-CCgjSoKn.js} +1 -1
  48. package/dist/gateway/static/root/assets/{extension-settings-page-CkJbjyM-.js → extension-settings-page-BJH5a-Gn.js} +1 -1
  49. package/dist/gateway/static/root/assets/{fetch-B5aiIguv.js → fetch-CkvRO30i.js} +1 -1
  50. package/dist/gateway/static/root/assets/{gateway-startup-retry-DqmLXhs6.js → gateway-startup-retry-BkKyRDrF.js} +1 -1
  51. package/dist/gateway/static/root/assets/{heartbeat-config-api-HwJV1K_Y.js → heartbeat-config-api-CVyP6pFj.js} +1 -1
  52. package/dist/gateway/static/root/assets/index-CEZxETzw.js +4845 -0
  53. package/dist/gateway/static/root/assets/{logs-page-DsjtTjBy.js → logs-page-R6Vv_Ja6.js} +2 -2
  54. package/dist/gateway/static/root/assets/{note-detail-page-B2V7RhgD.js → note-detail-page-BUDarLhh.js} +54 -54
  55. package/dist/gateway/static/root/assets/{note-time-BG8GCTr4.js → note-time-Cc2-T21g.js} +1 -1
  56. package/dist/gateway/static/root/assets/{notes-page-xrcVBQ6Z.js → notes-page-EAHl16bq.js} +1 -1
  57. package/dist/gateway/static/root/assets/{schema-form-S-QUvhul.js → schema-form-CrlOmEZt.js} +1 -1
  58. package/dist/gateway/static/root/assets/sessions-page-Ce_xuuSB.js +1 -0
  59. package/dist/gateway/static/root/assets/{settings-advanced-gate-DujQl9c7.js → settings-advanced-gate-BBAQxxew.js} +1 -1
  60. package/dist/gateway/static/root/assets/{settings-form-section-r1V7dRV6.js → settings-form-section-DjPD-ErC.js} +1 -1
  61. package/dist/gateway/static/root/assets/settings-page-KhQ9QgH0.js +3 -0
  62. package/dist/gateway/static/root/assets/{share-preview-page-DOFy0FNt.js → share-preview-page-BjvPht3V.js} +1 -1
  63. package/dist/gateway/static/root/assets/skills-page-C0laq7l1.js +2 -0
  64. package/dist/gateway/static/root/assets/{theme-store-Bo3j5Ovv.js → theme-store-DYgVdRB4.js} +1 -1
  65. package/dist/gateway/static/root/assets/url-DPEStU4M.js +3 -0
  66. package/dist/gateway/static/root/assets/{voice-api-key-field-AjmUAlEQ.js → voice-api-key-field-DeGckWkl.js} +1 -1
  67. package/dist/gateway/static/root/assets/{workflow-page.utils-DLEJ92gK.js → workflow-page.utils-D4sqR0yX.js} +1 -1
  68. package/dist/gateway/static/root/assets/workflows-page-DFjM95g5.js +27 -0
  69. package/dist/gateway/static/root/index.html +5 -5
  70. package/dist/package.js +1 -1
  71. package/dist/src/agent/context/workspace-seed.js +1 -1
  72. package/dist/src/agent/inbound/turn-dispatcher.d.ts +2 -0
  73. package/dist/src/agent/inbound/turn-dispatcher.js +1 -0
  74. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  75. package/dist/src/agent/mcp/bundle-mcp-config.js +16 -8
  76. package/dist/src/agent/mcp/bundle-mcp-config.js.map +1 -1
  77. package/dist/src/agent/mcp/bundle-mcp-policy.js +2 -2
  78. package/dist/src/agent/mcp/bundle-mcp-policy.js.map +1 -1
  79. package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
  80. package/dist/src/agent/mcp/mcp-http.js +1 -1
  81. package/dist/src/agent/mcp/mcp-http.js.map +1 -1
  82. package/dist/src/agent/mcp/mcp-transport-config.js +2 -6
  83. package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
  84. package/dist/src/agent/prompt/memory/index.js +30 -2
  85. package/dist/src/agent/prompt/memory/index.js.map +1 -1
  86. package/dist/src/agent/service/direct-turn-helpers.d.ts +2 -0
  87. package/dist/src/agent/service/direct-turn-helpers.js +5 -2
  88. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  89. package/dist/src/agent/service/process-direct-streaming.d.ts +2 -0
  90. package/dist/src/agent/service/process-direct-streaming.js +15 -1
  91. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  92. package/dist/src/agent/service.js +2 -1
  93. package/dist/src/agent/service.js.map +1 -1
  94. package/dist/src/agent/service.types.d.ts +3 -0
  95. package/dist/src/agent/skills/hub-pull.js +1 -1
  96. package/dist/src/agent/source-context/index.d.ts +2 -0
  97. package/dist/src/agent/source-context/index.js +3 -0
  98. package/dist/src/agent/source-context/injector.d.ts +3 -0
  99. package/dist/src/agent/source-context/injector.js +36 -0
  100. package/dist/src/agent/source-context/injector.js.map +1 -0
  101. package/dist/src/agent/source-context/types.d.ts +19 -0
  102. package/dist/src/agent/source-context/types.js +10 -0
  103. package/dist/src/agent/source-context/types.js.map +1 -0
  104. package/dist/src/agent/tools/factory.js +5 -5
  105. package/dist/src/agent/tools/index.js +3 -3
  106. package/dist/src/agent/tools/session-search-tool.js +1 -1
  107. package/dist/src/agent/tools/shell.js +1 -1
  108. package/dist/src/agent/tools/web-extract.js +2 -2
  109. package/dist/src/agent/tools/web.js +1 -1
  110. package/dist/src/agent/workflow/index.js +2 -1
  111. package/dist/src/agent/workflow/runtime.d.ts +2 -2
  112. package/dist/src/agent/workflow/runtime.js +1 -14
  113. package/dist/src/agent/workflow/runtime.js.map +1 -1
  114. package/dist/src/agent/workflow/snapshot-empty.d.ts +2 -0
  115. package/dist/src/agent/workflow/snapshot-empty.js +19 -0
  116. package/dist/src/agent/workflow/snapshot-empty.js.map +1 -0
  117. package/dist/src/agent/workflow/snapshot.js +1 -1
  118. package/dist/src/agent/workflow/snapshot.js.map +1 -1
  119. package/dist/src/browser/actions/extension-registry-bridge.js +1 -1
  120. package/dist/src/browser/actions/registry.js +2 -2
  121. package/dist/src/browser/cdp-local-launcher.js +1 -1
  122. package/dist/src/browser/index.js +3 -3
  123. package/dist/src/browser/providers/browser-ext-install.js +1 -1
  124. package/dist/src/browser/providers/cloakbrowser.js +1 -1
  125. package/dist/src/cli/commands/browser-cli-helpers.js +1 -1
  126. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  127. package/dist/src/config/paths.d.ts +1 -0
  128. package/dist/src/config/paths.js +5 -1
  129. package/dist/src/config/paths.js.map +1 -1
  130. package/dist/src/connectors/health.js +1 -1
  131. package/dist/src/connectors/install.js +1 -1
  132. package/dist/src/document-understanding/extract.d.ts +13 -0
  133. package/dist/src/document-understanding/extract.js +63 -0
  134. package/dist/src/document-understanding/extract.js.map +1 -0
  135. package/dist/src/gateway/hono/routes/mcp.js +1 -1
  136. package/dist/src/gateway/hono/routes/notes.js +46 -27
  137. package/dist/src/gateway/hono/routes/notes.js.map +1 -1
  138. package/dist/src/gateway/service.js +11 -0
  139. package/dist/src/gateway/service.js.map +1 -1
  140. package/dist/src/mcp/channel-bridge.js +1 -1
  141. package/dist/src/notes/agent-context.d.ts +40 -0
  142. package/dist/src/notes/agent-context.js +383 -0
  143. package/dist/src/notes/agent-context.js.map +1 -0
  144. package/dist/src/notes/index.d.ts +2 -0
  145. package/dist/src/notes/index.js +2 -1
  146. package/dist/src/notes/service.d.ts +5 -0
  147. package/dist/src/notes/service.js +22 -0
  148. package/dist/src/notes/service.js.map +1 -1
  149. package/dist/src/notes/store.js +2 -0
  150. package/dist/src/notes/store.js.map +1 -1
  151. package/dist/src/share/share-thumbnail.js +1 -1
  152. package/dist/src/storage/sqlite/index.d.ts +1 -0
  153. package/dist/src/storage/sqlite/index.js +2 -1
  154. package/dist/src/storage/sqlite/migrations/005_add_note_agent_contexts.sql +10 -0
  155. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  156. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  157. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  158. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  159. package/dist/src/storage/sqlite/note-agent-context-repository.d.ts +10 -0
  160. package/dist/src/storage/sqlite/note-agent-context-repository.js +35 -0
  161. package/dist/src/storage/sqlite/note-agent-context-repository.js.map +1 -0
  162. package/dist/src/storage/sqlite/schema.js +1 -1
  163. package/dist/src/tui/tui-startup-resources.js +1 -1
  164. package/dist/src/voice/stt/availability.d.ts +6 -0
  165. package/dist/src/voice/stt/availability.js +14 -6
  166. package/dist/src/voice/stt/availability.js.map +1 -1
  167. package/dist/src/voice/stt/index.js +2 -1
  168. package/dist/src/voice/stt/index.js.map +1 -1
  169. package/dist/src/voice/stt/mention.d.ts +2 -0
  170. package/dist/src/voice/stt/mention.js +20 -0
  171. package/dist/src/voice/stt/mention.js.map +1 -0
  172. package/dist/src/voice/stt/preflight.d.ts +1 -4
  173. package/dist/src/voice/stt/preflight.js +1 -18
  174. package/dist/src/voice/stt/preflight.js.map +1 -1
  175. package/package.json +4 -4
  176. package/dist/gateway/static/root/assets/agents-CvsBrBfi.js +0 -396
  177. package/dist/gateway/static/root/assets/channels-settings-D95KuPeP.js +0 -1
  178. package/dist/gateway/static/root/assets/cron-page-CfkWXDAI.js +0 -1
  179. package/dist/gateway/static/root/assets/index-DvTb6wNR.js +0 -4845
  180. package/dist/gateway/static/root/assets/sessions-page-CrQer6Au.js +0 -1
  181. package/dist/gateway/static/root/assets/settings-page-C9nrONQ2.js +0 -3
  182. package/dist/gateway/static/root/assets/skills-page-DmTsrs_a.js +0 -2
  183. package/dist/gateway/static/root/assets/url-WgQIjFZu.js +0 -3
  184. package/dist/gateway/static/root/assets/workflows-page-BnjjKrM1.js +0 -27
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","names":[],"sources":["../../../../src/agent/workflow/runtime.ts"],"sourcesContent":["/**\n * Sandboxed workflow runtime.\n *\n * Parses the script via {@link parseWorkflowScript} (which strips the `meta`\n * export), wraps the remaining body in an async IIFE, and runs it inside a\n * Node `vm` context with a curated set of globals:\n *\n * - `agent(prompt, opts)` — spawns a subagent through the injected\n * {@link SubagentRunner} and returns its result (string, or schema-validated\n * object). Failures resolve to `null`.\n * - `parallel(thunks)` — concurrent fan-out; thunks (not promises!) so the\n * limiter sees each agent() call.\n * - `pipeline(items, ...stages)` — per-item sequential stages, items run\n * concurrently (no stage barrier). Each stage receives\n * `(prevResult, originalItem, index)`. A stage that throws drops that item\n * to `null` and skips remaining stages.\n * - `phase(title)` — marks the current phase; surfaces through `onPhase`.\n * - `log(message)` — appends to the workflow log.\n * - `budget` — `{ total, spent(), remaining() }` for self-pacing scripts.\n * - `args`, `cwd`, `process.cwd()`.\n *\n * The runtime is the only code that touches `vm`. It exposes no IO surface,\n * carries no LLM dependency, and is fully driven by injected callbacks — that\n * means the workflow tool, tests, and any future runner share one runtime.\n */\n\nimport { availableParallelism } from 'node:os';\nimport { createContext, Script } from 'node:vm';\n\nimport type { Api, Model } from '@earendil-works/pi-ai';\n\nimport { parseWorkflowScript } from './parser.js';\nimport type {\n AgentScriptOptions,\n SubagentRunner,\n WorkflowMetaPhase,\n WorkflowRunOptions,\n WorkflowRunResult,\n WorkflowSnapshot,\n WorkflowAgentStatus,\n} from './types.js';\n\nconst DEFAULT_CONCURRENCY_FLOOR = 1;\nconst DEFAULT_CONCURRENCY_CEILING = 16;\nconst DEFAULT_MAX_SUBAGENTS = 1000;\n\ninterface RuntimeState {\n currentPhase?: string;\n logs: string[];\n phases: string[];\n agentCount: number;\n spent: number;\n}\n\nexport interface RunWorkflowDeps {\n runner: SubagentRunner;\n /** Resolve a model ref (`provider/model` or configured typed id) to a {@link Model}. Throws on unknown ref. */\n resolveModelId?: (modelId: string) => Model<Api>;\n}\n\nexport async function runWorkflow<T = unknown>(\n script: string,\n deps: RunWorkflowDeps,\n options: WorkflowRunOptions,\n): Promise<WorkflowRunResult<T>> {\n const started = Date.now();\n const { meta, body } = parseWorkflowScript(script);\n\n const state: RuntimeState = { logs: [], phases: [], agentCount: 0, spent: 0 };\n const concurrency = clampConcurrency(options.concurrency);\n const maxSubagents = Math.max(1, options.maxSubagents ?? DEFAULT_MAX_SUBAGENTS);\n const limiter = createLimiter(concurrency);\n const pendingAgentRuns = new Set<Promise<unknown>>();\n const phaseDefaultModels = buildPhaseModelMap(meta.phases);\n\n const log = (message: unknown) => {\n const text = String(message);\n state.logs.push(text);\n options.onLog?.(text);\n };\n\n const phase = (title: unknown) => {\n const text = requireString(title, 'phase title');\n state.currentPhase = text;\n if (!state.phases.includes(text)) state.phases.push(text);\n options.onPhase?.(text);\n };\n\n const budget = Object.freeze({\n total: options.tokenBudget ?? null,\n spent: () => state.spent,\n remaining: () =>\n options.tokenBudget == null\n ? Number.POSITIVE_INFINITY\n : Math.max(0, options.tokenBudget - state.spent),\n });\n\n const throwIfAborted = () => {\n if (options.signal?.aborted) throw new Error('workflow aborted');\n };\n\n const resolveAgentModel = (\n normalized: AgentScriptOptions,\n assignedPhase: string | undefined,\n ): Model<Api> | undefined => {\n const realId = normalized.model?.trim();\n if (realId) {\n if (!deps.resolveModelId) {\n throw new Error('workflow runtime missing resolveModelId; cannot resolve real model id');\n }\n return deps.resolveModelId(realId);\n }\n if (assignedPhase) {\n const phaseRef = phaseDefaultModels.get(assignedPhase);\n if (phaseRef) {\n if (!deps.resolveModelId) return undefined;\n return deps.resolveModelId(phaseRef);\n }\n }\n return undefined;\n };\n\n const agent = async (prompt: unknown, agentOptions: unknown = {}) => {\n throwIfAborted();\n if (budget.total !== null && budget.remaining() <= 0) {\n throw new Error('workflow token budget exhausted');\n }\n if (state.agentCount >= maxSubagents) {\n throw new Error(`workflow agent quota exhausted (max ${maxSubagents})`);\n }\n\n const taskPrompt = requireString(prompt, 'agent prompt');\n const normalized = normalizeAgentOptions(agentOptions);\n const assignedPhase = normalized.phase ?? state.currentPhase;\n const requestedLabel = normalized.label?.trim();\n\n state.agentCount += 1;\n const id = state.agentCount;\n const label = requestedLabel || defaultAgentLabel(assignedPhase, id);\n options.onAgentQueued?.({ id, label, phase: assignedPhase, prompt: taskPrompt });\n\n const runPromise = limiter(async () => {\n options.onAgentStart?.({ id, label, phase: assignedPhase, prompt: taskPrompt });\n\n try {\n throwIfAborted();\n const resolvedModel = resolveAgentModel(normalized, assignedPhase);\n const enhanced = options.enhanceSubagentRun?.({\n id,\n label,\n phase: assignedPhase,\n prompt: taskPrompt,\n });\n const result = await deps.runner.run<unknown>(taskPrompt, {\n label,\n schema: normalized.schema,\n allowedToolNames: normalized.toolset,\n maxIterations: normalized.maxIterations,\n phase: assignedPhase,\n signal: options.signal,\n model: resolvedModel,\n ...enhanced,\n });\n throwIfAborted();\n\n const status: WorkflowAgentStatus = result === null ? 'error' : 'done';\n state.spent += estimateTokens(result);\n options.onAgentEnd?.({ id, label, phase: assignedPhase, result, status });\n return result;\n } catch (e) {\n if (options.signal?.aborted) {\n options.onAgentEnd?.({ id, label, phase: assignedPhase, result: null, status: 'skipped' });\n throw e;\n }\n const message = e instanceof Error ? e.message : String(e);\n log(`agent ${label} failed: ${message}`);\n options.onAgentEnd?.({ id, label, phase: assignedPhase, result: null, status: 'error' });\n return null;\n }\n });\n\n pendingAgentRuns.add(runPromise);\n // `then` (not `finally`) keeps the bookkeeping promise from re-throwing into\n // the unhandled-rejection channel when `runPromise` rejects on abort.\n runPromise.then(\n () => pendingAgentRuns.delete(runPromise),\n () => pendingAgentRuns.delete(runPromise),\n );\n return runPromise;\n };\n\n const parallel = async (thunks: unknown) => {\n throwIfAborted();\n if (!Array.isArray(thunks)) throw new TypeError('parallel() expects an array of functions');\n for (const t of thunks) {\n if (typeof t !== 'function') {\n throw new TypeError(\n 'parallel() expects an array of functions, not promises. Wrap each call: () => agent(...)',\n );\n }\n }\n return Promise.all(\n thunks.map(async (thunk, index) => {\n try {\n return await (thunk as () => unknown)();\n } catch (e) {\n if (options.signal?.aborted) throw e;\n const message = e instanceof Error ? e.message : String(e);\n log(`parallel[${index}] failed: ${message}`);\n return null;\n }\n }),\n );\n };\n\n const pipeline = async (items: unknown, ...stages: Array<unknown>) => {\n throwIfAborted();\n if (!Array.isArray(items)) {\n throw new TypeError('pipeline() expects an array as the first argument');\n }\n for (const stage of stages) {\n if (typeof stage !== 'function') {\n throw new TypeError(\n 'pipeline() stages must be functions: pipeline(items, item => ..., result => ...)',\n );\n }\n }\n const typedStages = stages as Array<\n (prev: unknown, original: unknown, index: number) => unknown\n >;\n return Promise.all(\n items.map(async (item, index) => {\n let value: unknown = item;\n for (const stage of typedStages) {\n try {\n throwIfAborted();\n value = await stage(value, item, index);\n throwIfAborted();\n } catch (e) {\n if (options.signal?.aborted) throw e;\n const message = e instanceof Error ? e.message : String(e);\n log(`pipeline[${index}] failed: ${message}`);\n return null;\n }\n }\n return value;\n }),\n );\n };\n\n const context = createContext({\n agent,\n parallel,\n pipeline,\n log,\n phase,\n args: options.args,\n cwd: options.cwd,\n process: Object.freeze({ cwd: () => options.cwd }),\n budget,\n console: {\n log,\n info: log,\n warn: (m: unknown) => log(`[warn] ${String(m)}`),\n error: (m: unknown) => log(`[error] ${String(m)}`),\n },\n JSON,\n Math,\n Array,\n Object,\n String,\n Number,\n Boolean,\n Set,\n Map,\n Promise,\n });\n\n const wrapped = `(async () => {\\n${body}\\n})()`;\n const script$ = new Script(wrapped, { filename: `${meta.name}.workflow.js` });\n\n let result: unknown;\n try {\n result = await script$.runInContext(context);\n // Wait for any agent() runs the script forgot to await before declaring success.\n await Promise.allSettled([...pendingAgentRuns]);\n } catch (e) {\n // Drain pending agent calls before propagating, so the snapshot reflects final state.\n await Promise.allSettled([...pendingAgentRuns]);\n throw rewriteScriptError(e);\n }\n\n assertStructuredCloneable(result, 'workflow result');\n\n return {\n meta,\n result: result as T,\n logs: state.logs,\n phases: state.phases,\n agentCount: state.agentCount,\n durationMs: Date.now() - started,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Initial snapshot helper — kept here so the runtime is the single source of\n// truth for \"what a fresh snapshot looks like for this workflow\".\n// ---------------------------------------------------------------------------\n\nexport function emptySnapshotFor(\n name: string,\n description?: string,\n phaseTitles?: string[],\n): WorkflowSnapshot {\n return {\n name,\n description,\n phases: phaseTitles ? [...phaseTitles] : [],\n logs: [],\n agents: [],\n agentCount: 0,\n runningCount: 0,\n doneCount: 0,\n errorCount: 0,\n skippedCount: 0,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\nfunction clampConcurrency(requested?: number): number {\n if (typeof requested === 'number' && Number.isFinite(requested) && requested >= 1) {\n return Math.min(Math.floor(requested), DEFAULT_CONCURRENCY_CEILING);\n }\n let cpu = DEFAULT_CONCURRENCY_CEILING;\n try {\n cpu = availableParallelism();\n } catch {\n // Some sandboxes throw; fall back to the ceiling.\n }\n return Math.max(DEFAULT_CONCURRENCY_FLOOR, Math.min(DEFAULT_CONCURRENCY_CEILING, cpu - 2));\n}\n\nfunction createLimiter(limit: number) {\n let active = 0;\n const queue: Array<() => void> = [];\n const next = () => {\n active -= 1;\n const resume = queue.shift();\n if (resume) resume();\n };\n return async <T>(fn: () => Promise<T>): Promise<T> => {\n if (active >= limit) {\n await new Promise<void>((resolve) => queue.push(resolve));\n }\n active += 1;\n try {\n return await fn();\n } finally {\n next();\n }\n };\n}\n\nfunction buildPhaseModelMap(phases: WorkflowMetaPhase[] | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!phases) return out;\n for (const p of phases) {\n if (p && typeof p.title === 'string' && typeof p.model === 'string' && p.model.trim()) {\n out.set(p.title, p.model.trim());\n }\n }\n return out;\n}\n\nfunction requireString(value: unknown, name: string): string {\n if (typeof value !== 'string') throw new TypeError(`${name} must be a string`);\n return value;\n}\n\nfunction optionalString(value: unknown, name: string): string | undefined {\n if (value === undefined) return undefined;\n return requireString(value, name);\n}\n\nfunction normalizeAgentOptions(value: unknown): AgentScriptOptions {\n if (value === undefined || value === null) return {};\n if (typeof value !== 'object') throw new TypeError('agent options must be an object');\n const options = value as AgentScriptOptions;\n const toolset = options.toolset;\n if (toolset !== undefined) {\n if (!Array.isArray(toolset) || toolset.some((t) => typeof t !== 'string')) {\n throw new TypeError('agent toolset must be an array of strings');\n }\n }\n const maxIterations = options.maxIterations;\n if (maxIterations !== undefined) {\n if (typeof maxIterations !== 'number' || !Number.isFinite(maxIterations) || maxIterations < 1) {\n throw new TypeError('agent maxIterations must be a positive number');\n }\n }\n const modelStr = optionalString(options.model, 'agent model');\n return {\n label: optionalString(options.label, 'agent label'),\n phase: optionalString(options.phase, 'agent phase'),\n schema: options.schema,\n model: modelStr,\n toolset,\n maxIterations,\n };\n}\n\nfunction assertStructuredCloneable(value: unknown, name: string): void {\n try {\n structuredClone(value);\n } catch (e) {\n const promisePath = findPromisePath(value, '');\n if (promisePath !== null) {\n throw new Error(\n `${name} contains a Promise at \\`${promisePath || '<root>'}\\` — missing 'await' before parallel()/pipeline()/agent(). ` +\n `Async return auto-unwraps a bare Promise, but a Promise nested in an object/array is returned as-is.`,\n );\n }\n const detail = e instanceof Error ? ` ${e.message}` : '';\n throw new Error(\n `${name} must be structured-cloneable; did you forget to await agent(), parallel(), or pipeline()?${detail}`,\n );\n }\n}\n\n/**\n * Find the first Promise lurking in `value`, returning a JS-path like\n * \"results[0].pending\". Returns null when no Promise is found. Bounded\n * depth/breadth so it never explodes on weird user shapes.\n */\nfunction findPromisePath(value: unknown, path: string, depth = 0): string | null {\n if (depth > 4) return null;\n if (isThenable(value)) return path;\n if (Array.isArray(value)) {\n for (let i = 0; i < Math.min(value.length, 32); i++) {\n const inner = findPromisePath(value[i], `${path}[${i}]`, depth + 1);\n if (inner !== null) return inner;\n }\n return null;\n }\n if (value !== null && typeof value === 'object') {\n let count = 0;\n for (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n if (count++ > 32) break;\n const inner = findPromisePath(child, path ? `${path}.${key}` : key, depth + 1);\n if (inner !== null) return inner;\n }\n }\n return null;\n}\n\nfunction isThenable(value: unknown): boolean {\n return (\n value !== null &&\n typeof value === 'object' &&\n typeof (value as { then?: unknown }).then === 'function'\n );\n}\n\n/**\n * Rewrite TypeErrors that look like \"called .map on a Promise\" with an\n * await-shaped hint. Static lint catches the obvious form at parse time;\n * this is the safety net for cases lint can't see (dynamic indirection,\n * values stashed in helper closures).\n */\nconst PROMISE_METHOD_ERROR =\n /\\.(map|filter|forEach|flat|flatMap|find|some|every|reduce|join|length|then)\\b is not a function/;\n\nfunction rewriteScriptError(error: unknown): Error {\n // VM-context errors aren't `instanceof` host classes (the vm has its own\n // global constructors), so check by name + duck-typing message.\n const asError = error as { name?: string; message?: string } | null | undefined;\n const isTypeError = asError?.name === 'TypeError' && typeof asError.message === 'string';\n if (!isTypeError) {\n return error instanceof Error ? error : new Error(String(error));\n }\n const message = (asError as { message: string }).message;\n if (!PROMISE_METHOD_ERROR.test(message)) {\n return error instanceof Error ? error : new Error(message);\n }\n return new Error(\n `${message}\\n\\nHint: parallel()/pipeline()/agent() return a Promise — 'await' the call before using its result.\\n` +\n ` ❌ const r = parallel(...); r.map(...)\\n` +\n ` ✅ const r = await parallel(...); r.map(...)`,\n );\n}\n\nfunction defaultAgentLabel(phase: string | undefined, index: number): string {\n return phase ? `${phase} agent ${index}` : `agent ${index}`;\n}\n\nfunction estimateTokens(value: unknown): number {\n if (value === null || value === undefined) return 0;\n try {\n return Math.ceil(JSON.stringify(value).length / 4);\n } catch {\n return Math.ceil(String(value).length / 4);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runtime.js","names":[],"sources":["../../../../src/agent/workflow/runtime.ts"],"sourcesContent":["/**\n * Sandboxed workflow runtime.\n *\n * Parses the script via {@link parseWorkflowScript} (which strips the `meta`\n * export), wraps the remaining body in an async IIFE, and runs it inside a\n * Node `vm` context with a curated set of globals:\n *\n * - `agent(prompt, opts)` — spawns a subagent through the injected\n * {@link SubagentRunner} and returns its result (string, or schema-validated\n * object). Failures resolve to `null`.\n * - `parallel(thunks)` — concurrent fan-out; thunks (not promises!) so the\n * limiter sees each agent() call.\n * - `pipeline(items, ...stages)` — per-item sequential stages, items run\n * concurrently (no stage barrier). Each stage receives\n * `(prevResult, originalItem, index)`. A stage that throws drops that item\n * to `null` and skips remaining stages.\n * - `phase(title)` — marks the current phase; surfaces through `onPhase`.\n * - `log(message)` — appends to the workflow log.\n * - `budget` — `{ total, spent(), remaining() }` for self-pacing scripts.\n * - `args`, `cwd`, `process.cwd()`.\n *\n * The runtime is the only code that touches `vm`. It exposes no IO surface,\n * carries no LLM dependency, and is fully driven by injected callbacks — that\n * means the workflow tool, tests, and any future runner share one runtime.\n */\n\nimport { availableParallelism } from 'node:os';\nimport { createContext, Script } from 'node:vm';\n\nimport type { Api, Model } from '@earendil-works/pi-ai';\n\nimport { parseWorkflowScript } from './parser.js';\nimport type {\n AgentScriptOptions,\n SubagentRunner,\n WorkflowMetaPhase,\n WorkflowRunOptions,\n WorkflowRunResult,\n WorkflowAgentStatus,\n} from './types.js';\n\nconst DEFAULT_CONCURRENCY_FLOOR = 1;\nconst DEFAULT_CONCURRENCY_CEILING = 16;\nconst DEFAULT_MAX_SUBAGENTS = 1000;\n\ninterface RuntimeState {\n currentPhase?: string;\n logs: string[];\n phases: string[];\n agentCount: number;\n spent: number;\n}\n\nexport interface RunWorkflowDeps {\n runner: SubagentRunner;\n /** Resolve a model ref (`provider/model` or configured typed id) to a {@link Model}. Throws on unknown ref. */\n resolveModelId?: (modelId: string) => Model<Api>;\n}\n\nexport async function runWorkflow<T = unknown>(\n script: string,\n deps: RunWorkflowDeps,\n options: WorkflowRunOptions,\n): Promise<WorkflowRunResult<T>> {\n const started = Date.now();\n const { meta, body } = parseWorkflowScript(script);\n\n const state: RuntimeState = { logs: [], phases: [], agentCount: 0, spent: 0 };\n const concurrency = clampConcurrency(options.concurrency);\n const maxSubagents = Math.max(1, options.maxSubagents ?? DEFAULT_MAX_SUBAGENTS);\n const limiter = createLimiter(concurrency);\n const pendingAgentRuns = new Set<Promise<unknown>>();\n const phaseDefaultModels = buildPhaseModelMap(meta.phases);\n\n const log = (message: unknown) => {\n const text = String(message);\n state.logs.push(text);\n options.onLog?.(text);\n };\n\n const phase = (title: unknown) => {\n const text = requireString(title, 'phase title');\n state.currentPhase = text;\n if (!state.phases.includes(text)) state.phases.push(text);\n options.onPhase?.(text);\n };\n\n const budget = Object.freeze({\n total: options.tokenBudget ?? null,\n spent: () => state.spent,\n remaining: () =>\n options.tokenBudget == null\n ? Number.POSITIVE_INFINITY\n : Math.max(0, options.tokenBudget - state.spent),\n });\n\n const throwIfAborted = () => {\n if (options.signal?.aborted) throw new Error('workflow aborted');\n };\n\n const resolveAgentModel = (\n normalized: AgentScriptOptions,\n assignedPhase: string | undefined,\n ): Model<Api> | undefined => {\n const realId = normalized.model?.trim();\n if (realId) {\n if (!deps.resolveModelId) {\n throw new Error('workflow runtime missing resolveModelId; cannot resolve real model id');\n }\n return deps.resolveModelId(realId);\n }\n if (assignedPhase) {\n const phaseRef = phaseDefaultModels.get(assignedPhase);\n if (phaseRef) {\n if (!deps.resolveModelId) return undefined;\n return deps.resolveModelId(phaseRef);\n }\n }\n return undefined;\n };\n\n const agent = async (prompt: unknown, agentOptions: unknown = {}) => {\n throwIfAborted();\n if (budget.total !== null && budget.remaining() <= 0) {\n throw new Error('workflow token budget exhausted');\n }\n if (state.agentCount >= maxSubagents) {\n throw new Error(`workflow agent quota exhausted (max ${maxSubagents})`);\n }\n\n const taskPrompt = requireString(prompt, 'agent prompt');\n const normalized = normalizeAgentOptions(agentOptions);\n const assignedPhase = normalized.phase ?? state.currentPhase;\n const requestedLabel = normalized.label?.trim();\n\n state.agentCount += 1;\n const id = state.agentCount;\n const label = requestedLabel || defaultAgentLabel(assignedPhase, id);\n options.onAgentQueued?.({ id, label, phase: assignedPhase, prompt: taskPrompt });\n\n const runPromise = limiter(async () => {\n options.onAgentStart?.({ id, label, phase: assignedPhase, prompt: taskPrompt });\n\n try {\n throwIfAborted();\n const resolvedModel = resolveAgentModel(normalized, assignedPhase);\n const enhanced = options.enhanceSubagentRun?.({\n id,\n label,\n phase: assignedPhase,\n prompt: taskPrompt,\n });\n const result = await deps.runner.run<unknown>(taskPrompt, {\n label,\n schema: normalized.schema,\n allowedToolNames: normalized.toolset,\n maxIterations: normalized.maxIterations,\n phase: assignedPhase,\n signal: options.signal,\n model: resolvedModel,\n ...enhanced,\n });\n throwIfAborted();\n\n const status: WorkflowAgentStatus = result === null ? 'error' : 'done';\n state.spent += estimateTokens(result);\n options.onAgentEnd?.({ id, label, phase: assignedPhase, result, status });\n return result;\n } catch (e) {\n if (options.signal?.aborted) {\n options.onAgentEnd?.({ id, label, phase: assignedPhase, result: null, status: 'skipped' });\n throw e;\n }\n const message = e instanceof Error ? e.message : String(e);\n log(`agent ${label} failed: ${message}`);\n options.onAgentEnd?.({ id, label, phase: assignedPhase, result: null, status: 'error' });\n return null;\n }\n });\n\n pendingAgentRuns.add(runPromise);\n // `then` (not `finally`) keeps the bookkeeping promise from re-throwing into\n // the unhandled-rejection channel when `runPromise` rejects on abort.\n runPromise.then(\n () => pendingAgentRuns.delete(runPromise),\n () => pendingAgentRuns.delete(runPromise),\n );\n return runPromise;\n };\n\n const parallel = async (thunks: unknown) => {\n throwIfAborted();\n if (!Array.isArray(thunks)) throw new TypeError('parallel() expects an array of functions');\n for (const t of thunks) {\n if (typeof t !== 'function') {\n throw new TypeError(\n 'parallel() expects an array of functions, not promises. Wrap each call: () => agent(...)',\n );\n }\n }\n return Promise.all(\n thunks.map(async (thunk, index) => {\n try {\n return await (thunk as () => unknown)();\n } catch (e) {\n if (options.signal?.aborted) throw e;\n const message = e instanceof Error ? e.message : String(e);\n log(`parallel[${index}] failed: ${message}`);\n return null;\n }\n }),\n );\n };\n\n const pipeline = async (items: unknown, ...stages: Array<unknown>) => {\n throwIfAborted();\n if (!Array.isArray(items)) {\n throw new TypeError('pipeline() expects an array as the first argument');\n }\n for (const stage of stages) {\n if (typeof stage !== 'function') {\n throw new TypeError(\n 'pipeline() stages must be functions: pipeline(items, item => ..., result => ...)',\n );\n }\n }\n const typedStages = stages as Array<\n (prev: unknown, original: unknown, index: number) => unknown\n >;\n return Promise.all(\n items.map(async (item, index) => {\n let value: unknown = item;\n for (const stage of typedStages) {\n try {\n throwIfAborted();\n value = await stage(value, item, index);\n throwIfAborted();\n } catch (e) {\n if (options.signal?.aborted) throw e;\n const message = e instanceof Error ? e.message : String(e);\n log(`pipeline[${index}] failed: ${message}`);\n return null;\n }\n }\n return value;\n }),\n );\n };\n\n const context = createContext({\n agent,\n parallel,\n pipeline,\n log,\n phase,\n args: options.args,\n cwd: options.cwd,\n process: Object.freeze({ cwd: () => options.cwd }),\n budget,\n console: {\n log,\n info: log,\n warn: (m: unknown) => log(`[warn] ${String(m)}`),\n error: (m: unknown) => log(`[error] ${String(m)}`),\n },\n JSON,\n Math,\n Array,\n Object,\n String,\n Number,\n Boolean,\n Set,\n Map,\n Promise,\n });\n\n const wrapped = `(async () => {\\n${body}\\n})()`;\n const script$ = new Script(wrapped, { filename: `${meta.name}.workflow.js` });\n\n let result: unknown;\n try {\n result = await script$.runInContext(context);\n // Wait for any agent() runs the script forgot to await before declaring success.\n await Promise.allSettled([...pendingAgentRuns]);\n } catch (e) {\n // Drain pending agent calls before propagating, so the snapshot reflects final state.\n await Promise.allSettled([...pendingAgentRuns]);\n throw rewriteScriptError(e);\n }\n\n assertStructuredCloneable(result, 'workflow result');\n\n return {\n meta,\n result: result as T,\n logs: state.logs,\n phases: state.phases,\n agentCount: state.agentCount,\n durationMs: Date.now() - started,\n };\n}\n\nexport { emptySnapshotFor } from './snapshot-empty.js';\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\nfunction clampConcurrency(requested?: number): number {\n if (typeof requested === 'number' && Number.isFinite(requested) && requested >= 1) {\n return Math.min(Math.floor(requested), DEFAULT_CONCURRENCY_CEILING);\n }\n let cpu = DEFAULT_CONCURRENCY_CEILING;\n try {\n cpu = availableParallelism();\n } catch {\n // Some sandboxes throw; fall back to the ceiling.\n }\n return Math.max(DEFAULT_CONCURRENCY_FLOOR, Math.min(DEFAULT_CONCURRENCY_CEILING, cpu - 2));\n}\n\nfunction createLimiter(limit: number) {\n let active = 0;\n const queue: Array<() => void> = [];\n const next = () => {\n active -= 1;\n const resume = queue.shift();\n if (resume) resume();\n };\n return async <T>(fn: () => Promise<T>): Promise<T> => {\n if (active >= limit) {\n await new Promise<void>((resolve) => queue.push(resolve));\n }\n active += 1;\n try {\n return await fn();\n } finally {\n next();\n }\n };\n}\n\nfunction buildPhaseModelMap(phases: WorkflowMetaPhase[] | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!phases) return out;\n for (const p of phases) {\n if (p && typeof p.title === 'string' && typeof p.model === 'string' && p.model.trim()) {\n out.set(p.title, p.model.trim());\n }\n }\n return out;\n}\n\nfunction requireString(value: unknown, name: string): string {\n if (typeof value !== 'string') throw new TypeError(`${name} must be a string`);\n return value;\n}\n\nfunction optionalString(value: unknown, name: string): string | undefined {\n if (value === undefined) return undefined;\n return requireString(value, name);\n}\n\nfunction normalizeAgentOptions(value: unknown): AgentScriptOptions {\n if (value === undefined || value === null) return {};\n if (typeof value !== 'object') throw new TypeError('agent options must be an object');\n const options = value as AgentScriptOptions;\n const toolset = options.toolset;\n if (toolset !== undefined) {\n if (!Array.isArray(toolset) || toolset.some((t) => typeof t !== 'string')) {\n throw new TypeError('agent toolset must be an array of strings');\n }\n }\n const maxIterations = options.maxIterations;\n if (maxIterations !== undefined) {\n if (typeof maxIterations !== 'number' || !Number.isFinite(maxIterations) || maxIterations < 1) {\n throw new TypeError('agent maxIterations must be a positive number');\n }\n }\n const modelStr = optionalString(options.model, 'agent model');\n return {\n label: optionalString(options.label, 'agent label'),\n phase: optionalString(options.phase, 'agent phase'),\n schema: options.schema,\n model: modelStr,\n toolset,\n maxIterations,\n };\n}\n\nfunction assertStructuredCloneable(value: unknown, name: string): void {\n try {\n structuredClone(value);\n } catch (e) {\n const promisePath = findPromisePath(value, '');\n if (promisePath !== null) {\n throw new Error(\n `${name} contains a Promise at \\`${promisePath || '<root>'}\\` — missing 'await' before parallel()/pipeline()/agent(). ` +\n `Async return auto-unwraps a bare Promise, but a Promise nested in an object/array is returned as-is.`,\n );\n }\n const detail = e instanceof Error ? ` ${e.message}` : '';\n throw new Error(\n `${name} must be structured-cloneable; did you forget to await agent(), parallel(), or pipeline()?${detail}`,\n );\n }\n}\n\n/**\n * Find the first Promise lurking in `value`, returning a JS-path like\n * \"results[0].pending\". Returns null when no Promise is found. Bounded\n * depth/breadth so it never explodes on weird user shapes.\n */\nfunction findPromisePath(value: unknown, path: string, depth = 0): string | null {\n if (depth > 4) return null;\n if (isThenable(value)) return path;\n if (Array.isArray(value)) {\n for (let i = 0; i < Math.min(value.length, 32); i++) {\n const inner = findPromisePath(value[i], `${path}[${i}]`, depth + 1);\n if (inner !== null) return inner;\n }\n return null;\n }\n if (value !== null && typeof value === 'object') {\n let count = 0;\n for (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n if (count++ > 32) break;\n const inner = findPromisePath(child, path ? `${path}.${key}` : key, depth + 1);\n if (inner !== null) return inner;\n }\n }\n return null;\n}\n\nfunction isThenable(value: unknown): boolean {\n return (\n value !== null &&\n typeof value === 'object' &&\n typeof (value as { then?: unknown }).then === 'function'\n );\n}\n\n/**\n * Rewrite TypeErrors that look like \"called .map on a Promise\" with an\n * await-shaped hint. Static lint catches the obvious form at parse time;\n * this is the safety net for cases lint can't see (dynamic indirection,\n * values stashed in helper closures).\n */\nconst PROMISE_METHOD_ERROR =\n /\\.(map|filter|forEach|flat|flatMap|find|some|every|reduce|join|length|then)\\b is not a function/;\n\nfunction rewriteScriptError(error: unknown): Error {\n // VM-context errors aren't `instanceof` host classes (the vm has its own\n // global constructors), so check by name + duck-typing message.\n const asError = error as { name?: string; message?: string } | null | undefined;\n const isTypeError = asError?.name === 'TypeError' && typeof asError.message === 'string';\n if (!isTypeError) {\n return error instanceof Error ? error : new Error(String(error));\n }\n const message = (asError as { message: string }).message;\n if (!PROMISE_METHOD_ERROR.test(message)) {\n return error instanceof Error ? error : new Error(message);\n }\n return new Error(\n `${message}\\n\\nHint: parallel()/pipeline()/agent() return a Promise — 'await' the call before using its result.\\n` +\n ` ❌ const r = parallel(...); r.map(...)\\n` +\n ` ✅ const r = await parallel(...); r.map(...)`,\n );\n}\n\nfunction defaultAgentLabel(phase: string | undefined, index: number): string {\n return phase ? `${phase} agent ${index}` : `agent ${index}`;\n}\n\nfunction estimateTokens(value: unknown): number {\n if (value === null || value === undefined) return 0;\n try {\n return Math.ceil(JSON.stringify(value).length / 4);\n } catch {\n return Math.ceil(String(value).length / 4);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { WorkflowSnapshot } from './types.js';
2
+ export declare function emptySnapshotFor(name: string, description?: string, phaseTitles?: string[]): WorkflowSnapshot;
@@ -0,0 +1,19 @@
1
+ //#region src/agent/workflow/snapshot-empty.ts
2
+ function emptySnapshotFor(name, description, phaseTitles) {
3
+ return {
4
+ name,
5
+ description,
6
+ phases: phaseTitles ? [...phaseTitles] : [],
7
+ logs: [],
8
+ agents: [],
9
+ agentCount: 0,
10
+ runningCount: 0,
11
+ doneCount: 0,
12
+ errorCount: 0,
13
+ skippedCount: 0
14
+ };
15
+ }
16
+ //#endregion
17
+ export { emptySnapshotFor };
18
+
19
+ //# sourceMappingURL=snapshot-empty.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-empty.js","names":[],"sources":["../../../../src/agent/workflow/snapshot-empty.ts"],"sourcesContent":["import type { WorkflowSnapshot } from './types.js';\n\nexport function emptySnapshotFor(\n name: string,\n description?: string,\n phaseTitles?: string[],\n): WorkflowSnapshot {\n return {\n name,\n description,\n phases: phaseTitles ? [...phaseTitles] : [],\n logs: [],\n agents: [],\n agentCount: 0,\n runningCount: 0,\n doneCount: 0,\n errorCount: 0,\n skippedCount: 0,\n };\n}\n"],"mappings":";AAEA,SAAgB,iBACd,MACA,aACA,aACkB;AAClB,QAAO;EACL;EACA;EACA,QAAQ,cAAc,CAAC,GAAG,YAAY,GAAG,EAAE;EAC3C,MAAM,EAAE;EACR,QAAQ,EAAE;EACV,YAAY;EACZ,cAAc;EACd,WAAW;EACX,YAAY;EACZ,cAAc;EACf"}
@@ -1,4 +1,4 @@
1
- import { emptySnapshotFor } from "./runtime.js";
1
+ import { emptySnapshotFor } from "./snapshot-empty.js";
2
2
  //#region src/agent/workflow/snapshot.ts
3
3
  function createWorkflowSnapshot(meta) {
4
4
  return emptySnapshotFor(meta.name, meta.description, meta.phases?.map((p) => p.title));
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.js","names":[],"sources":["../../../../src/agent/workflow/snapshot.ts"],"sourcesContent":["/**\n * Workflow progress snapshot model + text renderers.\n *\n * The runtime emits per-event callbacks (`onPhase`, `onAgentStart`, `onAgentEnd`,\n * `onLog`). The workflow tool wraps those callbacks to mutate a snapshot and push\n * a text rendering through `onUpdate` — keeping TUI / Gateway / IM consumers in\n * lock-step from a single source of truth.\n */\n\nimport type {\n WorkflowAgentSnapshot,\n WorkflowAgentStatus,\n WorkflowMeta,\n WorkflowSnapshot,\n} from './types.js';\nimport { emptySnapshotFor } from './runtime.js';\n\nexport function createWorkflowSnapshot(meta: WorkflowMeta): WorkflowSnapshot {\n return emptySnapshotFor(\n meta.name,\n meta.description,\n meta.phases?.map((p) => p.title),\n );\n}\n\nexport function recomputeCounts(snapshot: WorkflowSnapshot): void {\n let running = 0;\n let done = 0;\n let error = 0;\n let skipped = 0;\n for (const agent of snapshot.agents) {\n switch (agent.status) {\n case 'running':\n running++;\n break;\n case 'done':\n done++;\n break;\n case 'error':\n error++;\n break;\n case 'skipped':\n skipped++;\n break;\n }\n }\n snapshot.agentCount = snapshot.agents.length;\n snapshot.runningCount = running;\n snapshot.doneCount = done;\n snapshot.errorCount = error;\n snapshot.skippedCount = skipped;\n}\n\nexport interface RenderOptions {\n maxAgentsPerPhase?: number;\n maxLogs?: number;\n showResultPreviews?: boolean;\n}\n\nexport function renderWorkflowText(\n snapshot: WorkflowSnapshot,\n completed = false,\n options: RenderOptions = {},\n): string {\n const maxAgentsPerPhase = options.maxAgentsPerPhase ?? 6;\n const maxLogs = options.maxLogs ?? 2;\n const showPreviews = options.showResultPreviews ?? false;\n\n const state = stateSuffix(snapshot);\n const header = completed\n ? `◆ workflow ✓ ${snapshot.name} (${snapshot.doneCount}/${snapshot.agentCount} done${state})`\n : `◆ workflow: ${snapshot.name} (${snapshot.doneCount}/${snapshot.agentCount} done${state})`;\n const lines = [header];\n\n const phaseNames = uniqueOrdered([\n ...snapshot.phases,\n ...(snapshot.currentPhase ? [snapshot.currentPhase] : []),\n ...snapshot.agents.map((a) => a.phase).filter((p): p is string => Boolean(p)),\n ]);\n const rendered = new Set<WorkflowAgentSnapshot>();\n\n for (const phase of phaseNames) {\n const agents = snapshot.agents.filter((a) => a.phase === phase);\n if (agents.length === 0 && snapshot.currentPhase !== phase) continue;\n for (const a of agents) rendered.add(a);\n\n const counts = countAgents(agents);\n const completeHere = agents.length > 0 && counts.done + counts.error + counts.skipped === agents.length;\n const marker =\n counts.running > 0 || (!completeHere && snapshot.currentPhase === phase)\n ? '▶'\n : completeHere\n ? '✓'\n : ' ';\n\n const tail =\n (counts.running ? ` · ${counts.running} running` : '') +\n (counts.error ? ` · ${counts.error} errors` : '') +\n (counts.skipped ? ` · ${counts.skipped} skipped` : '');\n lines.push(` ${marker} ${phase} ${counts.done}/${agents.length}${tail}`);\n\n const visible = agents.slice(-maxAgentsPerPhase);\n for (const agent of visible) {\n const previewText = showPreviews && agent.resultPreview ? ` — ${agent.resultPreview}` : '';\n lines.push(\n ` #${agent.id} ${statusIcon(agent.status)} ${shorten(agent.label, 56)}${previewText}`,\n );\n }\n if (agents.length > visible.length) {\n lines.push(` … ${agents.length - visible.length} earlier agents`);\n }\n }\n\n const unphased = snapshot.agents.filter((a) => !rendered.has(a));\n if (unphased.length > 0) {\n lines.push(' unphased');\n for (const agent of unphased.slice(-maxAgentsPerPhase)) {\n const previewText = showPreviews && agent.resultPreview ? ` — ${agent.resultPreview}` : '';\n lines.push(\n ` #${agent.id} ${statusIcon(agent.status)} ${shorten(agent.label, 56)}${previewText}`,\n );\n }\n }\n\n const visibleLogs = snapshot.logs.slice(-maxLogs);\n if (visibleLogs.length > 0) {\n lines.push('');\n for (const log of visibleLogs) {\n lines.push(` log: ${shorten(log, 80)}`);\n }\n }\n return lines.join('\\n');\n}\n\nexport function previewValue(value: unknown, max = 80): string {\n if (value === null || value === undefined) return '';\n const text = typeof value === 'string' ? value : safeStringify(value);\n if (!text) return '';\n return text.length > max ? `${text.slice(0, max - 1)}…` : text;\n}\n\n// ---------------------------------------------------------------------------\n\nfunction countAgents(agents: WorkflowAgentSnapshot[]) {\n let done = 0;\n let running = 0;\n let error = 0;\n let skipped = 0;\n for (const a of agents) {\n if (a.status === 'done') done++;\n else if (a.status === 'running') running++;\n else if (a.status === 'error') error++;\n else if (a.status === 'skipped') skipped++;\n }\n return { done, running, error, skipped };\n}\n\nfunction statusIcon(status: WorkflowAgentStatus): string {\n switch (status) {\n case 'queued':\n return '○';\n case 'running':\n return '●';\n case 'done':\n return '✓';\n case 'error':\n return '✗';\n case 'skipped':\n return '-';\n }\n}\n\nfunction stateSuffix(snapshot: WorkflowSnapshot): string {\n if (snapshot.errorCount > 0) return `, ${snapshot.errorCount} errors`;\n if (snapshot.skippedCount > 0) return `, ${snapshot.skippedCount} skipped`;\n if (snapshot.runningCount > 0) return `, ${snapshot.runningCount} running`;\n return '';\n}\n\nfunction uniqueOrdered(values: string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const v of values) {\n if (!seen.has(v)) {\n seen.add(v);\n out.push(v);\n }\n }\n return out;\n}\n\nfunction shorten(value: string, max: number): string {\n const text = value.replace(/\\s+/g, ' ').trim();\n return text.length > max ? `${text.slice(0, max - 1)}…` : text;\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n"],"mappings":";;AAiBA,SAAgB,uBAAuB,MAAsC;AAC3E,QAAO,iBACL,KAAK,MACL,KAAK,aACL,KAAK,QAAQ,KAAK,MAAM,EAAE,MAAM,CACjC;;AAGH,SAAgB,gBAAgB,UAAkC;CAChE,IAAI,UAAU;CACd,IAAI,OAAO;CACX,IAAI,QAAQ;CACZ,IAAI,UAAU;AACd,MAAK,MAAM,SAAS,SAAS,OAC3B,SAAQ,MAAM,QAAd;EACE,KAAK;AACH;AACA;EACF,KAAK;AACH;AACA;EACF,KAAK;AACH;AACA;EACF,KAAK;AACH;AACA;;AAGN,UAAS,aAAa,SAAS,OAAO;AACtC,UAAS,eAAe;AACxB,UAAS,YAAY;AACrB,UAAS,aAAa;AACtB,UAAS,eAAe;;AAS1B,SAAgB,mBACd,UACA,YAAY,OACZ,UAAyB,EAAE,EACnB;CACR,MAAM,oBAAoB,QAAQ,qBAAqB;CACvD,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,eAAe,QAAQ,sBAAsB;CAEnD,MAAM,QAAQ,YAAY,SAAS;CAInC,MAAM,QAAQ,CAHC,YACX,gBAAgB,SAAS,KAAK,IAAI,SAAS,UAAU,GAAG,SAAS,WAAW,OAAO,MAAM,KACzF,eAAe,SAAS,KAAK,IAAI,SAAS,UAAU,GAAG,SAAS,WAAW,OAAO,MAAM,GACtE;CAEtB,MAAM,aAAa,cAAc;EAC/B,GAAG,SAAS;EACZ,GAAI,SAAS,eAAe,CAAC,SAAS,aAAa,GAAG,EAAE;EACxD,GAAG,SAAS,OAAO,KAAK,MAAM,EAAE,MAAM,CAAC,QAAQ,MAAmB,QAAQ,EAAE,CAAC;EAC9E,CAAC;CACF,MAAM,2BAAW,IAAI,KAA4B;AAEjD,MAAK,MAAM,SAAS,YAAY;EAC9B,MAAM,SAAS,SAAS,OAAO,QAAQ,MAAM,EAAE,UAAU,MAAM;AAC/D,MAAI,OAAO,WAAW,KAAK,SAAS,iBAAiB,MAAO;AAC5D,OAAK,MAAM,KAAK,OAAQ,UAAS,IAAI,EAAE;EAEvC,MAAM,SAAS,YAAY,OAAO;EAClC,MAAM,eAAe,OAAO,SAAS,KAAK,OAAO,OAAO,OAAO,QAAQ,OAAO,YAAY,OAAO;EACjG,MAAM,SACJ,OAAO,UAAU,KAAM,CAAC,gBAAgB,SAAS,iBAAiB,QAC9D,MACA,eACE,MACA;EAER,MAAM,QACH,OAAO,UAAU,MAAM,OAAO,QAAQ,YAAY,OAClD,OAAO,QAAQ,MAAM,OAAO,MAAM,WAAW,OAC7C,OAAO,UAAU,MAAM,OAAO,QAAQ,YAAY;AACrD,QAAM,KAAK,KAAK,OAAO,GAAG,MAAM,GAAG,OAAO,KAAK,GAAG,OAAO,SAAS,OAAO;EAEzE,MAAM,UAAU,OAAO,MAAM,CAAC,kBAAkB;AAChD,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,cAAc,gBAAgB,MAAM,gBAAgB,MAAM,MAAM,kBAAkB;AACxF,SAAM,KACJ,QAAQ,MAAM,GAAG,GAAG,WAAW,MAAM,OAAO,CAAC,GAAG,QAAQ,MAAM,OAAO,GAAG,GAAG,cAC5E;;AAEH,MAAI,OAAO,SAAS,QAAQ,OAC1B,OAAM,KAAK,SAAS,OAAO,SAAS,QAAQ,OAAO,iBAAiB;;CAIxE,MAAM,WAAW,SAAS,OAAO,QAAQ,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;AAChE,KAAI,SAAS,SAAS,GAAG;AACvB,QAAM,KAAK,aAAa;AACxB,OAAK,MAAM,SAAS,SAAS,MAAM,CAAC,kBAAkB,EAAE;GACtD,MAAM,cAAc,gBAAgB,MAAM,gBAAgB,MAAM,MAAM,kBAAkB;AACxF,SAAM,KACJ,QAAQ,MAAM,GAAG,GAAG,WAAW,MAAM,OAAO,CAAC,GAAG,QAAQ,MAAM,OAAO,GAAG,GAAG,cAC5E;;;CAIL,MAAM,cAAc,SAAS,KAAK,MAAM,CAAC,QAAQ;AACjD,KAAI,YAAY,SAAS,GAAG;AAC1B,QAAM,KAAK,GAAG;AACd,OAAK,MAAM,OAAO,YAChB,OAAM,KAAK,UAAU,QAAQ,KAAK,GAAG,GAAG;;AAG5C,QAAO,MAAM,KAAK,KAAK;;AAGzB,SAAgB,aAAa,OAAgB,MAAM,IAAY;AAC7D,KAAI,UAAU,QAAQ,UAAU,KAAA,EAAW,QAAO;CAClD,MAAM,OAAO,OAAO,UAAU,WAAW,QAAQ,cAAc,MAAM;AACrE,KAAI,CAAC,KAAM,QAAO;AAClB,QAAO,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK;;AAK5D,SAAS,YAAY,QAAiC;CACpD,IAAI,OAAO;CACX,IAAI,UAAU;CACd,IAAI,QAAQ;CACZ,IAAI,UAAU;AACd,MAAK,MAAM,KAAK,OACd,KAAI,EAAE,WAAW,OAAQ;UAChB,EAAE,WAAW,UAAW;UACxB,EAAE,WAAW,QAAS;UACtB,EAAE,WAAW,UAAW;AAEnC,QAAO;EAAE;EAAM;EAAS;EAAO;EAAS;;AAG1C,SAAS,WAAW,QAAqC;AACvD,SAAQ,QAAR;EACE,KAAK,SACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,KAAK,UACH,QAAO;;;AAIb,SAAS,YAAY,UAAoC;AACvD,KAAI,SAAS,aAAa,EAAG,QAAO,KAAK,SAAS,WAAW;AAC7D,KAAI,SAAS,eAAe,EAAG,QAAO,KAAK,SAAS,aAAa;AACjE,KAAI,SAAS,eAAe,EAAG,QAAO,KAAK,SAAS,aAAa;AACjE,QAAO;;AAGT,SAAS,cAAc,QAA4B;CACjD,MAAM,uBAAO,IAAI,KAAa;CAC9B,MAAM,MAAgB,EAAE;AACxB,MAAK,MAAM,KAAK,OACd,KAAI,CAAC,KAAK,IAAI,EAAE,EAAE;AAChB,OAAK,IAAI,EAAE;AACX,MAAI,KAAK,EAAE;;AAGf,QAAO;;AAGT,SAAS,QAAQ,OAAe,KAAqB;CACnD,MAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI,CAAC,MAAM;AAC9C,QAAO,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK;;AAG5D,SAAS,cAAc,OAAwB;AAC7C,KAAI;AACF,SAAO,KAAK,UAAU,MAAM;SACtB;AACN,SAAO,OAAO,MAAM"}
1
+ {"version":3,"file":"snapshot.js","names":[],"sources":["../../../../src/agent/workflow/snapshot.ts"],"sourcesContent":["/**\n * Workflow progress snapshot model + text renderers.\n *\n * The runtime emits per-event callbacks (`onPhase`, `onAgentStart`, `onAgentEnd`,\n * `onLog`). The workflow tool wraps those callbacks to mutate a snapshot and push\n * a text rendering through `onUpdate` — keeping TUI / Gateway / IM consumers in\n * lock-step from a single source of truth.\n */\n\nimport type {\n WorkflowAgentSnapshot,\n WorkflowAgentStatus,\n WorkflowMeta,\n WorkflowSnapshot,\n} from './types.js';\nimport { emptySnapshotFor } from './snapshot-empty.js';\n\nexport function createWorkflowSnapshot(meta: WorkflowMeta): WorkflowSnapshot {\n return emptySnapshotFor(\n meta.name,\n meta.description,\n meta.phases?.map((p) => p.title),\n );\n}\n\nexport function recomputeCounts(snapshot: WorkflowSnapshot): void {\n let running = 0;\n let done = 0;\n let error = 0;\n let skipped = 0;\n for (const agent of snapshot.agents) {\n switch (agent.status) {\n case 'running':\n running++;\n break;\n case 'done':\n done++;\n break;\n case 'error':\n error++;\n break;\n case 'skipped':\n skipped++;\n break;\n }\n }\n snapshot.agentCount = snapshot.agents.length;\n snapshot.runningCount = running;\n snapshot.doneCount = done;\n snapshot.errorCount = error;\n snapshot.skippedCount = skipped;\n}\n\nexport interface RenderOptions {\n maxAgentsPerPhase?: number;\n maxLogs?: number;\n showResultPreviews?: boolean;\n}\n\nexport function renderWorkflowText(\n snapshot: WorkflowSnapshot,\n completed = false,\n options: RenderOptions = {},\n): string {\n const maxAgentsPerPhase = options.maxAgentsPerPhase ?? 6;\n const maxLogs = options.maxLogs ?? 2;\n const showPreviews = options.showResultPreviews ?? false;\n\n const state = stateSuffix(snapshot);\n const header = completed\n ? `◆ workflow ✓ ${snapshot.name} (${snapshot.doneCount}/${snapshot.agentCount} done${state})`\n : `◆ workflow: ${snapshot.name} (${snapshot.doneCount}/${snapshot.agentCount} done${state})`;\n const lines = [header];\n\n const phaseNames = uniqueOrdered([\n ...snapshot.phases,\n ...(snapshot.currentPhase ? [snapshot.currentPhase] : []),\n ...snapshot.agents.map((a) => a.phase).filter((p): p is string => Boolean(p)),\n ]);\n const rendered = new Set<WorkflowAgentSnapshot>();\n\n for (const phase of phaseNames) {\n const agents = snapshot.agents.filter((a) => a.phase === phase);\n if (agents.length === 0 && snapshot.currentPhase !== phase) continue;\n for (const a of agents) rendered.add(a);\n\n const counts = countAgents(agents);\n const completeHere = agents.length > 0 && counts.done + counts.error + counts.skipped === agents.length;\n const marker =\n counts.running > 0 || (!completeHere && snapshot.currentPhase === phase)\n ? '▶'\n : completeHere\n ? '✓'\n : ' ';\n\n const tail =\n (counts.running ? ` · ${counts.running} running` : '') +\n (counts.error ? ` · ${counts.error} errors` : '') +\n (counts.skipped ? ` · ${counts.skipped} skipped` : '');\n lines.push(` ${marker} ${phase} ${counts.done}/${agents.length}${tail}`);\n\n const visible = agents.slice(-maxAgentsPerPhase);\n for (const agent of visible) {\n const previewText = showPreviews && agent.resultPreview ? ` — ${agent.resultPreview}` : '';\n lines.push(\n ` #${agent.id} ${statusIcon(agent.status)} ${shorten(agent.label, 56)}${previewText}`,\n );\n }\n if (agents.length > visible.length) {\n lines.push(` … ${agents.length - visible.length} earlier agents`);\n }\n }\n\n const unphased = snapshot.agents.filter((a) => !rendered.has(a));\n if (unphased.length > 0) {\n lines.push(' unphased');\n for (const agent of unphased.slice(-maxAgentsPerPhase)) {\n const previewText = showPreviews && agent.resultPreview ? ` — ${agent.resultPreview}` : '';\n lines.push(\n ` #${agent.id} ${statusIcon(agent.status)} ${shorten(agent.label, 56)}${previewText}`,\n );\n }\n }\n\n const visibleLogs = snapshot.logs.slice(-maxLogs);\n if (visibleLogs.length > 0) {\n lines.push('');\n for (const log of visibleLogs) {\n lines.push(` log: ${shorten(log, 80)}`);\n }\n }\n return lines.join('\\n');\n}\n\nexport function previewValue(value: unknown, max = 80): string {\n if (value === null || value === undefined) return '';\n const text = typeof value === 'string' ? value : safeStringify(value);\n if (!text) return '';\n return text.length > max ? `${text.slice(0, max - 1)}…` : text;\n}\n\n// ---------------------------------------------------------------------------\n\nfunction countAgents(agents: WorkflowAgentSnapshot[]) {\n let done = 0;\n let running = 0;\n let error = 0;\n let skipped = 0;\n for (const a of agents) {\n if (a.status === 'done') done++;\n else if (a.status === 'running') running++;\n else if (a.status === 'error') error++;\n else if (a.status === 'skipped') skipped++;\n }\n return { done, running, error, skipped };\n}\n\nfunction statusIcon(status: WorkflowAgentStatus): string {\n switch (status) {\n case 'queued':\n return '○';\n case 'running':\n return '●';\n case 'done':\n return '✓';\n case 'error':\n return '✗';\n case 'skipped':\n return '-';\n }\n}\n\nfunction stateSuffix(snapshot: WorkflowSnapshot): string {\n if (snapshot.errorCount > 0) return `, ${snapshot.errorCount} errors`;\n if (snapshot.skippedCount > 0) return `, ${snapshot.skippedCount} skipped`;\n if (snapshot.runningCount > 0) return `, ${snapshot.runningCount} running`;\n return '';\n}\n\nfunction uniqueOrdered(values: string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const v of values) {\n if (!seen.has(v)) {\n seen.add(v);\n out.push(v);\n }\n }\n return out;\n}\n\nfunction shorten(value: string, max: number): string {\n const text = value.replace(/\\s+/g, ' ').trim();\n return text.length > max ? `${text.slice(0, max - 1)}…` : text;\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
+ import { checkWebsiteBlocklist } from "../../agent/tools/url-safety.js";
1
2
  import { resolveBrowserCommandTimeoutMs } from "../browser-command-timeout.js";
2
3
  import { assertBrowserUrlAllowed, checkPostRedirectUrl, containsApiKeyPattern } from "../url-policy.js";
3
4
  import { truncateSnapshotAtBoundary } from "../snapshot-helpers.js";
4
- import { checkWebsiteBlocklist } from "../../agent/tools/url-safety.js";
5
5
  //#region src/browser/actions/extension-registry-bridge.ts
6
6
  /**
7
7
  * Runs browser registry actions against the Chrome Extension bridge (no Playwright page).
@@ -1,10 +1,10 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
+ import { checkWebsiteBlocklist } from "../../agent/tools/url-safety.js";
3
4
  import { resolveBrowserCommandTimeoutMs } from "../browser-command-timeout.js";
4
- import { humanizedClick, humanizedFill, humanizedScroll } from "../humanize.js";
5
5
  import { assertBrowserUrlAllowed, checkPostRedirectUrl, containsApiKeyPattern } from "../url-policy.js";
6
6
  import { truncateSnapshotAtBoundary } from "../snapshot-helpers.js";
7
- import { checkWebsiteBlocklist } from "../../agent/tools/url-safety.js";
7
+ import { humanizedClick, humanizedFill, humanizedScroll } from "../humanize.js";
8
8
  //#region src/browser/actions/registry.ts
9
9
  init_logger();
10
10
  const log = createLogger("BrowserActionRegistry");
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
- import { pickFreePort } from "./free-port.js";
4
3
  import { loadPlaywrightCoreModule } from "./providers/playwright-doctor.js";
4
+ import { pickFreePort } from "./free-port.js";
5
5
  import { existsSync } from "node:fs";
6
6
  import { mkdir, rm } from "node:fs/promises";
7
7
  import { join } from "node:path";
@@ -1,13 +1,13 @@
1
+ import { BrowserManager } from "./manager.js";
1
2
  import { resolveBrowserBackendFromConfig } from "./backend-from-config.js";
2
3
  import { resolveBrowserCommandTimeoutMs } from "./browser-command-timeout.js";
3
- import { CdpSupervisor } from "./cdp-supervisor.js";
4
- import { generateMousePath, generateScrollPlan, generateTypingPlan, humanizedClick, humanizedFill, humanizedPress, humanizedScroll, resolveHumanConfig } from "./humanize.js";
5
- import { BrowserManager } from "./manager.js";
6
4
  import { BrowserNotReadyError, buildBrowserSetupDeepLink, checkBrowserReadiness } from "./readiness.js";
7
5
  import { assertBrowserUrlAllowed, checkPostRedirectUrl, containsApiKeyPattern, isAlwaysBlockedUrl } from "./url-policy.js";
6
+ import { CdpSupervisor } from "./cdp-supervisor.js";
8
7
  import { snapshotSummaryHeader, truncateSnapshotAtBoundary } from "./snapshot-helpers.js";
9
8
  import { checkBotDetection, cleanupOrphanProcesses, startTracing, stopTracing } from "./session-lifecycle.js";
10
9
  import { BrowserBackSchema, BrowserCdpSchema, BrowserClickSchema, BrowserCloseSchema, BrowserConsoleSchema, BrowserDialogSchema, BrowserGetImagesSchema, BrowserNavigateSchema, BrowserPressSchema, BrowserScreenshotSchema, BrowserScrollSchema, BrowserSnapshotSchema, BrowserTypeSchema, BrowserVisionSchema } from "./schemas.js";
10
+ import { generateMousePath, generateScrollPlan, generateTypingPlan, humanizedClick, humanizedFill, humanizedPress, humanizedScroll, resolveHumanConfig } from "./humanize.js";
11
11
  import { WEBDRIVER_OVERRIDE_SCRIPT, buildLocalStealthArgs, buildStealthArgs, generateFingerprintSeed, makeExecutable, removeQuarantineAttr } from "./stealth.js";
12
12
  import { createBrowserActionRegistry } from "./actions/registry.js";
13
13
  import { parseBrowserPipeline } from "./pipeline/schema.js";
@@ -8,8 +8,8 @@ import { resolvePackageRoot } from "../../infra/update-check.js";
8
8
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
9
9
  import { readFile, readdir, rm } from "node:fs/promises";
10
10
  import { dirname, join } from "node:path";
11
- import { spawn } from "node:child_process";
12
11
  import { fileURLToPath } from "node:url";
12
+ import { spawn } from "node:child_process";
13
13
  //#region src/browser/providers/browser-ext-install.ts
14
14
  /**
15
15
  * Install bundled Chrome extension artifacts into {resolveBinDir()}/browser-ext/.
@@ -2,9 +2,9 @@ import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
3
  import { init_paths, resolveBinDir } from "../../config/paths.js";
4
4
  import { assertCacheDir, expandHome, init_cache_dir_policy } from "../cache-dir-policy.js";
5
- import { pickFreePort } from "../free-port.js";
6
5
  import { loadPlaywrightCoreModule } from "./playwright-doctor.js";
7
6
  import { WEBDRIVER_OVERRIDE_SCRIPT, buildStealthArgs, filterCloakBrowserExtraArgs, generateFingerprintSeed, makeExecutable, removeQuarantineAttr } from "../stealth.js";
7
+ import { pickFreePort } from "../free-port.js";
8
8
  import { createHash } from "node:crypto";
9
9
  import { createReadStream, createWriteStream } from "node:fs";
10
10
  import { mkdir, mkdtemp, rm, stat } from "node:fs/promises";
@@ -1,7 +1,7 @@
1
1
  import { init_loader, loadConfig } from "../../config/loader.js";
2
- import { resolveBrowserBackendFromConfig } from "../../browser/backend-from-config.js";
3
2
  import { loadPlaywrightCoreModule } from "../../browser/providers/playwright-doctor.js";
4
3
  import { BrowserManager } from "../../browser/manager.js";
4
+ import { resolveBrowserBackendFromConfig } from "../../browser/backend-from-config.js";
5
5
  import { createBrowserActionRegistry } from "../../browser/actions/registry.js";
6
6
  import { runBrowserPipeline, validateBrowserPipeline } from "../../browser/pipeline/runner.js";
7
7
  import { loadBrowserPipelineSource } from "../../browser/pipeline/source.js";
@@ -52,7 +52,7 @@ async function checkDatabaseSchema(ctx) {
52
52
  id: "database-schema",
53
53
  label: "Database schema",
54
54
  status: "pass",
55
- message: `SQLite schema v${status.dbVersion} (current release v4).`,
55
+ message: `SQLite schema v${status.dbVersion} (current release v5).`,
56
56
  hints: []
57
57
  };
58
58
  }
@@ -183,6 +183,7 @@ export declare function resolveSkillsCachePath(config: Config, agentId: string):
183
183
  * Resolve the bundled extensions directory (shipped with xopc).
184
184
  *
185
185
  * Layout-dependent candidates (first existing wins):
186
+ * - `XOPC_BUNDLED_EXTENSIONS_ROOT` (Electron packaged gateway override)
186
187
  * - `dist/src/config` → `dist/extensions` (npm / gateway)
187
188
  * - `src/config` → `extensions/` (dev, tsx)
188
189
  * - `out/server` → `dist/extensions` (Electron esbuild bundle)
@@ -237,11 +237,14 @@ function resolveSkillsCachePath(config, agentId) {
237
237
  * Resolve the bundled extensions directory (shipped with xopc).
238
238
  *
239
239
  * Layout-dependent candidates (first existing wins):
240
+ * - `XOPC_BUNDLED_EXTENSIONS_ROOT` (Electron packaged gateway override)
240
241
  * - `dist/src/config` → `dist/extensions` (npm / gateway)
241
242
  * - `src/config` → `extensions/` (dev, tsx)
242
243
  * - `out/server` → `dist/extensions` (Electron esbuild bundle)
243
244
  */
244
245
  function resolveBundledExtensionsDir() {
246
+ const envDir = process.env[ENV_BUNDLED_EXTENSIONS_ROOT];
247
+ if (envDir && existsSync(envDir)) return envDir;
245
248
  try {
246
249
  const srcDir = dirname(fileURLToPath(import.meta.url));
247
250
  const candidates = [
@@ -287,11 +290,12 @@ function resolveExtensionSdkPath() {
287
290
  return join(process.cwd(), "src", "extensions", "sdk", "index.ts");
288
291
  }
289
292
  }
290
- var FILENAMES, WORKSPACE_FILES;
293
+ var ENV_BUNDLED_EXTENSIONS_ROOT, FILENAMES, WORKSPACE_FILES;
291
294
  var init_paths = __esmMin((() => {
292
295
  init_agent_scope();
293
296
  init_paths_state();
294
297
  init_workspace_defaults();
298
+ ENV_BUNDLED_EXTENSIONS_ROOT = "XOPC_BUNDLED_EXTENSIONS_ROOT";
295
299
  FILENAMES = {
296
300
  CONFIG: "xopc.json",
297
301
  MODELS_JSON: "models.json",
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","names":["resolveAgentDirScoped","resolveAgentHomeScoped","resolveAgentProfileDirScoped","resolveAgentProfileMarkdownPathScoped"],"sources":["../../../src/config/paths.ts"],"sourcesContent":["import { join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { dirname } from 'path';\nimport { existsSync } from 'fs';\n\nimport type { Config } from './schema.js';\nimport {\n resolveAgentDir as resolveAgentDirScoped,\n resolveAgentHomeDir as resolveAgentHomeScoped,\n resolveAgentProfileDir as resolveAgentProfileDirScoped,\n resolveAgentProfileMarkdownPath as resolveAgentProfileMarkdownPathScoped,\n resolveAgentWorkspaceDir,\n} from '../agent/agent-scope.js';\nimport { ENV_VARS, resolveStateDir } from './paths-state.js';\n\nexport { ENV_VARS, resolveHomeDir, resolveStateDir, resolveXopcDatabasePath, XOPC_DB_FILENAME } from './paths-state.js';\nexport { resolveDefaultAgentWorkspaceDir } from './workspace-defaults.js';\nexport {\n resolveAgentWorkspaceDir,\n resolveAgentDir as resolveAgentDirFromConfig,\n resolveAgentHomeDir as resolveAgentHomeDirFromConfig,\n} from '../agent/agent-scope.js';\n\n// ============================================\n// File Names\n// ============================================\nexport const FILENAMES = {\n CONFIG: 'xopc.json',\n MODELS_JSON: 'models.json',\n /** Primary SQLite state database (`~/.xopc/xopc.db`). */\n XOPC_DB: 'xopc.db',\n EXTENSIONS_LOCK: 'extensions-lock.json',\n CREDENTIALS_PROFILES: 'auth-profiles.json',\n CRON_JOBS: 'jobs.json',\n WORKSPACE_STATE: 'workspace.json',\n SKILLS_CACHE: 'skills-cache.json',\n /** Hub / CLI install provenance for ~/.xopc/skills/<id>. */\n SKILLS_LOCK: 'skills-lock.json',\n PID: 'pid',\n STATUS: 'status.json',\n SOCKET: 'agent.sock',\n} as const;\n\n// ============================================\n// Workspace Files\n// ============================================\nexport const WORKSPACE_FILES = {\n SOUL: 'SOUL.md',\n IDENTITY: 'IDENTITY.md',\n USER: 'USER.md',\n AGENTS: 'AGENTS.md',\n TOOLS: 'TOOLS.md',\n HEARTBEAT: 'HEARTBEAT.md',\n MEMORY: 'MEMORY.md',\n} as const;\n\n// ============================================\n// Path Resolution Functions\n// ============================================\n\n/**\n * Resolve the main config file path\n */\nexport function resolveConfigPath(): string {\n return process.env[ENV_VARS.CONFIG_PATH] ?? join(resolveStateDir(), FILENAMES.CONFIG);\n}\n\n/**\n * Resolve the credentials directory\n */\nexport function resolveCredentialsDir(): string {\n return process.env[ENV_VARS.CREDENTIALS_DIR] ?? join(resolveStateDir(), 'credentials');\n}\n\n/**\n * Resolve the global auth-profiles.json path\n */\nexport function resolveAuthProfilesPath(): string {\n return join(resolveCredentialsDir(), FILENAMES.CREDENTIALS_PROFILES);\n}\n\n/**\n * Resolve OAuth token file path for a provider\n */\nexport function resolveOAuthPath(provider: string): string {\n return join(resolveCredentialsDir(), 'oauth', `${provider}.json`);\n}\n\n/**\n * Internal agent state dir: `stateDir/agents/<id>/agent/`\n * (credentials, inbox IPC, pid, agent.json — not the Markdown workspace).\n */\nexport function resolveAgentDir(config: Config, agentId: string): string {\n return resolveAgentDirScoped(config, agentId);\n}\n\n/**\n * Per-agent home: `stateDir/agents/<id>/` (sessions + `agent/`).\n */\nexport function resolveAgentHomeDir(config: Config, agentId: string): string {\n return resolveAgentHomeScoped(config, agentId);\n}\n\n/** Agent profile Markdown root: `stateDir/agents/<id>/profile/`. */\nexport function resolveAgentProfileDir(config: Config, agentId: string): string {\n return resolveAgentProfileDirScoped(config, agentId);\n}\n\n/** Single file under {@link resolveAgentProfileDir} (basename only). */\nexport function resolveAgentProfileMarkdownPath(config: Config, agentId: string, filename: string): string {\n return resolveAgentProfileMarkdownPathScoped(config, agentId, filename);\n}\n\n/**\n * Resolve a profile system Markdown path (SOUL.md, …) under the agent `profile/` directory.\n */\nexport function resolveWorkspaceFile(config: Config, filename: string, agentId: string): string {\n return join(resolveAgentProfileDirScoped(config, agentId), filename);\n}\n\n/**\n * OpenClaw-aligned: per-agent auth-profiles.json directly under agent dir (no credentials subdirectory).\n */\nexport function resolveAgentAuthProfilesPath(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), FILENAMES.CREDENTIALS_PROFILES);\n}\n\n/**\n * Resolve the inbox directory for an agent\n */\nexport function resolveInboxDir(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), 'inbox');\n}\n\n/**\n * Resolve the pending inbox directory\n */\nexport function resolveInboxPendingDir(config: Config, agentId: string): string {\n return join(resolveInboxDir(config, agentId), 'pending');\n}\n\n/**\n * Resolve the processed inbox directory\n */\nexport function resolveInboxProcessedDir(config: Config, agentId: string): string {\n return join(resolveInboxDir(config, agentId), 'processed');\n}\n\n/**\n * Resolve a specific inbox message path\n */\nexport function resolveInboxMessagePath(\n config: Config,\n messageId: string,\n pending: boolean,\n agentId: string,\n): string {\n const dir = pending ? resolveInboxPendingDir(config, agentId) : resolveInboxProcessedDir(config, agentId);\n return join(dir, `${messageId}.json`);\n}\n\n/**\n * Resolve the pid file path\n */\nexport function resolvePidPath(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), FILENAMES.PID);\n}\n\n/**\n * Resolve the status.json path\n */\nexport function resolveStatusPath(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), FILENAMES.STATUS);\n}\n\n/**\n * Resolve the Unix socket path\n */\nexport function resolveSocketPath(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), FILENAMES.SOCKET);\n}\n\n/**\n * Resolve the extensions directory (global)\n */\nexport function resolveExtensionsDir(): string {\n return join(resolveStateDir(), 'extensions');\n}\n\n/**\n * Resolve the extensions lockfile path\n */\nexport function resolveExtensionsLockPath(): string {\n return join(resolveExtensionsDir(), FILENAMES.EXTENSIONS_LOCK);\n}\n\n/**\n * Per-agent extensions directory (`…/agents/<id>/agent/extensions/`) — used for discovery of\n * legacy or manually placed copies. CLI, web store, and `extensions dev` symlink installs use\n * {@link resolveExtensionsDir} (`~/.xopc/extensions`) only.\n */\nexport function resolveWorkspaceExtensionsDir(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), 'extensions');\n}\n\n/**\n * Resolve the skills directory (global)\n */\nexport function resolveSkillsDir(): string {\n return join(resolveStateDir(), 'skills');\n}\n\n/**\n * Resolve a specific skill path\n */\nexport function resolveSkillPath(skillId: string): string {\n return join(resolveSkillsDir(), skillId, 'SKILL.md');\n}\n\n/**\n * Skills hub lock file (~/.xopc/skills-lock.json): install source + content hash per managed skill id.\n */\nexport function resolveSkillsLockPath(): string {\n return join(resolveStateDir(), FILENAMES.SKILLS_LOCK);\n}\n\n/**\n * Resolve the cron directory\n */\nexport function resolveCronDir(): string {\n return join(resolveStateDir(), 'cron');\n}\n\n/**\n * Resolve the cron jobs file path\n */\nexport function resolveCronJobsPath(): string {\n return join(resolveCronDir(), FILENAMES.CRON_JOBS);\n}\n\n/**\n * Resolve the logs directory\n */\nexport function resolveLogsDir(): string {\n return process.env[ENV_VARS.LOG_DIR] ?? join(resolveStateDir(), 'logs');\n}\n\n/**\n * Resolve a specific log file path\n */\nexport function resolveLogPath(date: string): string {\n return join(resolveLogsDir(), `xopc-${date}.log`);\n}\n\n/**\n * Resolve the bin directory\n */\nexport function resolveBinDir(): string {\n return join(resolveStateDir(), 'bin');\n}\n\n/**\n * Resolve the xopc CLI path\n */\nexport function resolveXopcBinPath(): string {\n return join(resolveBinDir(), 'xopc');\n}\n\n/**\n * Resolve the tools directory\n */\nexport function resolveToolsDir(): string {\n return join(resolveStateDir(), 'tools');\n}\n\n/**\n * Resolve the Node.js tools directory\n */\nexport function resolveNodeToolsDir(): string {\n return join(resolveToolsDir(), 'node');\n}\n\n/**\n * Resolve the current Node.js bin directory\n */\nexport function resolveNodeBinDir(): string {\n return join(resolveNodeToolsDir(), 'current', 'bin');\n}\n\n/**\n * Resolve the node binary path\n */\nexport function resolveNodeBinPath(): string {\n return join(resolveNodeBinDir(), 'node');\n}\n\n/**\n * Resolve the npm binary path\n */\nexport function resolveNpmBinPath(): string {\n return join(resolveNodeBinDir(), 'npm');\n}\n\n/**\n * Resolve the models.json path\n */\nexport function resolveModelsJsonPath(): string {\n return join(resolveStateDir(), FILENAMES.MODELS_JSON);\n}\n\n/**\n * OpenClaw-aligned: workspace setup state directory (`<workspace>/.xopc/`).\n */\nexport function resolveWorkspaceStateDir(config: Config, agentId: string): string {\n return join(resolveAgentWorkspaceDir(config, agentId), '.xopc');\n}\n\n/**\n * OpenClaw-aligned: workspace setup state file (`<workspace>/.xopc/workspace.json`).\n */\nexport function resolveWorkspaceStatePath(config: Config, agentId: string): string {\n return join(resolveWorkspaceStateDir(config, agentId), FILENAMES.WORKSPACE_STATE);\n}\n\n/**\n * Resolve the skills cache file path (internal agent state, under agent dir).\n */\nexport function resolveSkillsCachePath(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), 'state', FILENAMES.SKILLS_CACHE);\n}\n\n/**\n * Resolve the bundled extensions directory (shipped with xopc).\n *\n * Layout-dependent candidates (first existing wins):\n * - `dist/src/config` → `dist/extensions` (npm / gateway)\n * - `src/config` → `extensions/` (dev, tsx)\n * - `out/server` → `dist/extensions` (Electron esbuild bundle)\n */\nexport function resolveBundledExtensionsDir(): string | null {\n try {\n const currentFile = fileURLToPath(import.meta.url);\n const srcDir = dirname(currentFile);\n const candidates = [\n join(srcDir, '..', '..', 'extensions'),\n join(srcDir, '..', '..', 'dist', 'extensions'),\n join(srcDir, '..', '..', '..', 'extensions'),\n ];\n for (const dir of candidates) {\n if (existsSync(dir)) {\n return dir;\n }\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the bundled skills directory (shipped with xopc)\n */\nexport function resolveBundledSkillsDir(): string | null {\n try {\n const currentFile = fileURLToPath(import.meta.url);\n const srcDir = dirname(currentFile);\n \n // Production (npm): dist/config/paths.js -> ../../ -> package root -> skills\n const packageRoot = join(srcDir, '..', '..');\n const skillsDir = join(packageRoot, 'skills');\n if (existsSync(skillsDir)) {\n return skillsDir;\n }\n \n // Development (source): src/config/paths.js -> ../../../ -> package root -> skills\n const devPackageRoot = join(srcDir, '..', '..', '..');\n const devSkillsDir = join(devPackageRoot, 'skills');\n if (existsSync(devSkillsDir)) {\n return devSkillsDir;\n }\n \n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Path to extension-sdk entry (for jiti / extension loader aliases).\n */\nexport function resolveExtensionSdkPath(): string {\n try {\n const currentFile = fileURLToPath(import.meta.url);\n const srcDir = dirname(currentFile);\n const adjacent = join(srcDir, '..', 'extensions', 'sdk', 'index.ts');\n if (existsSync(adjacent)) {\n return adjacent;\n }\n const fromPackageRoot = join(srcDir, '..', '..', 'src', 'extensions', 'sdk', 'index.ts');\n if (existsSync(fromPackageRoot)) {\n return fromPackageRoot;\n }\n const cwd = join(process.cwd(), 'src', 'extensions', 'sdk', 'index.ts');\n if (existsSync(cwd)) {\n return cwd;\n }\n return adjacent;\n } catch {\n return join(process.cwd(), 'src', 'extensions', 'sdk', 'index.ts');\n }\n}\n\n// Re-export existsSync for bundled paths\nexport { existsSync };\n"],"mappings":"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"}
1
+ {"version":3,"file":"paths.js","names":["resolveAgentDirScoped","resolveAgentHomeScoped","resolveAgentProfileDirScoped","resolveAgentProfileMarkdownPathScoped"],"sources":["../../../src/config/paths.ts"],"sourcesContent":["import { join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { dirname } from 'path';\nimport { existsSync } from 'fs';\n\nimport type { Config } from './schema.js';\nimport {\n resolveAgentDir as resolveAgentDirScoped,\n resolveAgentHomeDir as resolveAgentHomeScoped,\n resolveAgentProfileDir as resolveAgentProfileDirScoped,\n resolveAgentProfileMarkdownPath as resolveAgentProfileMarkdownPathScoped,\n resolveAgentWorkspaceDir,\n} from '../agent/agent-scope.js';\nimport { ENV_VARS, resolveStateDir } from './paths-state.js';\n\nexport { ENV_VARS, resolveHomeDir, resolveStateDir, resolveXopcDatabasePath, XOPC_DB_FILENAME } from './paths-state.js';\nexport { resolveDefaultAgentWorkspaceDir } from './workspace-defaults.js';\nexport {\n resolveAgentWorkspaceDir,\n resolveAgentDir as resolveAgentDirFromConfig,\n resolveAgentHomeDir as resolveAgentHomeDirFromConfig,\n} from '../agent/agent-scope.js';\n\n// ============================================\n// File Names\n// ============================================\nconst ENV_BUNDLED_EXTENSIONS_ROOT = 'XOPC_BUNDLED_EXTENSIONS_ROOT';\n\nexport const FILENAMES = {\n CONFIG: 'xopc.json',\n MODELS_JSON: 'models.json',\n /** Primary SQLite state database (`~/.xopc/xopc.db`). */\n XOPC_DB: 'xopc.db',\n EXTENSIONS_LOCK: 'extensions-lock.json',\n CREDENTIALS_PROFILES: 'auth-profiles.json',\n CRON_JOBS: 'jobs.json',\n WORKSPACE_STATE: 'workspace.json',\n SKILLS_CACHE: 'skills-cache.json',\n /** Hub / CLI install provenance for ~/.xopc/skills/<id>. */\n SKILLS_LOCK: 'skills-lock.json',\n PID: 'pid',\n STATUS: 'status.json',\n SOCKET: 'agent.sock',\n} as const;\n\n// ============================================\n// Workspace Files\n// ============================================\nexport const WORKSPACE_FILES = {\n SOUL: 'SOUL.md',\n IDENTITY: 'IDENTITY.md',\n USER: 'USER.md',\n AGENTS: 'AGENTS.md',\n TOOLS: 'TOOLS.md',\n HEARTBEAT: 'HEARTBEAT.md',\n MEMORY: 'MEMORY.md',\n} as const;\n\n// ============================================\n// Path Resolution Functions\n// ============================================\n\n/**\n * Resolve the main config file path\n */\nexport function resolveConfigPath(): string {\n return process.env[ENV_VARS.CONFIG_PATH] ?? join(resolveStateDir(), FILENAMES.CONFIG);\n}\n\n/**\n * Resolve the credentials directory\n */\nexport function resolveCredentialsDir(): string {\n return process.env[ENV_VARS.CREDENTIALS_DIR] ?? join(resolveStateDir(), 'credentials');\n}\n\n/**\n * Resolve the global auth-profiles.json path\n */\nexport function resolveAuthProfilesPath(): string {\n return join(resolveCredentialsDir(), FILENAMES.CREDENTIALS_PROFILES);\n}\n\n/**\n * Resolve OAuth token file path for a provider\n */\nexport function resolveOAuthPath(provider: string): string {\n return join(resolveCredentialsDir(), 'oauth', `${provider}.json`);\n}\n\n/**\n * Internal agent state dir: `stateDir/agents/<id>/agent/`\n * (credentials, inbox IPC, pid, agent.json — not the Markdown workspace).\n */\nexport function resolveAgentDir(config: Config, agentId: string): string {\n return resolveAgentDirScoped(config, agentId);\n}\n\n/**\n * Per-agent home: `stateDir/agents/<id>/` (sessions + `agent/`).\n */\nexport function resolveAgentHomeDir(config: Config, agentId: string): string {\n return resolveAgentHomeScoped(config, agentId);\n}\n\n/** Agent profile Markdown root: `stateDir/agents/<id>/profile/`. */\nexport function resolveAgentProfileDir(config: Config, agentId: string): string {\n return resolveAgentProfileDirScoped(config, agentId);\n}\n\n/** Single file under {@link resolveAgentProfileDir} (basename only). */\nexport function resolveAgentProfileMarkdownPath(config: Config, agentId: string, filename: string): string {\n return resolveAgentProfileMarkdownPathScoped(config, agentId, filename);\n}\n\n/**\n * Resolve a profile system Markdown path (SOUL.md, …) under the agent `profile/` directory.\n */\nexport function resolveWorkspaceFile(config: Config, filename: string, agentId: string): string {\n return join(resolveAgentProfileDirScoped(config, agentId), filename);\n}\n\n/**\n * OpenClaw-aligned: per-agent auth-profiles.json directly under agent dir (no credentials subdirectory).\n */\nexport function resolveAgentAuthProfilesPath(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), FILENAMES.CREDENTIALS_PROFILES);\n}\n\n/**\n * Resolve the inbox directory for an agent\n */\nexport function resolveInboxDir(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), 'inbox');\n}\n\n/**\n * Resolve the pending inbox directory\n */\nexport function resolveInboxPendingDir(config: Config, agentId: string): string {\n return join(resolveInboxDir(config, agentId), 'pending');\n}\n\n/**\n * Resolve the processed inbox directory\n */\nexport function resolveInboxProcessedDir(config: Config, agentId: string): string {\n return join(resolveInboxDir(config, agentId), 'processed');\n}\n\n/**\n * Resolve a specific inbox message path\n */\nexport function resolveInboxMessagePath(\n config: Config,\n messageId: string,\n pending: boolean,\n agentId: string,\n): string {\n const dir = pending ? resolveInboxPendingDir(config, agentId) : resolveInboxProcessedDir(config, agentId);\n return join(dir, `${messageId}.json`);\n}\n\n/**\n * Resolve the pid file path\n */\nexport function resolvePidPath(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), FILENAMES.PID);\n}\n\n/**\n * Resolve the status.json path\n */\nexport function resolveStatusPath(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), FILENAMES.STATUS);\n}\n\n/**\n * Resolve the Unix socket path\n */\nexport function resolveSocketPath(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), FILENAMES.SOCKET);\n}\n\n/**\n * Resolve the extensions directory (global)\n */\nexport function resolveExtensionsDir(): string {\n return join(resolveStateDir(), 'extensions');\n}\n\n/**\n * Resolve the extensions lockfile path\n */\nexport function resolveExtensionsLockPath(): string {\n return join(resolveExtensionsDir(), FILENAMES.EXTENSIONS_LOCK);\n}\n\n/**\n * Per-agent extensions directory (`…/agents/<id>/agent/extensions/`) — used for discovery of\n * legacy or manually placed copies. CLI, web store, and `extensions dev` symlink installs use\n * {@link resolveExtensionsDir} (`~/.xopc/extensions`) only.\n */\nexport function resolveWorkspaceExtensionsDir(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), 'extensions');\n}\n\n/**\n * Resolve the skills directory (global)\n */\nexport function resolveSkillsDir(): string {\n return join(resolveStateDir(), 'skills');\n}\n\n/**\n * Resolve a specific skill path\n */\nexport function resolveSkillPath(skillId: string): string {\n return join(resolveSkillsDir(), skillId, 'SKILL.md');\n}\n\n/**\n * Skills hub lock file (~/.xopc/skills-lock.json): install source + content hash per managed skill id.\n */\nexport function resolveSkillsLockPath(): string {\n return join(resolveStateDir(), FILENAMES.SKILLS_LOCK);\n}\n\n/**\n * Resolve the cron directory\n */\nexport function resolveCronDir(): string {\n return join(resolveStateDir(), 'cron');\n}\n\n/**\n * Resolve the cron jobs file path\n */\nexport function resolveCronJobsPath(): string {\n return join(resolveCronDir(), FILENAMES.CRON_JOBS);\n}\n\n/**\n * Resolve the logs directory\n */\nexport function resolveLogsDir(): string {\n return process.env[ENV_VARS.LOG_DIR] ?? join(resolveStateDir(), 'logs');\n}\n\n/**\n * Resolve a specific log file path\n */\nexport function resolveLogPath(date: string): string {\n return join(resolveLogsDir(), `xopc-${date}.log`);\n}\n\n/**\n * Resolve the bin directory\n */\nexport function resolveBinDir(): string {\n return join(resolveStateDir(), 'bin');\n}\n\n/**\n * Resolve the xopc CLI path\n */\nexport function resolveXopcBinPath(): string {\n return join(resolveBinDir(), 'xopc');\n}\n\n/**\n * Resolve the tools directory\n */\nexport function resolveToolsDir(): string {\n return join(resolveStateDir(), 'tools');\n}\n\n/**\n * Resolve the Node.js tools directory\n */\nexport function resolveNodeToolsDir(): string {\n return join(resolveToolsDir(), 'node');\n}\n\n/**\n * Resolve the current Node.js bin directory\n */\nexport function resolveNodeBinDir(): string {\n return join(resolveNodeToolsDir(), 'current', 'bin');\n}\n\n/**\n * Resolve the node binary path\n */\nexport function resolveNodeBinPath(): string {\n return join(resolveNodeBinDir(), 'node');\n}\n\n/**\n * Resolve the npm binary path\n */\nexport function resolveNpmBinPath(): string {\n return join(resolveNodeBinDir(), 'npm');\n}\n\n/**\n * Resolve the models.json path\n */\nexport function resolveModelsJsonPath(): string {\n return join(resolveStateDir(), FILENAMES.MODELS_JSON);\n}\n\n/**\n * OpenClaw-aligned: workspace setup state directory (`<workspace>/.xopc/`).\n */\nexport function resolveWorkspaceStateDir(config: Config, agentId: string): string {\n return join(resolveAgentWorkspaceDir(config, agentId), '.xopc');\n}\n\n/**\n * OpenClaw-aligned: workspace setup state file (`<workspace>/.xopc/workspace.json`).\n */\nexport function resolveWorkspaceStatePath(config: Config, agentId: string): string {\n return join(resolveWorkspaceStateDir(config, agentId), FILENAMES.WORKSPACE_STATE);\n}\n\n/**\n * Resolve the skills cache file path (internal agent state, under agent dir).\n */\nexport function resolveSkillsCachePath(config: Config, agentId: string): string {\n return join(resolveAgentDir(config, agentId), 'state', FILENAMES.SKILLS_CACHE);\n}\n\n/**\n * Resolve the bundled extensions directory (shipped with xopc).\n *\n * Layout-dependent candidates (first existing wins):\n * - `XOPC_BUNDLED_EXTENSIONS_ROOT` (Electron packaged gateway override)\n * - `dist/src/config` → `dist/extensions` (npm / gateway)\n * - `src/config` → `extensions/` (dev, tsx)\n * - `out/server` → `dist/extensions` (Electron esbuild bundle)\n */\nexport function resolveBundledExtensionsDir(): string | null {\n const envDir = process.env[ENV_BUNDLED_EXTENSIONS_ROOT];\n if (envDir && existsSync(envDir)) {\n return envDir;\n }\n\n try {\n const currentFile = fileURLToPath(import.meta.url);\n const srcDir = dirname(currentFile);\n const candidates = [\n join(srcDir, '..', '..', 'extensions'),\n join(srcDir, '..', '..', 'dist', 'extensions'),\n join(srcDir, '..', '..', '..', 'extensions'),\n ];\n for (const dir of candidates) {\n if (existsSync(dir)) {\n return dir;\n }\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the bundled skills directory (shipped with xopc)\n */\nexport function resolveBundledSkillsDir(): string | null {\n try {\n const currentFile = fileURLToPath(import.meta.url);\n const srcDir = dirname(currentFile);\n \n // Production (npm): dist/config/paths.js -> ../../ -> package root -> skills\n const packageRoot = join(srcDir, '..', '..');\n const skillsDir = join(packageRoot, 'skills');\n if (existsSync(skillsDir)) {\n return skillsDir;\n }\n \n // Development (source): src/config/paths.js -> ../../../ -> package root -> skills\n const devPackageRoot = join(srcDir, '..', '..', '..');\n const devSkillsDir = join(devPackageRoot, 'skills');\n if (existsSync(devSkillsDir)) {\n return devSkillsDir;\n }\n \n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Path to extension-sdk entry (for jiti / extension loader aliases).\n */\nexport function resolveExtensionSdkPath(): string {\n try {\n const currentFile = fileURLToPath(import.meta.url);\n const srcDir = dirname(currentFile);\n const adjacent = join(srcDir, '..', 'extensions', 'sdk', 'index.ts');\n if (existsSync(adjacent)) {\n return adjacent;\n }\n const fromPackageRoot = join(srcDir, '..', '..', 'src', 'extensions', 'sdk', 'index.ts');\n if (existsSync(fromPackageRoot)) {\n return fromPackageRoot;\n }\n const cwd = join(process.cwd(), 'src', 'extensions', 'sdk', 'index.ts');\n if (existsSync(cwd)) {\n return cwd;\n }\n return adjacent;\n } catch {\n return join(process.cwd(), 'src', 'extensions', 'sdk', 'index.ts');\n }\n}\n\n// Re-export existsSync for bundled paths\nexport { existsSync };\n"],"mappings":";;;;;;;;;;;AAiEA,SAAgB,oBAA4B;AAC1C,QAAO,QAAQ,IAAI,SAAS,gBAAgB,KAAK,iBAAiB,EAAE,UAAU,OAAO;;;;;AAMvF,SAAgB,wBAAgC;AAC9C,QAAO,QAAQ,IAAI,SAAS,oBAAoB,KAAK,iBAAiB,EAAE,cAAc;;;;;AAMxF,SAAgB,0BAAkC;AAChD,QAAO,KAAK,uBAAuB,EAAE,UAAU,qBAAqB;;;;;AAMtE,SAAgB,iBAAiB,UAA0B;AACzD,QAAO,KAAK,uBAAuB,EAAE,SAAS,GAAG,SAAS,OAAO;;;;;;AAOnE,SAAgB,gBAAgB,QAAgB,SAAyB;AACvE,QAAOA,kBAAsB,QAAQ,QAAQ;;;;;AAM/C,SAAgB,oBAAoB,QAAgB,SAAyB;AAC3E,QAAOC,sBAAuB,QAAQ,QAAQ;;;AAIhD,SAAgB,uBAAuB,QAAgB,SAAyB;AAC9E,QAAOC,yBAA6B,QAAQ,QAAQ;;;AAItD,SAAgB,gCAAgC,QAAgB,SAAiB,UAA0B;AACzG,QAAOC,kCAAsC,QAAQ,SAAS,SAAS;;;;;AAMzE,SAAgB,qBAAqB,QAAgB,UAAkB,SAAyB;AAC9F,QAAO,KAAKD,yBAA6B,QAAQ,QAAQ,EAAE,SAAS;;;;;AAMtE,SAAgB,6BAA6B,QAAgB,SAAyB;AACpF,QAAO,KAAK,gBAAgB,QAAQ,QAAQ,EAAE,UAAU,qBAAqB;;;;;AAM/E,SAAgB,gBAAgB,QAAgB,SAAyB;AACvE,QAAO,KAAK,gBAAgB,QAAQ,QAAQ,EAAE,QAAQ;;;;;AAMxD,SAAgB,uBAAuB,QAAgB,SAAyB;AAC9E,QAAO,KAAK,gBAAgB,QAAQ,QAAQ,EAAE,UAAU;;;;;AAM1D,SAAgB,yBAAyB,QAAgB,SAAyB;AAChF,QAAO,KAAK,gBAAgB,QAAQ,QAAQ,EAAE,YAAY;;;;;AAM5D,SAAgB,wBACd,QACA,WACA,SACA,SACQ;AAER,QAAO,KADK,UAAU,uBAAuB,QAAQ,QAAQ,GAAG,yBAAyB,QAAQ,QAAQ,EACxF,GAAG,UAAU,OAAO;;;;;AAMvC,SAAgB,eAAe,QAAgB,SAAyB;AACtE,QAAO,KAAK,gBAAgB,QAAQ,QAAQ,EAAE,UAAU,IAAI;;;;;AAM9D,SAAgB,kBAAkB,QAAgB,SAAyB;AACzE,QAAO,KAAK,gBAAgB,QAAQ,QAAQ,EAAE,UAAU,OAAO;;;;;AAMjE,SAAgB,kBAAkB,QAAgB,SAAyB;AACzE,QAAO,KAAK,gBAAgB,QAAQ,QAAQ,EAAE,UAAU,OAAO;;;;;AAMjE,SAAgB,uBAA+B;AAC7C,QAAO,KAAK,iBAAiB,EAAE,aAAa;;;;;AAM9C,SAAgB,4BAAoC;AAClD,QAAO,KAAK,sBAAsB,EAAE,UAAU,gBAAgB;;;;;;;AAQhE,SAAgB,8BAA8B,QAAgB,SAAyB;AACrF,QAAO,KAAK,gBAAgB,QAAQ,QAAQ,EAAE,aAAa;;;;;AAM7D,SAAgB,mBAA2B;AACzC,QAAO,KAAK,iBAAiB,EAAE,SAAS;;;;;AAM1C,SAAgB,iBAAiB,SAAyB;AACxD,QAAO,KAAK,kBAAkB,EAAE,SAAS,WAAW;;;;;AAMtD,SAAgB,wBAAgC;AAC9C,QAAO,KAAK,iBAAiB,EAAE,UAAU,YAAY;;;;;AAMvD,SAAgB,iBAAyB;AACvC,QAAO,KAAK,iBAAiB,EAAE,OAAO;;;;;AAMxC,SAAgB,sBAA8B;AAC5C,QAAO,KAAK,gBAAgB,EAAE,UAAU,UAAU;;;;;AAMpD,SAAgB,iBAAyB;AACvC,QAAO,QAAQ,IAAI,SAAS,YAAY,KAAK,iBAAiB,EAAE,OAAO;;;;;AAMzE,SAAgB,eAAe,MAAsB;AACnD,QAAO,KAAK,gBAAgB,EAAE,QAAQ,KAAK,MAAM;;;;;AAMnD,SAAgB,gBAAwB;AACtC,QAAO,KAAK,iBAAiB,EAAE,MAAM;;;;;AAMvC,SAAgB,qBAA6B;AAC3C,QAAO,KAAK,eAAe,EAAE,OAAO;;;;;AAMtC,SAAgB,kBAA0B;AACxC,QAAO,KAAK,iBAAiB,EAAE,QAAQ;;;;;AAMzC,SAAgB,sBAA8B;AAC5C,QAAO,KAAK,iBAAiB,EAAE,OAAO;;;;;AAMxC,SAAgB,oBAA4B;AAC1C,QAAO,KAAK,qBAAqB,EAAE,WAAW,MAAM;;;;;AAMtD,SAAgB,qBAA6B;AAC3C,QAAO,KAAK,mBAAmB,EAAE,OAAO;;;;;AAM1C,SAAgB,oBAA4B;AAC1C,QAAO,KAAK,mBAAmB,EAAE,MAAM;;;;;AAMzC,SAAgB,wBAAgC;AAC9C,QAAO,KAAK,iBAAiB,EAAE,UAAU,YAAY;;;;;AAMvD,SAAgB,yBAAyB,QAAgB,SAAyB;AAChF,QAAO,KAAK,yBAAyB,QAAQ,QAAQ,EAAE,QAAQ;;;;;AAMjE,SAAgB,0BAA0B,QAAgB,SAAyB;AACjF,QAAO,KAAK,yBAAyB,QAAQ,QAAQ,EAAE,UAAU,gBAAgB;;;;;AAMnF,SAAgB,uBAAuB,QAAgB,SAAyB;AAC9E,QAAO,KAAK,gBAAgB,QAAQ,QAAQ,EAAE,SAAS,UAAU,aAAa;;;;;;;;;;;AAYhF,SAAgB,8BAA6C;CAC3D,MAAM,SAAS,QAAQ,IAAI;AAC3B,KAAI,UAAU,WAAW,OAAO,CAC9B,QAAO;AAGT,KAAI;EAEF,MAAM,SAAS,QADK,cAAc,OAAO,KAAK,IACZ,CAAC;EACnC,MAAM,aAAa;GACjB,KAAK,QAAQ,MAAM,MAAM,aAAa;GACtC,KAAK,QAAQ,MAAM,MAAM,QAAQ,aAAa;GAC9C,KAAK,QAAQ,MAAM,MAAM,MAAM,aAAa;GAC7C;AACD,OAAK,MAAM,OAAO,WAChB,KAAI,WAAW,IAAI,CACjB,QAAO;AAGX,SAAO;SACD;AACN,SAAO;;;;;;AAOX,SAAgB,0BAAyC;AACvD,KAAI;EAEF,MAAM,SAAS,QADK,cAAc,OAAO,KAAK,IACZ,CAAC;EAInC,MAAM,YAAY,KADE,KAAK,QAAQ,MAAM,KACL,EAAE,SAAS;AAC7C,MAAI,WAAW,UAAU,CACvB,QAAO;EAKT,MAAM,eAAe,KADE,KAAK,QAAQ,MAAM,MAAM,KACR,EAAE,SAAS;AACnD,MAAI,WAAW,aAAa,CAC1B,QAAO;AAGT,SAAO;SACD;AACN,SAAO;;;;;;AAOX,SAAgB,0BAAkC;AAChD,KAAI;EAEF,MAAM,SAAS,QADK,cAAc,OAAO,KAAK,IACZ,CAAC;EACnC,MAAM,WAAW,KAAK,QAAQ,MAAM,cAAc,OAAO,WAAW;AACpE,MAAI,WAAW,SAAS,CACtB,QAAO;EAET,MAAM,kBAAkB,KAAK,QAAQ,MAAM,MAAM,OAAO,cAAc,OAAO,WAAW;AACxF,MAAI,WAAW,gBAAgB,CAC7B,QAAO;EAET,MAAM,MAAM,KAAK,QAAQ,KAAK,EAAE,OAAO,cAAc,OAAO,WAAW;AACvE,MAAI,WAAW,IAAI,CACjB,QAAO;AAET,SAAO;SACD;AACN,SAAO,KAAK,QAAQ,KAAK,EAAE,OAAO,cAAc,OAAO,WAAW;;;;;mBApZrC;mBAC4B;0BAGa;AAUpE,+BAA8B;AAEvB,aAAY;EACvB,QAAQ;EACR,aAAa;;EAEb,SAAS;EACT,iBAAiB;EACjB,sBAAsB;EACtB,WAAW;EACX,iBAAiB;EACjB,cAAc;;EAEd,aAAa;EACb,KAAK;EACL,QAAQ;EACR,QAAQ;EACT;AAKY,mBAAkB;EAC7B,MAAM;EACN,UAAU;EACV,MAAM;EACN,QAAQ;EACR,OAAO;EACP,WAAW;EACX,QAAQ;EACT"}
@@ -1,6 +1,6 @@
1
- import { getConnectorInstance } from "./instances.js";
2
1
  import { createBundleMcpToolRuntime, mapBundleMcpToolsForGateway } from "../agent/mcp/bundle-mcp-materialize.js";
3
2
  import { getWorkspacePath } from "../config/workspace-path-helpers.js";
3
+ import { getConnectorInstance } from "./instances.js";
4
4
  //#region src/connectors/health.ts
5
5
  function classifyConnectorHealthError(error) {
6
6
  const message = error instanceof Error ? error.message : String(error);
@@ -1,6 +1,6 @@
1
1
  import { CredentialResolver, init_credentials } from "../auth/credentials.js";
2
- import { getConnectorDefinition } from "./catalog.js";
3
2
  import { saveConnectorSecrets } from "./secret-store.js";
3
+ import { getConnectorDefinition } from "./catalog.js";
4
4
  import { isManagedConnectorServer, materializeConnectorMcpServer } from "./materialize.js";
5
5
  import { appendConnectorAuditRecord } from "./usage.js";
6
6
  import { listConnectorInstances } from "./instances.js";
@@ -0,0 +1,13 @@
1
+ export type DocumentExtractionResult = {
2
+ ok: true;
3
+ text: string;
4
+ kind: 'plain' | 'pdf';
5
+ } | {
6
+ ok: false;
7
+ reason: string;
8
+ };
9
+ export declare function extractDocumentText(params: {
10
+ buffer: Buffer;
11
+ fileName: string;
12
+ mimeType?: string;
13
+ }): DocumentExtractionResult;
@@ -0,0 +1,63 @@
1
+ import { extname } from "node:path";
2
+ //#region src/document-understanding/extract.ts
3
+ const MAX_EXTRACTED_CHARS = 6e4;
4
+ function truncate(text) {
5
+ return text.length > MAX_EXTRACTED_CHARS ? `${text.slice(0, MAX_EXTRACTED_CHARS)}\n\n[document extraction truncated ${text.length - MAX_EXTRACTED_CHARS} chars]` : text;
6
+ }
7
+ function decodePdfEscapedString(input) {
8
+ return input.replace(/\\n/g, "\n").replace(/\\r/g, "\n").replace(/\\t/g, " ").replace(/\\\(/g, "(").replace(/\\\)/g, ")").replace(/\\\\/g, "\\");
9
+ }
10
+ function extractPdfLiteralStrings(raw) {
11
+ const out = [];
12
+ for (const match of raw.matchAll(/\((?:\\.|[^\\()])*\)\s*T[Jj]/g)) {
13
+ const token = match[0];
14
+ const text = decodePdfEscapedString(token.slice(1, token.lastIndexOf(")"))).trim();
15
+ if (text.length >= 2) out.push(text);
16
+ }
17
+ return out;
18
+ }
19
+ function extractPdfHexStrings(raw) {
20
+ const out = [];
21
+ for (const match of raw.matchAll(/<([0-9a-fA-F\s]{6,})>\s*T[Jj]/g)) {
22
+ const hex = (match[1] ?? "").replace(/\s+/g, "");
23
+ if (hex.length < 6 || hex.length % 2 !== 0) continue;
24
+ const bytes = [];
25
+ for (let i = 0; i < hex.length; i += 2) bytes.push(Number.parseInt(hex.slice(i, i + 2), 16));
26
+ const buf = Buffer.from(bytes);
27
+ const text = (buf.length >= 2 && (buf[0] === 254 && buf[1] === 255 || buf[0] === 255 && buf[1] === 254) ? buf.toString("utf16le") : buf.toString("utf8")).replace(/[\u0000-\u001f]+/g, " ").trim();
28
+ if (text.length >= 2) out.push(text);
29
+ }
30
+ return out;
31
+ }
32
+ function extractPdfText(buffer) {
33
+ const raw = buffer.toString("latin1");
34
+ const parts = [...extractPdfLiteralStrings(raw), ...extractPdfHexStrings(raw)].map((line) => line.replace(/\s+/g, " ").trim()).filter(Boolean);
35
+ const text = Array.from(new Set(parts)).join("\n");
36
+ if (!text.trim()) return {
37
+ ok: false,
38
+ reason: "PDF text extraction found no text; scanned or compressed PDFs require a dedicated extractor."
39
+ };
40
+ return {
41
+ ok: true,
42
+ kind: "pdf",
43
+ text: truncate(text)
44
+ };
45
+ }
46
+ function extractDocumentText(params) {
47
+ const ext = extname(params.fileName).toLowerCase();
48
+ const mime = params.mimeType?.toLowerCase() ?? "";
49
+ if (mime === "application/pdf" || ext === ".pdf") return extractPdfText(params.buffer);
50
+ if (mime.startsWith("text/")) return {
51
+ ok: true,
52
+ kind: "plain",
53
+ text: truncate(params.buffer.toString("utf8"))
54
+ };
55
+ return {
56
+ ok: false,
57
+ reason: `No document extractor registered for ${params.mimeType || ext || "unknown type"}.`
58
+ };
59
+ }
60
+ //#endregion
61
+ export { extractDocumentText };
62
+
63
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","names":[],"sources":["../../../src/document-understanding/extract.ts"],"sourcesContent":["import { extname } from 'node:path';\n\nexport type DocumentExtractionResult =\n | { ok: true; text: string; kind: 'plain' | 'pdf' }\n | { ok: false; reason: string };\n\nconst MAX_EXTRACTED_CHARS = 60_000;\n\nfunction truncate(text: string): string {\n return text.length > MAX_EXTRACTED_CHARS\n ? `${text.slice(0, MAX_EXTRACTED_CHARS)}\\n\\n[document extraction truncated ${text.length - MAX_EXTRACTED_CHARS} chars]`\n : text;\n}\n\nfunction decodePdfEscapedString(input: string): string {\n return input\n .replace(/\\\\n/g, '\\n')\n .replace(/\\\\r/g, '\\n')\n .replace(/\\\\t/g, '\\t')\n .replace(/\\\\\\(/g, '(')\n .replace(/\\\\\\)/g, ')')\n .replace(/\\\\\\\\/g, '\\\\');\n}\n\nfunction extractPdfLiteralStrings(raw: string): string[] {\n const out: string[] = [];\n const re = /\\((?:\\\\.|[^\\\\()])*\\)\\s*T[Jj]/g;\n for (const match of raw.matchAll(re)) {\n const token = match[0];\n const body = token.slice(1, token.lastIndexOf(')'));\n const text = decodePdfEscapedString(body).trim();\n if (text.length >= 2) out.push(text);\n }\n return out;\n}\n\nfunction extractPdfHexStrings(raw: string): string[] {\n const out: string[] = [];\n const re = /<([0-9a-fA-F\\s]{6,})>\\s*T[Jj]/g;\n for (const match of raw.matchAll(re)) {\n const hex = (match[1] ?? '').replace(/\\s+/g, '');\n if (hex.length < 6 || hex.length % 2 !== 0) continue;\n const bytes: number[] = [];\n for (let i = 0; i < hex.length; i += 2) bytes.push(Number.parseInt(hex.slice(i, i + 2), 16));\n const buf = Buffer.from(bytes);\n const utf16 = buf.length >= 2 && ((buf[0] === 0xfe && buf[1] === 0xff) || (buf[0] === 0xff && buf[1] === 0xfe));\n const text = (utf16 ? buf.toString('utf16le') : buf.toString('utf8')).replace(/[\\u0000-\\u001f]+/g, ' ').trim();\n if (text.length >= 2) out.push(text);\n }\n return out;\n}\n\nfunction extractPdfText(buffer: Buffer): DocumentExtractionResult {\n const raw = buffer.toString('latin1');\n const parts = [...extractPdfLiteralStrings(raw), ...extractPdfHexStrings(raw)]\n .map((line) => line.replace(/\\s+/g, ' ').trim())\n .filter(Boolean);\n const text = Array.from(new Set(parts)).join('\\n');\n if (!text.trim()) {\n return { ok: false, reason: 'PDF text extraction found no text; scanned or compressed PDFs require a dedicated extractor.' };\n }\n return { ok: true, kind: 'pdf', text: truncate(text) };\n}\n\nexport function extractDocumentText(params: {\n buffer: Buffer;\n fileName: string;\n mimeType?: string;\n}): DocumentExtractionResult {\n const ext = extname(params.fileName).toLowerCase();\n const mime = params.mimeType?.toLowerCase() ?? '';\n if (mime === 'application/pdf' || ext === '.pdf') return extractPdfText(params.buffer);\n if (mime.startsWith('text/')) return { ok: true, kind: 'plain', text: truncate(params.buffer.toString('utf8')) };\n return { ok: false, reason: `No document extractor registered for ${params.mimeType || ext || 'unknown type'}.` };\n}\n"],"mappings":";;AAMA,MAAM,sBAAsB;AAE5B,SAAS,SAAS,MAAsB;AACtC,QAAO,KAAK,SAAS,sBACjB,GAAG,KAAK,MAAM,GAAG,oBAAoB,CAAC,qCAAqC,KAAK,SAAS,oBAAoB,WAC7G;;AAGN,SAAS,uBAAuB,OAAuB;AACrD,QAAO,MACJ,QAAQ,QAAQ,KAAK,CACrB,QAAQ,QAAQ,KAAK,CACrB,QAAQ,QAAQ,IAAK,CACrB,QAAQ,SAAS,IAAI,CACrB,QAAQ,SAAS,IAAI,CACrB,QAAQ,SAAS,KAAK;;AAG3B,SAAS,yBAAyB,KAAuB;CACvD,MAAM,MAAgB,EAAE;AAExB,MAAK,MAAM,SAAS,IAAI,SAAS,gCAAG,EAAE;EACpC,MAAM,QAAQ,MAAM;EAEpB,MAAM,OAAO,uBADA,MAAM,MAAM,GAAG,MAAM,YAAY,IAAI,CACV,CAAC,CAAC,MAAM;AAChD,MAAI,KAAK,UAAU,EAAG,KAAI,KAAK,KAAK;;AAEtC,QAAO;;AAGT,SAAS,qBAAqB,KAAuB;CACnD,MAAM,MAAgB,EAAE;AAExB,MAAK,MAAM,SAAS,IAAI,SAAS,iCAAG,EAAE;EACpC,MAAM,OAAO,MAAM,MAAM,IAAI,QAAQ,QAAQ,GAAG;AAChD,MAAI,IAAI,SAAS,KAAK,IAAI,SAAS,MAAM,EAAG;EAC5C,MAAM,QAAkB,EAAE;AAC1B,OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,EAAG,OAAM,KAAK,OAAO,SAAS,IAAI,MAAM,GAAG,IAAI,EAAE,EAAE,GAAG,CAAC;EAC5F,MAAM,MAAM,OAAO,KAAK,MAAM;EAE9B,MAAM,QADQ,IAAI,UAAU,MAAO,IAAI,OAAO,OAAQ,IAAI,OAAO,OAAU,IAAI,OAAO,OAAQ,IAAI,OAAO,OACnF,IAAI,SAAS,UAAU,GAAG,IAAI,SAAS,OAAO,EAAE,QAAQ,qBAAqB,IAAI,CAAC,MAAM;AAC9G,MAAI,KAAK,UAAU,EAAG,KAAI,KAAK,KAAK;;AAEtC,QAAO;;AAGT,SAAS,eAAe,QAA0C;CAChE,MAAM,MAAM,OAAO,SAAS,SAAS;CACrC,MAAM,QAAQ,CAAC,GAAG,yBAAyB,IAAI,EAAE,GAAG,qBAAqB,IAAI,CAAC,CAC3E,KAAK,SAAS,KAAK,QAAQ,QAAQ,IAAI,CAAC,MAAM,CAAC,CAC/C,OAAO,QAAQ;CAClB,MAAM,OAAO,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,CAAC,KAAK,KAAK;AAClD,KAAI,CAAC,KAAK,MAAM,CACd,QAAO;EAAE,IAAI;EAAO,QAAQ;EAAgG;AAE9H,QAAO;EAAE,IAAI;EAAM,MAAM;EAAO,MAAM,SAAS,KAAK;EAAE;;AAGxD,SAAgB,oBAAoB,QAIP;CAC3B,MAAM,MAAM,QAAQ,OAAO,SAAS,CAAC,aAAa;CAClD,MAAM,OAAO,OAAO,UAAU,aAAa,IAAI;AAC/C,KAAI,SAAS,qBAAqB,QAAQ,OAAQ,QAAO,eAAe,OAAO,OAAO;AACtF,KAAI,KAAK,WAAW,QAAQ,CAAE,QAAO;EAAE,IAAI;EAAM,MAAM;EAAS,MAAM,SAAS,OAAO,OAAO,SAAS,OAAO,CAAC;EAAE;AAChH,QAAO;EAAE,IAAI;EAAO,QAAQ,wCAAwC,OAAO,YAAY,OAAO,eAAe;EAAI"}
@@ -1,8 +1,8 @@
1
- import { isManagedConnectorServer } from "../../../connectors/materialize.js";
2
1
  import { canonicalizeConfiguredMcpServer, normalizeConfiguredMcpServers } from "../../../config/mcp-config-normalize.js";
3
2
  import { loadMergedBundleMcpConfig } from "../../../agent/mcp/bundle-mcp-config.js";
4
3
  import { createBundleMcpToolRuntime, listBundleMcpServerToolsForGateway, mapBundleMcpToolsForGateway } from "../../../agent/mcp/bundle-mcp-materialize.js";
5
4
  import { getWorkspacePath } from "../../../config/workspace-path-helpers.js";
5
+ import { isManagedConnectorServer } from "../../../connectors/materialize.js";
6
6
  //#region src/gateway/hono/routes/mcp.ts
7
7
  function registerMcpRoutes(authenticated, deps) {
8
8
  authenticated.get("/api/mcp/servers", (c) => {