@goondocks/myco 0.21.0 → 0.21.2

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 (157) hide show
  1. package/bin/myco-run +68 -7
  2. package/dist/{agent-eval-RJSQI5S2.js → agent-eval-2MQKTXX3.js} +7 -6
  3. package/dist/{agent-eval-RJSQI5S2.js.map → agent-eval-2MQKTXX3.js.map} +1 -1
  4. package/dist/{agent-run-2JSYFOKU.js → agent-run-XJBTSVJR.js} +5 -5
  5. package/dist/{agent-tasks-APFJIM2T.js → agent-tasks-7MWBZOC7.js} +5 -5
  6. package/dist/{chunk-75Z7UKDY.js → chunk-4D22KTXY.js} +2 -2
  7. package/dist/{chunk-P66DLD6G.js → chunk-6FBLL7MD.js} +8 -2
  8. package/dist/chunk-6FBLL7MD.js.map +1 -0
  9. package/dist/{chunk-JZS6GZ6T.js → chunk-AUIXX33A.js} +10 -3
  10. package/dist/chunk-AUIXX33A.js.map +1 -0
  11. package/dist/{chunk-F3OEQYLS.js → chunk-DBBO6FHE.js} +33 -30
  12. package/dist/{chunk-F3OEQYLS.js.map → chunk-DBBO6FHE.js.map} +1 -1
  13. package/dist/{chunk-CESKJD44.js → chunk-DMPCC7V6.js} +19 -11
  14. package/dist/chunk-DMPCC7V6.js.map +1 -0
  15. package/dist/{chunk-RL5R4CQU.js → chunk-DTWUHHFI.js} +39 -2
  16. package/dist/{chunk-RL5R4CQU.js.map → chunk-DTWUHHFI.js.map} +1 -1
  17. package/dist/{chunk-XL75KZGI.js → chunk-EKZG2MCD.js} +7 -3
  18. package/dist/chunk-EKZG2MCD.js.map +1 -0
  19. package/dist/{chunk-NGH7U6A3.js → chunk-HCT7RMM2.js} +487 -98
  20. package/dist/chunk-HCT7RMM2.js.map +1 -0
  21. package/dist/{chunk-G6QIBNZM.js → chunk-IMW5TJ3O.js} +7 -6
  22. package/dist/chunk-IMW5TJ3O.js.map +1 -0
  23. package/dist/chunk-LQIPXVDH.js +17 -0
  24. package/dist/chunk-LQIPXVDH.js.map +1 -0
  25. package/dist/{chunk-5ZG4RMUH.js → chunk-N2DGFACQ.js} +2 -2
  26. package/dist/{chunk-VHNRMM4O.js → chunk-OTQH5KZW.js} +87 -37
  27. package/dist/chunk-OTQH5KZW.js.map +1 -0
  28. package/dist/{chunk-6LB7XELY.js → chunk-QATYARI5.js} +15 -13
  29. package/dist/chunk-QATYARI5.js.map +1 -0
  30. package/dist/{chunk-LVIY7P35.js → chunk-QLLBJEM7.js} +5 -1
  31. package/dist/chunk-QLLBJEM7.js.map +1 -0
  32. package/dist/{chunk-DJ3IHNYO.js → chunk-TFRUDNLI.js} +2 -2
  33. package/dist/{chunk-R2JIJBCL.js → chunk-TMAXWERS.js} +87 -4
  34. package/dist/chunk-TMAXWERS.js.map +1 -0
  35. package/dist/chunk-TSM6VESW.js +25 -0
  36. package/dist/chunk-TSM6VESW.js.map +1 -0
  37. package/dist/{chunk-ILJPRYES.js → chunk-USVFEWYL.js} +2 -2
  38. package/dist/{chunk-JR54LTPP.js → chunk-W5L5IHP5.js} +3 -3
  39. package/dist/{chunk-BUTL6IFS.js → chunk-Z55WGA2J.js} +2 -2
  40. package/dist/{chunk-NGROSFOH.js → chunk-Z66IT5KL.js} +14 -9
  41. package/dist/chunk-Z66IT5KL.js.map +1 -0
  42. package/dist/{cli-LNYSTDQM.js → cli-DDHTHU2J.js} +37 -37
  43. package/dist/{client-NWE4TCNO.js → client-PQU53UQU.js} +5 -3
  44. package/dist/{detect-PXNM6TA7.js → detect-7NUD5B5R.js} +2 -2
  45. package/dist/{doctor-TI7EZ3RW.js → doctor-QK6KFY6H.js} +6 -6
  46. package/dist/{executor-F2YU7HXJ.js → executor-FJCMNSXM.js} +11 -10
  47. package/dist/{init-KG3TYVGE.js → init-GQPD6HHX.js} +9 -9
  48. package/dist/{installer-UMH7OJ5A.js → installer-N4UTEACX.js} +2 -2
  49. package/dist/{loader-NAVVZK63.js → loader-UDNUMEDA.js} +3 -2
  50. package/dist/{main-5PRQNEEE.js → main-4HKTZFIM.js} +469 -187
  51. package/dist/main-4HKTZFIM.js.map +1 -0
  52. package/dist/{open-5A27BCSB.js → open-3P3DDAOA.js} +5 -5
  53. package/dist/{post-compact-USAODKPQ.js → post-compact-QA5LME2J.js} +7 -7
  54. package/dist/{post-tool-use-GMMSYBII.js → post-tool-use-QRZMPNYL.js} +6 -6
  55. package/dist/{post-tool-use-failure-NZVSL2PO.js → post-tool-use-failure-XNHIKBZG.js} +7 -7
  56. package/dist/{pre-compact-LZ57DLUS.js → pre-compact-HDV6X5QM.js} +7 -7
  57. package/dist/{registry-M2Z5QBWH.js → registry-F3THYC5M.js} +4 -3
  58. package/dist/{remove-T3KE6C5N.js → remove-USQDLGTJ.js} +7 -7
  59. package/dist/{restart-YWDEVZUJ.js → restart-FQLZE2TW.js} +6 -6
  60. package/dist/{search-GKFDGELR.js → search-5COKV6TD.js} +6 -6
  61. package/dist/{server-AHUR6CWF.js → server-KRMBRW4T.js} +23 -7
  62. package/dist/{server-AHUR6CWF.js.map → server-KRMBRW4T.js.map} +1 -1
  63. package/dist/{session-2ZEPLWW6.js → session-NJCUW3OX.js} +5 -5
  64. package/dist/{session-end-LWJYQAXX.js → session-end-XD27GRYF.js} +6 -6
  65. package/dist/{session-start-WTA6GCOQ.js → session-start-RDTXUSYL.js} +11 -11
  66. package/dist/{setup-llm-E7UU5IO7.js → setup-llm-FYPPJI6W.js} +5 -5
  67. package/dist/src/agent/definitions/tasks/cortex-instructions.yaml +63 -41
  68. package/dist/src/agent/definitions/tasks/skill-evolve.yaml +178 -22
  69. package/dist/src/agent/definitions/tasks/skill-generate.yaml +20 -6
  70. package/dist/src/agent/definitions/tasks/vault-evolve.yaml +65 -55
  71. package/dist/src/cli.js +1 -1
  72. package/dist/src/daemon/main.js +1 -1
  73. package/dist/src/hooks/post-tool-use.js +1 -1
  74. package/dist/src/hooks/session-end.js +1 -1
  75. package/dist/src/hooks/session-start.js +1 -1
  76. package/dist/src/hooks/stop.js +1 -1
  77. package/dist/src/hooks/user-prompt-submit.js +1 -1
  78. package/dist/src/mcp/server.js +1 -1
  79. package/dist/src/symbionts/manifests/opencode.yaml +7 -0
  80. package/dist/src/symbionts/templates/agents-starter.md +1 -1
  81. package/dist/src/symbionts/templates/opencode/plugin.ts +41 -1
  82. package/dist/src/symbionts/templates/pi/plugin.ts +12 -1
  83. package/dist/{stats-DFG6S23S.js → stats-JCLZLA5G.js} +6 -6
  84. package/dist/{stop-WRBTXEVT.js → stop-B7XCXEM5.js} +6 -6
  85. package/dist/{stop-failure-32MGIG2Q.js → stop-failure-R6QZCUOZ.js} +7 -7
  86. package/dist/{subagent-start-VFGHQFVL.js → subagent-start-N7A622F3.js} +7 -7
  87. package/dist/{subagent-stop-663FXG3P.js → subagent-stop-SVOG5MZJ.js} +7 -7
  88. package/dist/{task-completed-ZCQYEFMZ.js → task-completed-3DL5LJXF.js} +7 -7
  89. package/dist/{team-JTI5CDUO.js → team-VJ3M263F.js} +3 -3
  90. package/dist/ui/assets/{index-DGf1h-Ha.js → index-O1kNWlWM.js} +119 -119
  91. package/dist/ui/assets/index-z2Jm8i4A.css +1 -0
  92. package/dist/ui/index.html +2 -2
  93. package/dist/{update-3NBQTG32.js → update-TVXAUJMZ.js} +45 -11
  94. package/dist/update-TVXAUJMZ.js.map +1 -0
  95. package/dist/{user-prompt-submit-ME2TBKOS.js → user-prompt-submit-KYO2VGLB.js} +10 -9
  96. package/dist/user-prompt-submit-KYO2VGLB.js.map +1 -0
  97. package/dist/{version-GQAFBBPX.js → version-LDFEALUJ.js} +2 -2
  98. package/package.json +1 -1
  99. package/skills/myco-rules/SKILL.md +94 -0
  100. package/skills/{rules → myco-rules}/references/rules-bad-example.md +1 -1
  101. package/skills/{rules → myco-rules}/references/rules-good-example.md +1 -1
  102. package/dist/chunk-6LB7XELY.js.map +0 -1
  103. package/dist/chunk-CESKJD44.js.map +0 -1
  104. package/dist/chunk-CUDIZJY7.js +0 -36
  105. package/dist/chunk-CUDIZJY7.js.map +0 -1
  106. package/dist/chunk-G6QIBNZM.js.map +0 -1
  107. package/dist/chunk-JZS6GZ6T.js.map +0 -1
  108. package/dist/chunk-LVIY7P35.js.map +0 -1
  109. package/dist/chunk-NGH7U6A3.js.map +0 -1
  110. package/dist/chunk-NGROSFOH.js.map +0 -1
  111. package/dist/chunk-P66DLD6G.js.map +0 -1
  112. package/dist/chunk-R2JIJBCL.js.map +0 -1
  113. package/dist/chunk-VHNRMM4O.js.map +0 -1
  114. package/dist/chunk-XL75KZGI.js.map +0 -1
  115. package/dist/main-5PRQNEEE.js.map +0 -1
  116. package/dist/ui/assets/index-_OP4ifzH.css +0 -1
  117. package/dist/update-3NBQTG32.js.map +0 -1
  118. package/dist/user-prompt-submit-ME2TBKOS.js.map +0 -1
  119. package/skills/myco-curate/SKILL.md +0 -86
  120. package/skills/rules/SKILL.md +0 -214
  121. /package/dist/{agent-run-2JSYFOKU.js.map → agent-run-XJBTSVJR.js.map} +0 -0
  122. /package/dist/{agent-tasks-APFJIM2T.js.map → agent-tasks-7MWBZOC7.js.map} +0 -0
  123. /package/dist/{chunk-75Z7UKDY.js.map → chunk-4D22KTXY.js.map} +0 -0
  124. /package/dist/{chunk-5ZG4RMUH.js.map → chunk-N2DGFACQ.js.map} +0 -0
  125. /package/dist/{chunk-DJ3IHNYO.js.map → chunk-TFRUDNLI.js.map} +0 -0
  126. /package/dist/{chunk-ILJPRYES.js.map → chunk-USVFEWYL.js.map} +0 -0
  127. /package/dist/{chunk-JR54LTPP.js.map → chunk-W5L5IHP5.js.map} +0 -0
  128. /package/dist/{chunk-BUTL6IFS.js.map → chunk-Z55WGA2J.js.map} +0 -0
  129. /package/dist/{cli-LNYSTDQM.js.map → cli-DDHTHU2J.js.map} +0 -0
  130. /package/dist/{client-NWE4TCNO.js.map → client-PQU53UQU.js.map} +0 -0
  131. /package/dist/{detect-PXNM6TA7.js.map → detect-7NUD5B5R.js.map} +0 -0
  132. /package/dist/{doctor-TI7EZ3RW.js.map → doctor-QK6KFY6H.js.map} +0 -0
  133. /package/dist/{executor-F2YU7HXJ.js.map → executor-FJCMNSXM.js.map} +0 -0
  134. /package/dist/{init-KG3TYVGE.js.map → init-GQPD6HHX.js.map} +0 -0
  135. /package/dist/{installer-UMH7OJ5A.js.map → installer-N4UTEACX.js.map} +0 -0
  136. /package/dist/{loader-NAVVZK63.js.map → loader-UDNUMEDA.js.map} +0 -0
  137. /package/dist/{open-5A27BCSB.js.map → open-3P3DDAOA.js.map} +0 -0
  138. /package/dist/{post-compact-USAODKPQ.js.map → post-compact-QA5LME2J.js.map} +0 -0
  139. /package/dist/{post-tool-use-GMMSYBII.js.map → post-tool-use-QRZMPNYL.js.map} +0 -0
  140. /package/dist/{post-tool-use-failure-NZVSL2PO.js.map → post-tool-use-failure-XNHIKBZG.js.map} +0 -0
  141. /package/dist/{pre-compact-LZ57DLUS.js.map → pre-compact-HDV6X5QM.js.map} +0 -0
  142. /package/dist/{registry-M2Z5QBWH.js.map → registry-F3THYC5M.js.map} +0 -0
  143. /package/dist/{remove-T3KE6C5N.js.map → remove-USQDLGTJ.js.map} +0 -0
  144. /package/dist/{restart-YWDEVZUJ.js.map → restart-FQLZE2TW.js.map} +0 -0
  145. /package/dist/{search-GKFDGELR.js.map → search-5COKV6TD.js.map} +0 -0
  146. /package/dist/{session-2ZEPLWW6.js.map → session-NJCUW3OX.js.map} +0 -0
  147. /package/dist/{session-end-LWJYQAXX.js.map → session-end-XD27GRYF.js.map} +0 -0
  148. /package/dist/{session-start-WTA6GCOQ.js.map → session-start-RDTXUSYL.js.map} +0 -0
  149. /package/dist/{setup-llm-E7UU5IO7.js.map → setup-llm-FYPPJI6W.js.map} +0 -0
  150. /package/dist/{stats-DFG6S23S.js.map → stats-JCLZLA5G.js.map} +0 -0
  151. /package/dist/{stop-WRBTXEVT.js.map → stop-B7XCXEM5.js.map} +0 -0
  152. /package/dist/{stop-failure-32MGIG2Q.js.map → stop-failure-R6QZCUOZ.js.map} +0 -0
  153. /package/dist/{subagent-start-VFGHQFVL.js.map → subagent-start-N7A622F3.js.map} +0 -0
  154. /package/dist/{subagent-stop-663FXG3P.js.map → subagent-stop-SVOG5MZJ.js.map} +0 -0
  155. /package/dist/{task-completed-ZCQYEFMZ.js.map → task-completed-3DL5LJXF.js.map} +0 -0
  156. /package/dist/{team-JTI5CDUO.js.map → team-VJ3M263F.js.map} +0 -0
  157. /package/dist/{version-GQAFBBPX.js.map → version-LDFEALUJ.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agent/executor-state.ts","../src/agent/context-windows.ts","../src/agent/provider-runtime.ts","../src/agent/run-accounting.ts"],"sourcesContent":["import { errorMessage as toErrorMessage } from '@myco/utils/error-message.js';\nimport type { CostResolution, CostSource } from './cost/types.js';\nimport type {\n EffectiveConfig,\n PhaseResult,\n ProviderConfig,\n ProviderType,\n RuntimeId,\n RuntimeTokenBudget,\n RuntimeUsage,\n} from './types.js';\n\n/** Error patterns that indicate an SDK/runtime session could not be resumed. */\nconst SESSION_RESUME_ERROR_PATTERNS = [\n /session/,\n /resume/,\n /previous[_ ]response/,\n /conversation/,\n];\n\n/**\n * Error patterns that specifically indicate the Claude Agent SDK subprocess\n * crashed while trying to attach to an expired or missing session.\n *\n * The SDK TTLs its sessions on Anthropic's side (hours to days). When the\n * scheduler resumes a run after that TTL, the `claude` subprocess exits 1\n * within ~100 seconds with 0 turns recorded — the generic \"Claude Code\n * process exited with code 1\" message contains no `session`/`resume`\n * substring, so `SESSION_RESUME_ERROR_PATTERNS` misses it and the resume\n * keeps re-queuing forever.\n *\n * These patterns are only safe to apply in contexts where a `sessionRef`\n * was passed to the runtime (see `isExpiredSessionError` callers), since\n * \"exited with code\" also fires on unrelated SDK crashes.\n */\nconst EXPIRED_SESSION_ERROR_PATTERNS = [\n /exited with code/i,\n /session[\\s_-]*not[\\s_-]*found/i,\n /session[\\s_-]*expired/i,\n /session[\\s_-]*(is|was)?[\\s_-]*(gone|missing|invalid)/i,\n];\n\nexport interface PhaseCheckpoint {\n name: string;\n status: 'pending' | 'running' | 'completed' | 'failed';\n summary?: string;\n turnsUsed?: number;\n tokensUsed?: number;\n costUsd?: number;\n costSource?: CostSource;\n costData?: CostResolution;\n sessionRef?: string;\n sessionData?: unknown;\n usage?: RuntimeUsage;\n updatedAt: number;\n}\n\nexport interface RunCheckpointState {\n runtime: RuntimeId;\n provider?: ProviderType;\n providerConfig?: ProviderConfig;\n model?: string;\n sessionRef?: string;\n sessionData?: unknown;\n phases: Record<string, PhaseCheckpoint>;\n}\n\nexport function abortReasonMessage(abortController?: AbortController): string | null {\n if (!abortController?.signal.aborted) return null;\n const reason = abortController.signal.reason;\n if (reason instanceof Error) return reason.message;\n if (typeof reason === 'string' && reason.length > 0) return reason;\n return 'Agent run aborted';\n}\n\nexport function isSessionResumeFailure(error: unknown): boolean {\n const message = toErrorMessage(error).toLowerCase();\n return SESSION_RESUME_ERROR_PATTERNS.some((pattern) => pattern.test(message));\n}\n\n/**\n * Matches errors that look like \"the Claude SDK session we tried to resume\n * is no longer alive.\" Callers MUST gate this on actually having passed a\n * `sessionRef` — otherwise an unrelated \"exited with code\" SDK crash would\n * be falsely classified as expired-session and the run would stop being\n * retried.\n */\nexport function isExpiredSessionError(error: unknown): boolean {\n const message = toErrorMessage(error);\n return EXPIRED_SESSION_ERROR_PATTERNS.some((pattern) => pattern.test(message));\n}\n\nexport function parseCheckpointState(raw: string | null | undefined): RunCheckpointState {\n if (!raw) {\n return { runtime: 'claude-sdk', phases: {} };\n }\n try {\n const parsed = JSON.parse(raw) as Partial<RunCheckpointState>;\n return {\n runtime: parsed.runtime ?? 'claude-sdk',\n provider: parsed.provider,\n providerConfig: parsed.providerConfig,\n model: parsed.model,\n sessionRef: parsed.sessionRef,\n sessionData: parsed.sessionData,\n phases: parsed.phases ?? {},\n };\n } catch {\n return { runtime: 'claude-sdk', phases: {} };\n }\n}\n\nexport function checkpointResultsForResume(\n config: EffectiveConfig,\n checkpointState: RunCheckpointState,\n): PhaseResult[] {\n if (!config.phases) return [];\n const order = new Map(config.phases.map((phase, index) => [phase.name, index]));\n return Object.values(checkpointState.phases)\n .filter((phase) => phase.status === 'completed')\n .sort((a, b) => (order.get(a.name) ?? 0) - (order.get(b.name) ?? 0))\n .map((phase) => buildPhaseResult({\n name: phase.name,\n status: 'completed',\n summary: phase.summary ?? '',\n turnsUsed: phase.turnsUsed,\n tokensUsed: phase.tokensUsed,\n costUsd: phase.costUsd,\n costSource: phase.costSource,\n costData: phase.costData,\n usage: phase.usage,\n sessionRef: phase.sessionRef,\n }));\n}\n\n/**\n * Construct a `PhaseResult` from whatever telemetry is available.\n *\n * Three callers hit this: (1) executePhase's success path has a full\n * `RuntimeUsage` + `CostResolution`; (2) executePhase's failure path has\n * partial telemetry attached to a `RuntimeExecutionError`; (3)\n * `checkpointResultsForResume` has pre-computed scalar fields from a\n * persisted checkpoint. Each caller passes what it has — the helper\n * derives `turnsUsed`/`tokensUsed`/`costUsd` from `usage` + `costData`\n * when available, and falls back to the direct scalar fields when not.\n */\nexport function buildPhaseResult(input: {\n name: string;\n status: PhaseResult['status'];\n summary: string;\n usage?: RuntimeUsage;\n costData?: CostResolution;\n turnsUsed?: number;\n tokensUsed?: number;\n costUsd?: number;\n costSource?: CostSource;\n sessionRef?: string;\n sessionData?: unknown;\n}): PhaseResult & { sessionData?: unknown } {\n const {\n name, status, summary, usage, costData,\n turnsUsed, tokensUsed, costUsd, costSource,\n sessionRef, sessionData,\n } = input;\n return {\n name,\n status,\n turnsUsed: turnsUsed ?? usage?.requests ?? 0,\n tokensUsed: tokensUsed ?? usage?.totalTokens ?? 0,\n costUsd: costUsd ?? costData?.costUsd ?? 0,\n ...(costData ? { costSource: costData.source, costData } : costSource ? { costSource } : {}),\n ...(usage ? { usage } : {}),\n ...(sessionRef ? { sessionRef } : {}),\n ...(sessionData !== undefined ? { sessionData } : {}),\n summary,\n };\n}\n\nexport function serializeCheckpointState(state: RunCheckpointState): string {\n return JSON.stringify(state);\n}\n\nexport function aggregateUsage(usages: Array<RuntimeUsage | undefined>): RuntimeUsage {\n const aggregate: RuntimeUsage = {\n requests: 0,\n inputTokens: 0,\n outputTokens: 0,\n totalTokens: 0,\n reasoningTokens: 0,\n cachedTokens: 0,\n durationMs: 0,\n };\n let sawCost = false;\n\n for (const usage of usages) {\n if (!usage) continue;\n aggregate.requests = (aggregate.requests ?? 0) + (usage.requests ?? 0);\n aggregate.inputTokens = (aggregate.inputTokens ?? 0) + (usage.inputTokens ?? 0);\n aggregate.outputTokens = (aggregate.outputTokens ?? 0) + (usage.outputTokens ?? 0);\n aggregate.totalTokens = (aggregate.totalTokens ?? 0) + (usage.totalTokens ?? 0);\n aggregate.reasoningTokens = (aggregate.reasoningTokens ?? 0) + (usage.reasoningTokens ?? 0);\n aggregate.cachedTokens = (aggregate.cachedTokens ?? 0) + (usage.cachedTokens ?? 0);\n aggregate.durationMs = (aggregate.durationMs ?? 0) + (usage.durationMs ?? 0);\n if (usage.costUsd !== undefined && usage.costUsd !== null) {\n aggregate.costUsd = (aggregate.costUsd ?? 0) + usage.costUsd;\n sawCost = true;\n }\n }\n\n if (!sawCost) {\n delete aggregate.costUsd;\n }\n\n return aggregate;\n}\n\nexport function buildUsageData(\n runUsage: RuntimeUsage,\n runCost?: CostResolution,\n phaseResults?: PhaseResult[],\n runBudget?: RuntimeTokenBudget,\n): string {\n return JSON.stringify({\n run: runUsage,\n runCost: runCost ?? null,\n runBudget: runBudget ?? null,\n phases: phaseResults?.map((phase) => ({\n name: phase.name,\n usage: phase.usage ?? null,\n turnsUsed: phase.turnsUsed,\n tokensUsed: phase.tokensUsed,\n costUsd: phase.costUsd,\n costSource: phase.costSource ?? null,\n costData: phase.costData ?? null,\n })) ?? [],\n });\n}\n\nexport function buildActionsTaken(\n runtime: RuntimeId,\n provider: ProviderConfig | undefined,\n model: string,\n phaseResults?: PhaseResult[],\n): string {\n return JSON.stringify({\n runtime,\n model,\n provider: provider?.type ?? 'anthropic',\n ...(provider?.baseUrl ? { baseUrl: provider.baseUrl } : {}),\n ...(phaseResults ? { phases: phaseResults } : {}),\n });\n}\n\nexport function resolveProviderForResume(\n currentProvider: ProviderConfig | undefined,\n resumedRun: {\n provider: ProviderType | null;\n } | null,\n checkpointState: RunCheckpointState,\n runtime: RuntimeId,\n model: string,\n): ProviderConfig | undefined {\n const persistedType = resumedRun?.provider ?? checkpointState.provider;\n const persistedProvider = checkpointState.providerConfig;\n if (!persistedType && !persistedProvider) return currentProvider;\n const matchingCurrentProvider = currentProvider?.type === persistedType ? currentProvider : undefined;\n\n return {\n ...(persistedProvider ?? {}),\n ...(matchingCurrentProvider ?? {}),\n runtime,\n type: persistedType ?? persistedProvider?.type ?? currentProvider?.type ?? 'anthropic',\n model,\n };\n}\n","/** Default context window Myco applies for local agent runs when no override is set. */\nexport const DEFAULT_LOCAL_AGENT_CONTEXT_WINDOW_TOKENS = 32_768;\n\n/** Inferred frontier-model context window when the provider does not expose one. */\nexport const DEFAULT_FRONTIER_CONTEXT_WINDOW_TOKENS = 200_000;\n\n/** Inferred OpenAI-compatible cloud context window when the provider does not expose one. */\nexport const DEFAULT_COMPATIBLE_CONTEXT_WINDOW_TOKENS = 128_000;\n","import type { ProviderType, RuntimeId } from './types.js';\nimport {\n DEFAULT_COMPATIBLE_CONTEXT_WINDOW_TOKENS,\n DEFAULT_FRONTIER_CONTEXT_WINDOW_TOKENS,\n DEFAULT_LOCAL_AGENT_CONTEXT_WINDOW_TOKENS,\n} from './context-windows.js';\n\ninterface ProviderMetadata {\n runtime: RuntimeId;\n defaultContextWindowTokens?: number;\n}\n\nconst PROVIDER_METADATA_BY_TYPE: Record<ProviderType, ProviderMetadata> = {\n anthropic: {\n runtime: 'claude-sdk',\n defaultContextWindowTokens: DEFAULT_FRONTIER_CONTEXT_WINDOW_TOKENS,\n },\n ollama: {\n runtime: 'claude-sdk',\n defaultContextWindowTokens: DEFAULT_LOCAL_AGENT_CONTEXT_WINDOW_TOKENS,\n },\n lmstudio: {\n runtime: 'claude-sdk',\n defaultContextWindowTokens: DEFAULT_LOCAL_AGENT_CONTEXT_WINDOW_TOKENS,\n },\n openai: {\n runtime: 'openai-agents',\n defaultContextWindowTokens: DEFAULT_FRONTIER_CONTEXT_WINDOW_TOKENS,\n },\n openrouter: {\n runtime: 'openai-agents',\n defaultContextWindowTokens: DEFAULT_FRONTIER_CONTEXT_WINDOW_TOKENS,\n },\n 'openai-compatible': {\n runtime: 'openai-agents',\n defaultContextWindowTokens: DEFAULT_COMPATIBLE_CONTEXT_WINDOW_TOKENS,\n },\n};\n\nexport function getProviderMetadata(providerType: ProviderType | undefined): ProviderMetadata | undefined {\n if (!providerType) return undefined;\n return PROVIDER_METADATA_BY_TYPE[providerType];\n}\n\nexport function inferRuntimeFromProviderType(providerType: ProviderType | undefined): RuntimeId | undefined {\n return getProviderMetadata(providerType)?.runtime;\n}\n\nexport function inferDefaultContextWindowFromProviderType(providerType: ProviderType | undefined): number | null {\n return getProviderMetadata(providerType)?.defaultContextWindowTokens ?? null;\n}\n","import type { RunUpdate } from '@myco/db/queries/runs.js';\nimport type { CostResolution } from './cost/types.js';\nimport {\n buildActionsTaken,\n buildUsageData,\n serializeCheckpointState,\n type RunCheckpointState,\n} from './executor-state.js';\nimport { inferDefaultContextWindowFromProviderType } from './provider-runtime.js';\nimport type { PhaseResult, ProviderConfig, RuntimeId, RuntimeTokenBudget, RuntimeUsage } from './types.js';\n\nconst TOKEN_BUDGET_WARNING_PERCENT = 75;\nconst TOKEN_BUDGET_CRITICAL_PERCENT = 90;\n\n/**\n * Compute the elapsed duration of a run in milliseconds. Returns `null`\n * unless both timestamps are populated. Started/completed timestamps on\n * `agent_runs` are stored in seconds; this helper converts to ms for UI\n * consumers that expect millisecond precision.\n */\nexport function runDurationMs(run: {\n started_at: number | null;\n completed_at: number | null;\n}): number | null {\n if (run.started_at === null || run.completed_at === null) return null;\n return (run.completed_at - run.started_at) * 1000;\n}\n\nfunction toRequestTokenNumber(\n entry: Record<string, unknown>,\n key: 'inputTokens' | 'outputTokens' | 'totalTokens',\n): number {\n const value = entry[key];\n return typeof value === 'number' ? value : 0;\n}\n\nfunction resolveContextWindow(\n provider: ProviderConfig | undefined,\n usage: RuntimeUsage,\n): { tokens: number | null; source?: RuntimeTokenBudget['contextWindowSource'] } {\n if (provider?.contextLength) {\n return { tokens: provider.contextLength, source: 'provider-config' };\n }\n const providerData = usage.providerData;\n if (providerData) {\n const candidates = [\n providerData.contextWindowTokens,\n providerData.context_window,\n providerData.maxContextTokens,\n providerData.max_context_tokens,\n ];\n for (const candidate of candidates) {\n if (typeof candidate === 'number' && candidate > 0) {\n return { tokens: candidate, source: 'provider-metadata' };\n }\n }\n }\n const providerDefault = inferDefaultContextWindowFromProviderType(provider?.type);\n if (providerDefault) {\n return { tokens: providerDefault, source: 'provider-default' };\n }\n return { tokens: null };\n}\n\nexport function analyzeRuntimeTokenBudget(\n usage: RuntimeUsage,\n provider?: ProviderConfig,\n): RuntimeTokenBudget {\n const requestEntries = usage.requestUsageEntries && usage.requestUsageEntries.length > 0\n ? usage.requestUsageEntries\n : [{\n inputTokens: usage.inputTokens ?? 0,\n outputTokens: usage.outputTokens ?? 0,\n totalTokens: usage.totalTokens ?? (\n (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0)\n ),\n }];\n let peakRequestInputTokens = 0;\n let peakRequestOutputTokens = 0;\n let peakRequestTotalTokens = 0;\n for (const entry of requestEntries) {\n const input = toRequestTokenNumber(entry, 'inputTokens');\n const output = toRequestTokenNumber(entry, 'outputTokens');\n const total = toRequestTokenNumber(entry, 'totalTokens');\n if (input > peakRequestInputTokens) peakRequestInputTokens = input;\n if (output > peakRequestOutputTokens) peakRequestOutputTokens = output;\n if (total > peakRequestTotalTokens) peakRequestTotalTokens = total;\n }\n const { tokens: contextWindowTokens, source: contextWindowSource } = resolveContextWindow(provider, usage);\n\n if (!contextWindowTokens) {\n return {\n contextWindowTokens: null,\n peakRequestInputTokens: peakRequestInputTokens || null,\n peakRequestOutputTokens: peakRequestOutputTokens || null,\n peakRequestTotalTokens: peakRequestTotalTokens || null,\n utilizationPercent: null,\n headroomTokens: null,\n status: 'unknown',\n message: 'Context window unavailable for this provider/model.',\n };\n }\n\n const utilizationPercent = peakRequestTotalTokens > 0\n ? Math.round((peakRequestTotalTokens / contextWindowTokens) * 100)\n : 0;\n const headroomTokens = Math.max(0, contextWindowTokens - peakRequestTotalTokens);\n const status = utilizationPercent >= TOKEN_BUDGET_CRITICAL_PERCENT\n ? 'post_run_pressure'\n : utilizationPercent >= TOKEN_BUDGET_WARNING_PERCENT\n ? 'warning'\n : 'ok';\n const statusMessage = status === 'post_run_pressure'\n ? 'Run operated near the model context limit.'\n : status === 'warning'\n ? 'Run used a large share of the model context window.'\n : undefined;\n const isInferredWindow = contextWindowSource === 'provider-default';\n const message = isInferredWindow\n ? (statusMessage\n ? `${statusMessage} Using inferred provider default context window.`\n : 'Using inferred provider default context window.')\n : statusMessage;\n\n return {\n contextWindowTokens,\n ...(contextWindowSource ? { contextWindowSource } : {}),\n peakRequestInputTokens: peakRequestInputTokens || null,\n peakRequestOutputTokens: peakRequestOutputTokens || null,\n peakRequestTotalTokens: peakRequestTotalTokens || null,\n utilizationPercent,\n headroomTokens,\n status,\n ...(message ? { message } : {}),\n };\n}\n\nexport function serializeCostData(cost: CostResolution | undefined): string | null {\n return cost ? JSON.stringify(cost) : null;\n}\n\nexport function summarizePhaseCosts(phaseResults: PhaseResult[]): CostResolution {\n const costedPhases = phaseResults.filter(\n (phase): phase is PhaseResult & { costData: CostResolution } =>\n phase.costData !== undefined && phase.costData !== null && phase.costData.costUsd !== null,\n );\n if (costedPhases.length === 0) {\n return {\n source: 'unavailable',\n costUsd: null,\n actualCostUsd: null,\n estimatedCostUsd: null,\n breakdown: {\n inputTokens: 0,\n cachedInputTokens: 0,\n uncachedInputTokens: 0,\n outputTokens: 0,\n reasoningTokens: 0,\n requestCount: 0,\n },\n pricingVersion: null,\n message: 'No phase cost data available',\n };\n }\n\n const firstCost = costedPhases[0].costData;\n const aggregate = {\n costUsd: 0,\n actualCostUsd: 0,\n estimatedCostUsd: 0,\n breakdown: {\n inputTokens: 0,\n cachedInputTokens: 0,\n uncachedInputTokens: 0,\n outputTokens: 0,\n reasoningTokens: 0,\n requestCount: 0,\n inputCostUsd: 0,\n cachedInputCostUsd: 0,\n outputCostUsd: 0,\n reasoningCostUsd: 0,\n requestCostUsd: 0,\n cacheSavingsUsd: 0,\n totalCostUsd: 0,\n },\n };\n\n for (const phase of costedPhases) {\n const cost = phase.costData;\n const breakdown = cost.breakdown;\n aggregate.costUsd += cost.costUsd ?? 0;\n aggregate.actualCostUsd += cost.actualCostUsd ?? 0;\n aggregate.estimatedCostUsd += cost.estimatedCostUsd ?? 0;\n aggregate.breakdown.inputTokens += breakdown.inputTokens;\n aggregate.breakdown.cachedInputTokens += breakdown.cachedInputTokens;\n aggregate.breakdown.uncachedInputTokens += breakdown.uncachedInputTokens;\n aggregate.breakdown.outputTokens += breakdown.outputTokens;\n aggregate.breakdown.reasoningTokens += breakdown.reasoningTokens;\n aggregate.breakdown.requestCount += breakdown.requestCount;\n aggregate.breakdown.inputCostUsd += breakdown.inputCostUsd ?? 0;\n aggregate.breakdown.cachedInputCostUsd += breakdown.cachedInputCostUsd ?? 0;\n aggregate.breakdown.outputCostUsd += breakdown.outputCostUsd ?? 0;\n aggregate.breakdown.reasoningCostUsd += breakdown.reasoningCostUsd ?? 0;\n aggregate.breakdown.requestCostUsd += breakdown.requestCostUsd ?? 0;\n aggregate.breakdown.cacheSavingsUsd += breakdown.cacheSavingsUsd ?? 0;\n }\n aggregate.breakdown.totalCostUsd = aggregate.costUsd;\n\n const allActual = phaseResults.every((phase) => phase.costData?.source === 'actual');\n const hasUnavailable = phaseResults.some((phase) => phase.costData?.source === 'unavailable');\n return {\n source: allActual ? 'actual' : 'estimated',\n costUsd: aggregate.costUsd,\n actualCostUsd: allActual ? aggregate.actualCostUsd : null,\n estimatedCostUsd: allActual ? null : aggregate.costUsd,\n breakdown: aggregate.breakdown,\n pricingVersion: costedPhases.every((phase) => phase.costData?.pricingVersion === firstCost.pricingVersion)\n ? firstCost.pricingVersion ?? null\n : null,\n ...(hasUnavailable ? { message: 'Some phase costs were unavailable; total reflects known phase costs only' } : {}),\n };\n}\n\ninterface RunAccountingUpdateInput {\n runtime: RuntimeId;\n provider?: ProviderConfig;\n model: string;\n checkpointState: RunCheckpointState;\n usage: RuntimeUsage;\n costData: CostResolution;\n phaseResults?: PhaseResult[];\n sessionRef?: string | null;\n}\n\ninterface RunAccountingUpdateFields extends Pick<\n RunUpdate,\n | 'runtime'\n | 'provider'\n | 'model'\n | 'session_ref'\n | 'checkpoints'\n | 'usage_data'\n | 'cost_usd'\n | 'actual_cost_usd'\n | 'estimated_cost_usd'\n | 'cost_source'\n | 'cost_data'\n> {\n actions_taken: string;\n}\n\nexport function buildRunAccountingUpdate(input: RunAccountingUpdateInput): RunAccountingUpdateFields {\n const tokenBudget = analyzeRuntimeTokenBudget(input.usage, input.provider);\n return {\n runtime: input.runtime,\n provider: input.provider?.type ?? null,\n model: input.model,\n session_ref: input.sessionRef ?? input.checkpointState.sessionRef ?? null,\n checkpoints: serializeCheckpointState(input.checkpointState),\n usage_data: buildUsageData(input.usage, input.costData, input.phaseResults, tokenBudget),\n cost_usd: input.costData.costUsd ?? null,\n actual_cost_usd: input.costData.actualCostUsd,\n estimated_cost_usd: input.costData.estimatedCostUsd,\n cost_source: input.costData.source,\n cost_data: serializeCostData(input.costData),\n actions_taken: buildActionsTaken(input.runtime, input.provider, input.model, input.phaseResults),\n };\n}\n"],"mappings":"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","names":[]}
@@ -8,6 +8,9 @@ import {
8
8
  } from "./chunk-6C6QZ4PM.js";
9
9
 
10
10
  // src/db/queries/search.ts
11
+ function sanitizeFtsQuery(query) {
12
+ return query.split(/\s+/).filter((tok) => tok.length > 0).map((tok) => /^[\w]+$/.test(tok) ? tok : `"${tok.replace(/"/g, '""')}"`).join(" ");
13
+ }
11
14
  function fullTextSearch(query, options = {}) {
12
15
  const db = getDatabase();
13
16
  const limit = options.limit ?? SEARCH_RESULTS_DEFAULT_LIMIT;
@@ -225,7 +228,8 @@ function hydrateSearchResults(vectorResults) {
225
228
  }
226
229
 
227
230
  export {
231
+ sanitizeFtsQuery,
228
232
  fullTextSearch,
229
233
  hydrateSearchResults
230
234
  };
231
- //# sourceMappingURL=chunk-LVIY7P35.js.map
235
+ //# sourceMappingURL=chunk-QLLBJEM7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/db/queries/search.ts"],"sourcesContent":["/**\n * Full-text search using SQLite FTS5.\n *\n * Searches prompt_batches and activities via their FTS5 virtual tables.\n * Semantic search (vector similarity) is handled by the external VectorStore —\n * this module covers text-based retrieval only.\n *\n * All queries use parameterized placeholders throughout.\n */\n\nimport { getDatabase } from '@myco/db/client.js';\nimport {\n SEARCH_RESULTS_DEFAULT_LIMIT,\n SEARCH_PREVIEW_CHARS,\n} from '@myco/constants.js';\nimport type { VectorSearchResult } from '@myco/daemon/embedding/types.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** All result types that can appear in search results. */\nexport type SearchResultType =\n | 'session'\n | 'spore'\n | 'plan'\n | 'artifact'\n | 'prompt_batch'\n | 'activity'\n | 'skill';\n\n/** A single result returned from full-text or semantic search. */\nexport interface SearchResult {\n id: string;\n type: SearchResultType;\n title: string;\n preview: string;\n score: number;\n session_id?: string;\n}\n\n/** Options for fullTextSearch. */\nexport interface SearchOptions {\n /** Restrict results to a single type. */\n type?: string;\n /** Maximum number of results to return (default: SEARCH_RESULTS_DEFAULT_LIMIT). */\n limit?: number;\n /**\n * When explicitly `false`, hide results belonging to sessions still in\n * `status = 'active'` (and active sessions themselves). Intelligence-task\n * reads opt in to this; UI/CLI callers leave it unset.\n */\n includeActive?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Query sanitization\n// ---------------------------------------------------------------------------\n\n/**\n * Sanitize a free-form search string for FTS5 MATCH.\n *\n * FTS5 MATCH treats several characters as operators: `-` means NOT, `:` is\n * a column filter, `\"...\"` is a literal phrase, `+` means must-match, `(`/`)`\n * group, `*` is a prefix wildcard, and unquoted hyphens in identifiers\n * (`skill-evolve-inventory`) or slashes in paths (`packages/myco/src/loader.ts`)\n * are routinely rejected as syntax errors.\n *\n * Natural-language callers (agents querying the vault, users typing in a UI\n * search box) don't know or care about FTS5 syntax — they expect their\n * string to \"just work.\" This helper tokenizes on whitespace and wraps any\n * token containing non-word characters in double quotes, turning it into\n * a literal phrase search. Plain alphanumeric/underscore tokens are left\n * unquoted so they AND together with their neighbours normally.\n *\n * Examples:\n * \"packages/myco/src/loader.ts tools\" → \"\\\"packages/myco/src/loader.ts\\\" tools\"\n * \"skill-evolve merge_candidates\" → \"\\\"skill-evolve\\\" merge_candidates\"\n * \"plain words\" → \"plain words\"\n *\n * Callers that DO want FTS5 operator semantics should skip this helper\n * and pass their own pre-formed MATCH expression to `fullTextSearch`.\n */\nexport function sanitizeFtsQuery(query: string): string {\n return query\n .split(/\\s+/)\n .filter((tok) => tok.length > 0)\n .map((tok) => /^[\\w]+$/.test(tok) ? tok : `\"${tok.replace(/\"/g, '\"\"')}\"`)\n .join(' ');\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Full-text search across capture tables using SQLite FTS5.\n *\n * Searches prompt_batches (indexed on user_prompt) and activities (indexed\n * on tool_name, tool_input, file_path). The raw query string is passed\n * directly to FTS5 MATCH — callers should sanitize if needed.\n *\n * FTS5 `rank` values are negative (lower = better match). This function\n * converts them to positive scores via `Math.abs()` so higher = better\n * in the returned results.\n *\n * When `options.type` is specified, only the matching table branch is queried.\n *\n * @param query — search string (FTS5 MATCH syntax)\n * @param options — optional type filter and result limit\n * @returns SearchResult[] ordered by score DESC\n */\nexport function fullTextSearch(\n query: string,\n options: SearchOptions = {},\n): SearchResult[] {\n const db = getDatabase();\n const limit = options.limit ?? SEARCH_RESULTS_DEFAULT_LIMIT;\n const typeFilter = options.type;\n const excludeActive = options.includeActive === false;\n\n const results: SearchResult[] = [];\n\n // -- prompt_batches branch ------------------------------------------------\n if (typeFilter === undefined || typeFilter === 'prompt_batch') {\n const activeGate = excludeActive\n ? ` AND EXISTS (SELECT 1 FROM sessions s WHERE s.id = pb.session_id AND s.status != 'active')`\n : '';\n const batchRows = db.prepare(\n `SELECT pb.id, pb.prompt_number, pb.session_id,\n substr(COALESCE(pb.user_prompt, '') || ' ' || COALESCE(pb.response_summary, ''), 1, ?) AS preview,\n fts.rank\n FROM prompt_batches_fts fts\n JOIN prompt_batches pb ON pb.id = fts.rowid\n WHERE prompt_batches_fts MATCH ?${activeGate}\n ORDER BY fts.rank\n LIMIT ?`\n ).all(SEARCH_PREVIEW_CHARS, query, limit) as Array<{\n id: number;\n prompt_number: number | null;\n session_id: string | null;\n preview: string;\n rank: number;\n }>;\n\n for (const row of batchRows) {\n results.push({\n id: String(row.id),\n type: 'prompt_batch',\n title: row.prompt_number != null\n ? `Batch #${row.prompt_number}`\n : `Batch ${row.id}`,\n preview: row.preview,\n score: Math.abs(row.rank),\n ...(row.session_id != null ? { session_id: row.session_id } : {}),\n });\n }\n }\n\n // -- activities branch ----------------------------------------------------\n if (typeFilter === undefined || typeFilter === 'activity') {\n const activeGate = excludeActive\n ? ` AND EXISTS (SELECT 1 FROM sessions s WHERE s.id = a.session_id AND s.status != 'active')`\n : '';\n const activityRows = db.prepare(\n `SELECT a.id, a.tool_name, a.tool_input, a.file_path, a.session_id,\n fts.rank\n FROM activities_fts fts\n JOIN activities a ON a.id = fts.rowid\n WHERE activities_fts MATCH ?${activeGate}\n ORDER BY fts.rank\n LIMIT ?`\n ).all(query, limit) as Array<{\n id: number;\n tool_name: string;\n tool_input: string | null;\n file_path: string | null;\n session_id: string | null;\n rank: number;\n }>;\n\n for (const row of activityRows) {\n const preview = (row.tool_input ?? row.file_path ?? '').slice(0, SEARCH_PREVIEW_CHARS);\n results.push({\n id: String(row.id),\n type: 'activity',\n title: row.tool_name,\n preview,\n score: Math.abs(row.rank),\n ...(row.session_id != null ? { session_id: row.session_id } : {}),\n });\n }\n }\n\n // -- spores branch --------------------------------------------------------\n if (typeFilter === undefined || typeFilter === 'spore') {\n // Spores may have a NULL session_id (agent-authored, no source session),\n // which are always kept; only spores attached to still-active sessions\n // are excluded when the gate is on.\n const activeGate = excludeActive\n ? ` AND (s.session_id IS NULL OR EXISTS (SELECT 1 FROM sessions ss WHERE ss.id = s.session_id AND ss.status != 'active'))`\n : '';\n const sporeRows = db.prepare(\n `SELECT s.id, s.observation_type, s.session_id,\n substr(COALESCE(s.content, ''), 1, ?) AS preview,\n fts.rank\n FROM spores_fts fts\n JOIN spores s ON s.rowid = fts.rowid\n WHERE spores_fts MATCH ?${activeGate}\n ORDER BY fts.rank\n LIMIT ?`\n ).all(SEARCH_PREVIEW_CHARS, query, limit) as Array<{\n id: string;\n observation_type: string;\n session_id: string | null;\n preview: string;\n rank: number;\n }>;\n\n for (const row of sporeRows) {\n results.push({\n id: String(row.id),\n type: 'spore',\n title: row.observation_type,\n preview: row.preview,\n score: Math.abs(row.rank),\n ...(row.session_id != null ? { session_id: row.session_id } : {}),\n });\n }\n }\n\n // -- sessions branch ------------------------------------------------------\n if (typeFilter === undefined || typeFilter === 'session') {\n const activeGate = excludeActive ? ` AND s.status != 'active'` : '';\n const sessionRows = db.prepare(\n `SELECT s.id, s.title,\n substr(COALESCE(s.summary, s.title, ''), 1, ?) AS preview,\n fts.rank\n FROM sessions_fts fts\n JOIN sessions s ON s.rowid = fts.rowid\n WHERE sessions_fts MATCH ?${activeGate}\n ORDER BY fts.rank\n LIMIT ?`\n ).all(SEARCH_PREVIEW_CHARS, query, limit) as Array<{\n id: string;\n title: string | null;\n preview: string;\n rank: number;\n }>;\n\n for (const row of sessionRows) {\n results.push({\n id: String(row.id),\n type: 'session',\n title: row.title ?? `Session ${row.id.slice(-6)}`,\n preview: row.preview,\n score: Math.abs(row.rank),\n });\n }\n }\n\n // Sort combined results by score DESC and apply limit.\n results.sort((a, b) => b.score - a.score);\n return results.slice(0, limit);\n}\n\n// ---------------------------------------------------------------------------\n// Hydration — convert VectorSearchResults into SearchResults\n// ---------------------------------------------------------------------------\n\n/** Row shape returned from sessions table for hydration. */\ninterface SessionRow {\n id: string;\n title: string | null;\n summary: string | null;\n session_id?: undefined;\n}\n\n/** Row shape returned from spores table for hydration. */\ninterface SporeRow {\n id: string;\n observation_type: string;\n content: string;\n session_id: string | null;\n}\n\n/** Row shape returned from plans table for hydration. */\ninterface PlanRow {\n id: string;\n title: string | null;\n content: string | null;\n session_id: string | null;\n}\n\n/** Row shape returned from artifacts table for hydration. */\ninterface ArtifactRow {\n id: string;\n title: string;\n content: string | null;\n}\n\n/**\n * Hydrate vector search results into SearchResults by fetching full records\n * from the record store.\n *\n * Groups results by namespace, queries each table for the relevant IDs, then\n * maps them into SearchResult format with titles and previews.\n */\nexport function hydrateSearchResults(\n vectorResults: VectorSearchResult[],\n): SearchResult[] {\n if (vectorResults.length === 0) return [];\n\n const db = getDatabase();\n const results: SearchResult[] = [];\n\n // Group result IDs by namespace\n const byNamespace = new Map<string, VectorSearchResult[]>();\n for (const vr of vectorResults) {\n const group = byNamespace.get(vr.namespace) ?? [];\n group.push(vr);\n byNamespace.set(vr.namespace, group);\n }\n\n // Use json_each so the statement text is stable and SQLite can cache the plan.\n const sessionStmt = db.prepare(\n `SELECT id, title, summary FROM sessions WHERE id IN (SELECT value FROM json_each(?))`,\n );\n const sporeStmt = db.prepare(\n `SELECT id, observation_type, content, session_id FROM spores WHERE id IN (SELECT value FROM json_each(?))`,\n );\n const planStmt = db.prepare(\n `SELECT id, title, content, session_id FROM plans WHERE id IN (SELECT value FROM json_each(?))`,\n );\n const artifactStmt = db.prepare(\n `SELECT id, title, content FROM artifacts WHERE id IN (SELECT value FROM json_each(?))`,\n );\n const skillStmt = db.prepare(\n `SELECT id, name, display_name, description FROM skill_records WHERE id IN (SELECT value FROM json_each(?))`,\n );\n\n // --- sessions ---\n const sessionResults = byNamespace.get('sessions');\n if (sessionResults && sessionResults.length > 0) {\n const ids = sessionResults.map((r) => r.id);\n const rows = sessionStmt.all(JSON.stringify(ids)) as SessionRow[];\n\n const rowMap = new Map(rows.map((r) => [r.id, r]));\n for (const vr of sessionResults) {\n const row = rowMap.get(vr.id);\n if (!row) continue;\n results.push({\n id: row.id,\n type: 'session',\n title: row.title ?? `Session ${row.id.slice(-6)}`,\n preview: (row.summary ?? '').slice(0, SEARCH_PREVIEW_CHARS),\n score: vr.similarity,\n });\n }\n }\n\n // --- spores ---\n const sporeResults = byNamespace.get('spores');\n if (sporeResults && sporeResults.length > 0) {\n const ids = sporeResults.map((r) => r.id);\n const rows = sporeStmt.all(JSON.stringify(ids)) as SporeRow[];\n\n const rowMap = new Map(rows.map((r) => [r.id, r]));\n for (const vr of sporeResults) {\n const row = rowMap.get(vr.id);\n if (!row) continue;\n results.push({\n id: row.id,\n type: 'spore',\n title: row.observation_type,\n preview: row.content.slice(0, SEARCH_PREVIEW_CHARS),\n score: vr.similarity,\n ...(row.session_id != null ? { session_id: row.session_id } : {}),\n });\n }\n }\n\n // --- plans ---\n const planResults = byNamespace.get('plans');\n if (planResults && planResults.length > 0) {\n const ids = planResults.map((r) => r.id);\n const rows = planStmt.all(JSON.stringify(ids)) as PlanRow[];\n\n const rowMap = new Map(rows.map((r) => [r.id, r]));\n for (const vr of planResults) {\n const row = rowMap.get(vr.id);\n if (!row) continue;\n results.push({\n id: row.id,\n type: 'plan',\n title: row.title ?? `Plan ${row.id.slice(-6)}`,\n preview: (row.content ?? '').slice(0, SEARCH_PREVIEW_CHARS),\n score: vr.similarity,\n ...(row.session_id != null ? { session_id: row.session_id } : {}),\n });\n }\n }\n\n // --- artifacts ---\n const artifactResults = byNamespace.get('artifacts');\n if (artifactResults && artifactResults.length > 0) {\n const ids = artifactResults.map((r) => r.id);\n const rows = artifactStmt.all(JSON.stringify(ids)) as ArtifactRow[];\n\n const rowMap = new Map(rows.map((r) => [r.id, r]));\n for (const vr of artifactResults) {\n const row = rowMap.get(vr.id);\n if (!row) continue;\n results.push({\n id: row.id,\n type: 'artifact',\n title: row.title,\n preview: (row.content ?? '').slice(0, SEARCH_PREVIEW_CHARS),\n score: vr.similarity,\n });\n }\n }\n\n // --- skill_records ---\n const skillResults = byNamespace.get('skill_records');\n if (skillResults && skillResults.length > 0) {\n const ids = skillResults.map((r) => r.id);\n const rows = skillStmt.all(JSON.stringify(ids)) as Array<{ id: string; name: string; display_name: string; description: string }>;\n\n const rowMap = new Map(rows.map((r) => [r.id, r]));\n for (const vr of skillResults) {\n const row = rowMap.get(vr.id);\n if (!row) continue;\n results.push({\n id: row.id,\n type: 'skill',\n title: row.display_name || row.name,\n preview: row.description.slice(0, SEARCH_PREVIEW_CHARS),\n score: vr.similarity,\n });\n }\n }\n\n // Preserve the original similarity-based ordering from vector search\n results.sort((a, b) => b.score - a.score);\n return results;\n}\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@ import { createRequire as __cr } from 'node:module'; const require = __cr(import
2
2
  import {
3
3
  getDigestExtract,
4
4
  shouldInjectCortex
5
- } from "./chunk-F3OEQYLS.js";
5
+ } from "./chunk-DBBO6FHE.js";
6
6
  import {
7
7
  DEFAULT_AGENT_ID,
8
8
  DIGEST_FALLBACK_TIER
@@ -47,4 +47,4 @@ export {
47
47
  shouldInjectSessionStartDigest,
48
48
  composeSessionStartContext
49
49
  };
50
- //# sourceMappingURL=chunk-DJ3IHNYO.js.map
50
+ //# sourceMappingURL=chunk-TFRUDNLI.js.map
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  SCHEMA_VERSION
4
- } from "./chunk-RL5R4CQU.js";
4
+ } from "./chunk-DTWUHHFI.js";
5
5
  import {
6
6
  loadConfig,
7
7
  updateTeamConfig
@@ -200,6 +200,11 @@ var TEAM_CONFIG_FILE = "config.json";
200
200
  var LEGACY_TEAM_CONFIG_DIR = ".myco-team";
201
201
  var LEGACY_TEAM_DEPLOY_DIR = ".team-worker";
202
202
  var TEAM_CONFIG_VERSION = 1;
203
+ var TEAM_VECTOR_REINDEX_RETRY_ATTEMPTS = 10;
204
+ var TEAM_VECTOR_REINDEX_RETRY_DELAY_MS = 1500;
205
+ var TEAM_VECTOR_REINDEX_BATCH_SIZE = 20;
206
+ var TEAM_VECTOR_REINDEX_REQUEST_TIMEOUT_MS = WRANGLER_COMMAND_TIMEOUT_MS * 6;
207
+ var TEAM_VECTOR_REINDEX_TABLES = ["spores", "sessions", "plans", "artifacts", "skill_records"];
203
208
  var TOML_NAME_REGEX = /^name\s*=\s*"[^"]*"/m;
204
209
  var TOML_D1_PLACEHOLDER_REGEX = /<YOUR_D1_DATABASE_ID>/g;
205
210
  var TOML_DB_NAME_REGEX = /database_name\s*=\s*"[^"]*"/g;
@@ -223,7 +228,7 @@ function resolvePackageRoot() {
223
228
  }
224
229
  function getTeamPackageVersion() {
225
230
  if (true) {
226
- return "0.1.2";
231
+ return "0.1.3";
227
232
  }
228
233
  const packageRoot = resolvePackageRoot();
229
234
  const candidatePaths = [
@@ -285,6 +290,80 @@ function readLocalConfig(vaultDir) {
285
290
  migrateLegacyDeployDir(vaultDir);
286
291
  return migrated;
287
292
  }
293
+ function requireLocalConfig(vaultDir) {
294
+ const config = readLocalConfig(vaultDir);
295
+ if (config) return config;
296
+ console.error(`No local myco-team config found at ${resolveLocalConfigPath(vaultDir)}`);
297
+ process.exit(1);
298
+ }
299
+ function delay(ms) {
300
+ return new Promise((resolve) => setTimeout(resolve, ms));
301
+ }
302
+ async function reindexWorkerVectors(vaultDir, workerUrlOverride) {
303
+ const config = workerUrlOverride ? null : requireLocalConfig(vaultDir);
304
+ const secrets = readSecrets(vaultDir);
305
+ const apiKey = secrets[TEAM_API_KEY_SECRET];
306
+ if (!apiKey) {
307
+ throw new Error(`Missing ${TEAM_API_KEY_SECRET} secret in ${vaultDir}`);
308
+ }
309
+ const workerUrl = workerUrlOverride ?? config?.worker_url;
310
+ if (!workerUrl) {
311
+ throw new Error("No team worker URL configured");
312
+ }
313
+ for (const table of TEAM_VECTOR_REINDEX_TABLES) {
314
+ let cursor = null;
315
+ let processed = 0;
316
+ let reindexed = 0;
317
+ let deleted = 0;
318
+ while (true) {
319
+ let response = null;
320
+ let retryableError = null;
321
+ for (let attempt = 1; attempt <= TEAM_VECTOR_REINDEX_RETRY_ATTEMPTS; attempt += 1) {
322
+ try {
323
+ response = await fetch(`${workerUrl.replace(/\/+$/, "")}/vectors/reindex`, {
324
+ method: "POST",
325
+ headers: {
326
+ "Content-Type": "application/json",
327
+ "Authorization": `Bearer ${apiKey}`
328
+ },
329
+ body: JSON.stringify({ table, limit: TEAM_VECTOR_REINDEX_BATCH_SIZE, cursor }),
330
+ signal: AbortSignal.timeout(TEAM_VECTOR_REINDEX_REQUEST_TIMEOUT_MS)
331
+ });
332
+ } catch (error) {
333
+ const message = error instanceof Error ? error.message : String(error);
334
+ const isTimeout = message.includes("timeout");
335
+ if (!isTimeout || attempt >= TEAM_VECTOR_REINDEX_RETRY_ATTEMPTS) {
336
+ throw error;
337
+ }
338
+ retryableError = new Error(`Worker vector reindex timed out for ${table} (attempt ${attempt}/${TEAM_VECTOR_REINDEX_RETRY_ATTEMPTS})`);
339
+ await delay(TEAM_VECTOR_REINDEX_RETRY_DELAY_MS);
340
+ continue;
341
+ }
342
+ if (response.ok) {
343
+ retryableError = null;
344
+ break;
345
+ }
346
+ const body2 = await response.text();
347
+ const isRetryable = response.status === 404 && body2.includes("Not found") && attempt < TEAM_VECTOR_REINDEX_RETRY_ATTEMPTS;
348
+ if (!isRetryable) {
349
+ throw new Error(`Worker vector reindex failed for ${table}: ${response.status} ${body2}`);
350
+ }
351
+ retryableError = new Error(`Worker vector reindex route not ready for ${table} yet (attempt ${attempt}/${TEAM_VECTOR_REINDEX_RETRY_ATTEMPTS})`);
352
+ await delay(TEAM_VECTOR_REINDEX_RETRY_DELAY_MS);
353
+ }
354
+ if (!response?.ok) {
355
+ throw retryableError ?? new Error(`Worker vector reindex failed for ${table}`);
356
+ }
357
+ const body = await response.json();
358
+ processed += body.processed;
359
+ reindexed += body.reindexed;
360
+ deleted += body.deleted;
361
+ cursor = body.next_cursor;
362
+ if (!cursor) break;
363
+ }
364
+ console.log(` \u2713 Reindexed ${table}: ${reindexed} upserted, ${deleted} deleted (${processed} processed)`);
365
+ }
366
+ }
288
367
  function wrangler(args, options) {
289
368
  return runWrangler(args, { cwd: options?.cwd, timeoutMs: WRANGLER_COMMAND_TIMEOUT_MS });
290
369
  }
@@ -585,7 +664,7 @@ function upgradeWorker(vaultDir) {
585
664
  return { success: false, error: `Failed to deploy worker: ${err.message}` };
586
665
  }
587
666
  }
588
- async function teamUpgrade(vaultDir) {
667
+ async function teamUpgrade(vaultDir, options = {}) {
589
668
  console.log("Upgrading team sync worker...\n");
590
669
  const result = upgradeWorker(vaultDir);
591
670
  if (!result.success) {
@@ -594,6 +673,10 @@ async function teamUpgrade(vaultDir) {
594
673
  }
595
674
  console.log(`Worker deployed: ${result.worker_url}`);
596
675
  console.log(`Version: ${result.version}`);
676
+ if (options.reindexVectors) {
677
+ console.log("Refreshing remote Vectorize metadata...");
678
+ await reindexWorkerVectors(vaultDir, result.worker_url);
679
+ }
597
680
  console.log("\nUpgrade complete.");
598
681
  }
599
682
 
@@ -609,4 +692,4 @@ export {
609
692
  upgradeWorker,
610
693
  teamUpgrade
611
694
  };
612
- //# sourceMappingURL=chunk-R2JIJBCL.js.map
695
+ //# sourceMappingURL=chunk-TMAXWERS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../myco-team/src/cli.ts","../src/config/secrets.ts","../../myco-deploy/src/cloudflare.ts","../../myco-deploy/src/local-config.ts"],"sourcesContent":["/**\n * CLI team commands — provision and manage Cloudflare team sync infrastructure.\n *\n * `myco team init` — Provision D1 database, Vectorize index, deploy worker.\n * `myco team upgrade` — Redeploy worker with updated source.\n */\n\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { CONFIG_FILENAME, loadConfig, updateTeamConfig } from '@myco/config/loader.js';\nimport { writeSecret, readSecrets } from '@myco/config/secrets.js';\nimport { WRANGLER_COMMAND_TIMEOUT_MS, TEAM_API_KEY_SECRET, TEAM_MCP_TOKEN_SECRET } from '@myco/constants.js';\nimport { SCHEMA_VERSION } from '@myco/db/schema.js';\nimport {\n extractJsonArray,\n installDeploymentDeps,\n maskSecret,\n parseD1Id,\n parseKvNamespaceId,\n parseWorkerUrl,\n readJsonConfig,\n resolveHomeConfigPath,\n resolveVaultConfigPath,\n runWrangler,\n stageDeploymentDir,\n writeJsonConfig,\n} from '@myco-deploy/index.js';\n\ndeclare const __MYCO_TEAM_VERSION__: string;\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Number of random bytes for API key generation. */\nconst API_KEY_BYTES = 32;\n\n/** Vectorize index dimensions (must match the embedding model). */\nconst VECTORIZE_DIMENSIONS = '1024';\n\n/** Vectorize distance metric. */\nconst VECTORIZE_METRIC = 'cosine';\n\n/** Prefix for team resource names. */\nconst TEAM_RESOURCE_PREFIX = 'myco-team';\n\n/** Length of the project hash suffix for unique resource naming. */\nconst PROJECT_HASH_LENGTH = 8;\n\n\n/** Source directory for worker files (relative to package root). */\nconst WORKER_SOURCE_DIR = 'worker';\n\n/** Team sync state directory within the vault. */\nconst TEAM_STATE_DIR = 'team';\nconst TEAM_DEPLOY_DIR = 'worker';\nconst TEAM_CONFIG_FILE = 'config.json';\nconst LEGACY_TEAM_CONFIG_DIR = '.myco-team';\nconst LEGACY_TEAM_DEPLOY_DIR = '.team-worker';\nconst TEAM_CONFIG_VERSION = 1;\nconst TEAM_MCP_ROTATION_RETRY_ATTEMPTS = 10;\nconst TEAM_MCP_ROTATION_RETRY_DELAY_MS = 1500;\nconst TEAM_VECTOR_REINDEX_RETRY_ATTEMPTS = 10;\nconst TEAM_VECTOR_REINDEX_RETRY_DELAY_MS = 1500;\nconst TEAM_VECTOR_REINDEX_BATCH_SIZE = 20;\nconst TEAM_VECTOR_REINDEX_REQUEST_TIMEOUT_MS = WRANGLER_COMMAND_TIMEOUT_MS * 6;\nconst TEAM_VECTOR_REINDEX_TABLES = ['spores', 'sessions', 'plans', 'artifacts', 'skill_records'] as const;\n\n/** Regex to match wrangler.toml name field. */\nconst TOML_NAME_REGEX = /^name\\s*=\\s*\"[^\"]*\"/m;\n\n/** Regex to match wrangler.toml D1 placeholder. */\nconst TOML_D1_PLACEHOLDER_REGEX = /<YOUR_D1_DATABASE_ID>/g;\n\n/** Regex to match wrangler.toml database_name field. */\nconst TOML_DB_NAME_REGEX = /database_name\\s*=\\s*\"[^\"]*\"/g;\n\n/** Regex to match wrangler.toml index_name field. */\nconst TOML_INDEX_NAME_REGEX = /index_name\\s*=\\s*\"[^\"]*\"/g;\n\n/** Regex to match wrangler.toml team package version placeholder. */\nconst TOML_TEAM_PACKAGE_VERSION_REGEX = /MYCO_TEAM_PACKAGE_VERSION\\s*=\\s*\"[^\"]*\"/g;\n\n/** Regex to match wrangler.toml Myco schema version placeholder. */\nconst TOML_MYCO_SCHEMA_VERSION_REGEX = /MYCO_SCHEMA_VERSION\\s*=\\s*\"[^\"]*\"/g;\n\n/** Regex to match database_id in existing wrangler.toml. */\nconst TOML_DB_ID_REGEX = /database_id\\s*=\\s*\"([^\"]+)\"/;\n\n/** Regex to match wrangler.toml KV namespace placeholder. */\nconst TOML_KV_PLACEHOLDER_REGEX = /<YOUR_KV_NAMESPACE_ID>/g;\n\n/** Regex to extract the KV namespace ID from an existing wrangler.toml. */\nconst TOML_KV_ID_REGEX = /\\[\\[kv_namespaces\\]\\][\\s\\S]*?id\\s*=\\s*\"([0-9a-f]+)\"/;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Generate a project hash from vault dir for unique resource naming. */\nfunction projectHash(vaultDir: string): string {\n const hash = crypto.createHash('sha256').update(vaultDir).digest('hex');\n return hash.slice(0, PROJECT_HASH_LENGTH);\n}\n\n/** Build the unique resource name for this project's team infrastructure. */\nfunction resourceName(vaultDir: string): string {\n return `${TEAM_RESOURCE_PREFIX}-${projectHash(vaultDir)}`;\n}\n\nfunction resolvePackageRoot(): string {\n const override = process.env.MYCO_TEAM_PACKAGE_ROOT?.trim();\n if (override) return override;\n return path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');\n}\n\nexport function getTeamPackageVersion(): string {\n if (typeof __MYCO_TEAM_VERSION__ !== 'undefined') {\n return __MYCO_TEAM_VERSION__;\n }\n\n const packageRoot = resolvePackageRoot();\n const candidatePaths = [\n path.join(packageRoot, 'package.json'),\n path.join(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'), 'package.json'),\n ];\n\n for (const candidatePath of candidatePaths) {\n if (!fs.existsSync(candidatePath)) continue;\n const packageJson = JSON.parse(fs.readFileSync(candidatePath, 'utf-8')) as { version?: string };\n if (packageJson.version) return packageJson.version;\n }\n\n return '0.1.0';\n}\n\nfunction getMycoSchemaVersion(): string {\n return String(SCHEMA_VERSION);\n}\n\nexport interface TeamLocalConfig {\n worker_name: string;\n worker_url: string;\n package_version: string;\n created_at: string;\n last_upgraded: string;\n config_version: number;\n}\n\ninterface LegacyTeamLocalConfig extends TeamLocalConfig {\n api_key?: string;\n mcp_token?: string | null;\n vault_dir?: string;\n}\n\nfunction resolveLocalConfigPath(vaultDir: string): string {\n return resolveVaultConfigPath(vaultDir, TEAM_STATE_DIR, TEAM_CONFIG_FILE);\n}\n\nfunction resolveLegacyLocalConfigPath(): string {\n return resolveHomeConfigPath(LEGACY_TEAM_CONFIG_DIR, TEAM_CONFIG_FILE);\n}\n\nfunction resolveDeployDir(vaultDir: string): string {\n return path.join(vaultDir, TEAM_STATE_DIR, TEAM_DEPLOY_DIR);\n}\n\nfunction resolveLegacyDeployDir(vaultDir: string): string {\n return path.join(vaultDir, LEGACY_TEAM_DEPLOY_DIR);\n}\n\nfunction writeLocalConfig(vaultDir: string, config: TeamLocalConfig): void {\n writeJsonConfig(resolveLocalConfigPath(vaultDir), config);\n}\n\nfunction migrateLegacyDeployDir(vaultDir: string): void {\n const legacyDeployDir = resolveLegacyDeployDir(vaultDir);\n const nextDeployDir = resolveDeployDir(vaultDir);\n if (!fs.existsSync(legacyDeployDir) || fs.existsSync(nextDeployDir)) return;\n\n fs.mkdirSync(path.dirname(nextDeployDir), { recursive: true });\n fs.renameSync(legacyDeployDir, nextDeployDir);\n}\n\nfunction readLocalConfig(vaultDir: string): TeamLocalConfig | null {\n const config = readJsonConfig<TeamLocalConfig>(resolveLocalConfigPath(vaultDir));\n if (config) {\n migrateLegacyDeployDir(vaultDir);\n return config;\n }\n\n const legacyConfig = readJsonConfig<LegacyTeamLocalConfig>(resolveLegacyLocalConfigPath());\n if (!legacyConfig) return null;\n if (legacyConfig.vault_dir && legacyConfig.vault_dir !== vaultDir) return null;\n\n const migrated: TeamLocalConfig = {\n worker_name: legacyConfig.worker_name,\n worker_url: legacyConfig.worker_url,\n package_version: legacyConfig.package_version,\n created_at: legacyConfig.created_at,\n last_upgraded: legacyConfig.last_upgraded,\n config_version: legacyConfig.config_version ?? TEAM_CONFIG_VERSION,\n };\n writeLocalConfig(vaultDir, migrated);\n if (legacyConfig.api_key) writeSecret(vaultDir, TEAM_API_KEY_SECRET, legacyConfig.api_key);\n if (legacyConfig.mcp_token) writeSecret(vaultDir, TEAM_MCP_TOKEN_SECRET, legacyConfig.mcp_token);\n migrateLegacyDeployDir(vaultDir);\n return migrated;\n}\n\nfunction requireLocalConfig(vaultDir: string): TeamLocalConfig {\n const config = readLocalConfig(vaultDir);\n if (config) return config;\n\n console.error(`No local myco-team config found at ${resolveLocalConfigPath(vaultDir)}`);\n process.exit(1);\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function rotateMcpTokenWithRetry(workerUrl: string, apiKey: string): Promise<string> {\n let lastError: Error | null = null;\n\n for (let attempt = 1; attempt <= TEAM_MCP_ROTATION_RETRY_ATTEMPTS; attempt += 1) {\n try {\n return await rotateMcpTokenForWorker(workerUrl, apiKey);\n } catch (error) {\n lastError = error as Error;\n const isRetryable =\n lastError.message.includes('401') &&\n lastError.message.includes('Invalid API key') &&\n attempt < TEAM_MCP_ROTATION_RETRY_ATTEMPTS;\n if (!isRetryable) {\n throw lastError;\n }\n await delay(TEAM_MCP_ROTATION_RETRY_DELAY_MS);\n }\n }\n\n throw lastError ?? new Error('MCP token rotation failed');\n}\n\nasync function reindexWorkerVectors(vaultDir: string, workerUrlOverride?: string): Promise<void> {\n const config = workerUrlOverride ? null : requireLocalConfig(vaultDir);\n const secrets = readSecrets(vaultDir);\n const apiKey = secrets[TEAM_API_KEY_SECRET];\n if (!apiKey) {\n throw new Error(`Missing ${TEAM_API_KEY_SECRET} secret in ${vaultDir}`);\n }\n const workerUrl = workerUrlOverride ?? config?.worker_url;\n if (!workerUrl) {\n throw new Error('No team worker URL configured');\n }\n\n for (const table of TEAM_VECTOR_REINDEX_TABLES) {\n let cursor: string | null = null;\n let processed = 0;\n let reindexed = 0;\n let deleted = 0;\n\n while (true) {\n let response: Response | null = null;\n let retryableError: Error | null = null;\n\n for (let attempt = 1; attempt <= TEAM_VECTOR_REINDEX_RETRY_ATTEMPTS; attempt += 1) {\n try {\n response = await fetch(`${workerUrl.replace(/\\/+$/, '')}/vectors/reindex`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ table, limit: TEAM_VECTOR_REINDEX_BATCH_SIZE, cursor }),\n signal: AbortSignal.timeout(TEAM_VECTOR_REINDEX_REQUEST_TIMEOUT_MS),\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const isTimeout = message.includes('timeout');\n if (!isTimeout || attempt >= TEAM_VECTOR_REINDEX_RETRY_ATTEMPTS) {\n throw error;\n }\n retryableError = new Error(`Worker vector reindex timed out for ${table} (attempt ${attempt}/${TEAM_VECTOR_REINDEX_RETRY_ATTEMPTS})`);\n await delay(TEAM_VECTOR_REINDEX_RETRY_DELAY_MS);\n continue;\n }\n\n if (response.ok) {\n retryableError = null;\n break;\n }\n\n const body = await response.text();\n const isRetryable = response.status === 404 && body.includes('Not found') && attempt < TEAM_VECTOR_REINDEX_RETRY_ATTEMPTS;\n if (!isRetryable) {\n throw new Error(`Worker vector reindex failed for ${table}: ${response.status} ${body}`);\n }\n\n retryableError = new Error(`Worker vector reindex route not ready for ${table} yet (attempt ${attempt}/${TEAM_VECTOR_REINDEX_RETRY_ATTEMPTS})`);\n await delay(TEAM_VECTOR_REINDEX_RETRY_DELAY_MS);\n }\n\n if (!response?.ok) {\n throw retryableError ?? new Error(`Worker vector reindex failed for ${table}`);\n }\n\n const body = await response.json() as {\n processed: number;\n reindexed: number;\n deleted: number;\n next_cursor: string | null;\n };\n processed += body.processed;\n reindexed += body.reindexed;\n deleted += body.deleted;\n cursor = body.next_cursor;\n if (!cursor) break;\n }\n\n console.log(` ✓ Reindexed ${table}: ${reindexed} upserted, ${deleted} deleted (${processed} processed)`);\n }\n}\n\n/** Run a wrangler command and return stdout. Throws on failure, surfacing stderr. */\nfunction wrangler(args: string[], options?: { cwd?: string }): string {\n return runWrangler(args, { cwd: options?.cwd, timeoutMs: WRANGLER_COMMAND_TIMEOUT_MS });\n}\n\n/** Find the worker source directory. Checks dist layout first (installed), then source layout (dev). */\nfunction locateWorkerSource(): string {\n const root = resolvePackageRoot();\n const distPath = path.join(root, 'dist', WORKER_SOURCE_DIR);\n if (fs.existsSync(distPath)) return distPath;\n const srcPath = path.join(root, WORKER_SOURCE_DIR);\n if (fs.existsSync(srcPath)) return srcPath;\n throw new Error(`Cannot find ${WORKER_SOURCE_DIR} — are you running from the myco-team package?`);\n}\n\n/**\n * Copy worker source to the vault deployment directory and patch wrangler.toml\n * with actual D1 database ID and resource names.\n */\nfunction prepareDeployDir(vaultDir: string, d1Id: string, kvId: string): string {\n const srcDir = locateWorkerSource();\n const deployDir = resolveDeployDir(vaultDir);\n const name = resourceName(vaultDir);\n return stageDeploymentDir({\n sourceDir: srcDir,\n deployDir,\n reset: true,\n textPatches: [{\n filePath: 'wrangler.toml',\n transforms: [\n (toml) => toml.replace(TOML_NAME_REGEX, `name = \"${name}\"`),\n (toml) => toml.replace(TOML_D1_PLACEHOLDER_REGEX, d1Id),\n (toml) => toml.replace(TOML_DB_NAME_REGEX, `database_name = \"${name}\"`),\n (toml) => toml.replace(TOML_INDEX_NAME_REGEX, `index_name = \"${name}-vectors\"`),\n (toml) => toml.replace(TOML_KV_PLACEHOLDER_REGEX, kvId),\n (toml) => toml.replace(TOML_TEAM_PACKAGE_VERSION_REGEX, `MYCO_TEAM_PACKAGE_VERSION = \"${getTeamPackageVersion()}\"`),\n (toml) => toml.replace(TOML_MYCO_SCHEMA_VERSION_REGEX, `MYCO_SCHEMA_VERSION = \"${getMycoSchemaVersion()}\"`),\n ],\n }],\n installDepsTimeoutMs: WRANGLER_COMMAND_TIMEOUT_MS * 3,\n });\n}\n\n/** Ensure a KV namespace exists for this project. Returns the namespace ID. */\nfunction ensureKvNamespace(name: string): string {\n const kvName = `${name}-secrets`;\n const lookupExisting = (): string => {\n const listOutput = wrangler(['kv', 'namespace', 'list']);\n const namespaces = extractJsonArray(listOutput) as Array<{ id: string; title: string }>;\n // Wrangler sometimes rewrites hyphens to underscores in titles\n const normalize = (s: string) => s.replace(/[-_]/g, '');\n const target = normalize(kvName);\n const existing = namespaces.find((ns) => normalize(ns.title) === target || normalize(ns.title).endsWith(target));\n if (!existing) throw new Error(`KV namespace \"${kvName}\" not found in list of ${namespaces.length} namespaces`);\n return existing.id;\n };\n\n try {\n const output = wrangler(['kv', 'namespace', 'create', kvName]);\n return parseKvNamespaceId(output);\n // Created successfully but we couldn't parse — fall back to list lookup\n } catch (err) {\n const errMsg = (err as Error).message;\n if (errMsg.includes('already exists') || errMsg.includes('duplicate') || errMsg.includes('same title')) {\n return lookupExisting();\n }\n throw err;\n }\n}\n\nasync function rotateMcpTokenForWorker(workerUrl: string, apiKey: string): Promise<string> {\n const response = await fetch(`${workerUrl}/mcp/rotate`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n if (!response.ok) {\n throw new Error(`MCP token rotation failed: ${response.status} ${await response.text()}`);\n }\n const body = await response.json() as { token?: string };\n if (!body.token) {\n throw new Error('MCP token rotation response did not include a token');\n }\n return body.token;\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport async function teamInit(vaultDir: string): Promise<void> {\n console.log('Provisioning team sync infrastructure...\\n');\n\n // 1. Check for wrangler\n try {\n const version = wrangler(['--version']).trim();\n console.log(`wrangler: ${version}`);\n } catch {\n console.error('Error: wrangler CLI not found. Install it with: npm install -g wrangler');\n process.exit(1);\n }\n\n // 2. Check auth\n try {\n wrangler(['whoami']);\n console.log('Cloudflare auth: OK\\n');\n } catch {\n console.error('Error: Not authenticated with Cloudflare. Run: wrangler login');\n process.exit(1);\n }\n\n const name = resourceName(vaultDir);\n console.log(`Resource name: ${name}\\n`);\n\n // 3. Create D1 database (or reuse existing)\n console.log('Creating D1 database...');\n let d1Id: string;\n try {\n const d1Output = wrangler(['d1', 'create', name]);\n d1Id = parseD1Id(d1Output);\n console.log(`D1 database created: ${d1Id}\\n`);\n } catch (err) {\n const errMsg = (err as Error).message;\n if (errMsg.includes('already exists')) {\n console.log('D1 database already exists, looking up ID...');\n const listOutput = wrangler(['d1', 'list', '--json']);\n const databases = JSON.parse(listOutput) as Array<{ name: string; uuid: string }>;\n const existing = databases.find((db) => db.name === name);\n if (!existing) {\n console.error(`D1 database \"${name}\" reported as existing but not found in list`);\n process.exit(1);\n }\n d1Id = existing.uuid;\n console.log(`Reusing D1 database: ${d1Id}\\n`);\n } else {\n console.error(`Failed to create D1 database: ${errMsg}`);\n process.exit(1);\n }\n }\n\n // 4. Create Vectorize index (or reuse existing)\n console.log('Creating Vectorize index...');\n try {\n wrangler(['vectorize', 'create', `${name}-vectors`, '--dimensions', VECTORIZE_DIMENSIONS, '--metric', VECTORIZE_METRIC]);\n console.log('Vectorize index created\\n');\n } catch (err) {\n const errMsg = (err as Error).message;\n if (errMsg.includes('already exists') || errMsg.includes('duplicate_name')) {\n console.log('Vectorize index already exists, reusing\\n');\n } else {\n console.error(`Failed to create Vectorize index: ${errMsg}`);\n process.exit(1);\n }\n }\n\n // 5. Create KV namespace for runtime secrets (MCP tokens)\n console.log('Creating KV namespace for secrets...');\n let kvId: string;\n try {\n kvId = ensureKvNamespace(name);\n console.log(`KV namespace ready: ${kvId}\\n`);\n } catch (err) {\n console.error(`Failed to create KV namespace: ${(err as Error).message}`);\n process.exit(1);\n }\n\n // 6. Generate API key\n const apiKey = crypto.randomBytes(API_KEY_BYTES).toString('hex');\n\n // 7. Prepare deployment directory\n console.log('Preparing worker deployment...');\n const deployDir = prepareDeployDir(vaultDir, d1Id, kvId);\n\n // 7. Set API key secret via wrangler\n console.log('Setting API key secret...');\n try {\n runWrangler(['secret', 'put', TEAM_API_KEY_SECRET, '--name', name], {\n cwd: deployDir,\n input: apiKey,\n timeoutMs: WRANGLER_COMMAND_TIMEOUT_MS,\n });\n console.log('Secret set\\n');\n } catch (err) {\n console.error(`Failed to set API key secret: ${(err as Error).message}`);\n process.exit(1);\n }\n\n // 8. Deploy worker\n console.log('Deploying worker...');\n let workerUrl: string;\n try {\n const deployOutput = wrangler(['deploy'], { cwd: deployDir });\n workerUrl = parseWorkerUrl(deployOutput);\n console.log(`Worker deployed: ${workerUrl}\\n`);\n } catch (err) {\n console.error(`Failed to deploy worker: ${(err as Error).message}`);\n process.exit(1);\n }\n\n // 9. Seed team config in the Worker\n console.log('Setting team configuration...');\n try {\n const { getMachineId } = await import('@myco/daemon/machine-id.js');\n const creatorMachineId = await getMachineId(vaultDir);\n await fetch(`${workerUrl}/config`, {\n method: 'PUT',\n headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n team_name: name,\n embedding_model: '@cf/baai/bge-m3',\n embedding_dimensions: '1024',\n created_at: String(Math.floor(Date.now() / 1000)),\n created_by: creatorMachineId,\n }),\n });\n console.log('Team config saved\\n');\n } catch {\n console.log('Warning: could not seed team config (non-fatal)\\n');\n }\n\n let mcpToken: string | null = null;\n try {\n mcpToken = await rotateMcpTokenForWorker(workerUrl, apiKey);\n } catch {\n // Non-fatal. The daemon can also fetch the token later through /connect.\n }\n\n // 10. Save config and API key locally\n updateTeamConfig(vaultDir, {\n enabled: true,\n worker_url: workerUrl,\n });\n writeSecret(vaultDir, TEAM_API_KEY_SECRET, apiKey);\n if (mcpToken) writeSecret(vaultDir, TEAM_MCP_TOKEN_SECRET, mcpToken);\n writeLocalConfig(vaultDir, {\n worker_name: name,\n worker_url: workerUrl,\n package_version: getTeamPackageVersion(),\n created_at: new Date().toISOString(),\n last_upgraded: new Date().toISOString(),\n config_version: TEAM_CONFIG_VERSION,\n });\n\n console.log('Team sync configured!\\n');\n console.log(` URL: ${workerUrl}`);\n console.log(` API Key: ${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`);\n if (mcpToken) {\n console.log(` MCP: ${mcpToken.slice(0, 8)}...${mcpToken.slice(-4)}`);\n }\n console.log('\\nShare the URL and API key with teammates so they can connect.');\n}\n\n// ---------------------------------------------------------------------------\n// Shared upgrade logic (used by CLI and daemon API)\n// ---------------------------------------------------------------------------\n\nexport interface UpgradeResult {\n success: boolean;\n worker_url?: string;\n version?: string;\n error?: string;\n}\n\n/**\n * Upgrade the team sync worker: re-copy source, patch config, redeploy.\n * Returns a result instead of calling process.exit — safe for both CLI and daemon.\n */\nexport function upgradeWorker(vaultDir: string): UpgradeResult {\n const config = loadConfig(vaultDir);\n if (!config.team.worker_url) {\n return { success: false, error: 'No team sync configured. Run: myco team init' };\n }\n\n migrateLegacyDeployDir(vaultDir);\n const deployDir = resolveDeployDir(vaultDir);\n const tomlPath = path.join(deployDir, 'wrangler.toml');\n\n if (!fs.existsSync(tomlPath)) {\n return { success: false, error: 'No deployment directory found. Run: myco team init' };\n }\n\n // Read ALL existing resource identifiers from current wrangler.toml.\n const existingToml = fs.readFileSync(tomlPath, 'utf-8');\n const d1Match = existingToml.match(TOML_DB_ID_REGEX);\n if (!d1Match || d1Match[1] === '<YOUR_D1_DATABASE_ID>') {\n return { success: false, error: 'Cannot determine D1 database ID from existing deployment. Run: myco team init' };\n }\n const d1Id = d1Match[1];\n\n const nameMatch = existingToml.match(/^name\\s*=\\s*\"([^\"]*)\"/m);\n const dbNameMatch = existingToml.match(/database_name\\s*=\\s*\"([^\"]*)\"/);\n const indexNameMatch = existingToml.match(/index_name\\s*=\\s*\"([^\"]*)\"/);\n const workerName = nameMatch?.[1] ?? resourceName(vaultDir);\n\n // KV namespace may not exist on older deployments — create or reuse.\n const kvMatch = existingToml.match(TOML_KV_ID_REGEX);\n let kvId: string;\n if (kvMatch) {\n kvId = kvMatch[1];\n } else {\n try {\n kvId = ensureKvNamespace(workerName);\n } catch (err) {\n return { success: false, error: `Failed to provision KV namespace: ${(err as Error).message}` };\n }\n }\n\n try {\n stageDeploymentDir({\n sourceDir: locateWorkerSource(),\n deployDir,\n textPatches: [{\n filePath: 'wrangler.toml',\n transforms: [\n (toml) => toml.replace(TOML_NAME_REGEX, `name = \"${workerName}\"`),\n (toml) => toml.replace(TOML_D1_PLACEHOLDER_REGEX, d1Id),\n (toml) => toml.replace(TOML_DB_NAME_REGEX, `database_name = \"${dbNameMatch?.[1] ?? workerName}\"`),\n (toml) => toml.replace(TOML_INDEX_NAME_REGEX, `index_name = \"${indexNameMatch?.[1] ?? `${workerName}-vectors`}\"`),\n (toml) => toml.replace(TOML_KV_PLACEHOLDER_REGEX, kvId),\n (toml) => toml.replace(TOML_TEAM_PACKAGE_VERSION_REGEX, `MYCO_TEAM_PACKAGE_VERSION = \"${getTeamPackageVersion()}\"`),\n (toml) => toml.replace(TOML_MYCO_SCHEMA_VERSION_REGEX, `MYCO_SCHEMA_VERSION = \"${getMycoSchemaVersion()}\"`),\n ],\n }],\n installDepsTimeoutMs: WRANGLER_COMMAND_TIMEOUT_MS * 3,\n });\n } catch (err) {\n return { success: false, error: `Failed to install worker dependencies: ${(err as Error).message}` };\n }\n\n // Re-set API key secret before deploy (deploy can wipe secrets)\n const secrets = readSecrets(vaultDir);\n const apiKey = secrets[TEAM_API_KEY_SECRET];\n if (apiKey) {\n try {\n runWrangler(['secret', 'put', TEAM_API_KEY_SECRET, '--name', workerName], {\n cwd: deployDir,\n input: apiKey,\n timeoutMs: WRANGLER_COMMAND_TIMEOUT_MS,\n });\n } catch {\n // Non-fatal — secret may already be set\n }\n }\n\n // Redeploy\n try {\n const deployOutput = wrangler(['deploy'], { cwd: deployDir });\n const workerUrl = parseWorkerUrl(deployOutput);\n const version = getTeamPackageVersion();\n\n updateTeamConfig(vaultDir, {\n worker_url: workerUrl,\n });\n const localConfig = readLocalConfig(vaultDir);\n if (localConfig) {\n writeLocalConfig(vaultDir, {\n ...localConfig,\n worker_name: workerName,\n worker_url: workerUrl,\n package_version: version,\n last_upgraded: new Date().toISOString(),\n });\n }\n\n return { success: true, worker_url: workerUrl, version };\n } catch (err) {\n return { success: false, error: `Failed to deploy worker: ${(err as Error).message}` };\n }\n}\n\n// ---------------------------------------------------------------------------\n// CLI wrapper\n// ---------------------------------------------------------------------------\n\nexport async function teamUpgrade(vaultDir: string, options: { reindexVectors?: boolean } = {}): Promise<void> {\n console.log('Upgrading team sync worker...\\n');\n const result = upgradeWorker(vaultDir);\n if (!result.success) {\n console.error(result.error);\n process.exit(1);\n }\n console.log(`Worker deployed: ${result.worker_url}`);\n console.log(`Version: ${result.version}`);\n if (options.reindexVectors) {\n console.log('Refreshing remote Vectorize metadata...');\n await reindexWorkerVectors(vaultDir, result.worker_url);\n }\n console.log('\\nUpgrade complete.');\n}\n\nexport async function teamReindexVectors(vaultDir: string): Promise<void> {\n console.log('Reindexing remote team vectors...\\n');\n await reindexWorkerVectors(vaultDir);\n console.log('\\nRemote vector reindex complete.');\n}\n\nexport async function teamStatus(vaultDir: string): Promise<void> {\n const config = requireLocalConfig(vaultDir);\n const secrets = readSecrets(vaultDir);\n\n console.log(`Worker: ${config.worker_name}`);\n console.log(`URL: ${config.worker_url}`);\n console.log(`API Key: ${maskSecret(secrets[TEAM_API_KEY_SECRET] ?? null)}`);\n console.log(`MCP Token: ${maskSecret(secrets[TEAM_MCP_TOKEN_SECRET] ?? null)}`);\n console.log(`Package v: ${config.package_version}`);\n console.log(`Created: ${config.created_at}`);\n console.log(`Upgraded: ${config.last_upgraded}`);\n console.log(`Config v: ${config.config_version}`);\n}\n\nexport async function teamRotateTokens(vaultDir: string, which: 'api' | 'mcp' | 'all' = 'all'): Promise<void> {\n const config = requireLocalConfig(vaultDir);\n const secrets = readSecrets(vaultDir);\n let currentApiKey = secrets[TEAM_API_KEY_SECRET] ?? '';\n let currentMcpToken = secrets[TEAM_MCP_TOKEN_SECRET] ?? null;\n\n let nextConfig = { ...config };\n\n if (which === 'api' || which === 'all') {\n const apiKey = crypto.randomBytes(API_KEY_BYTES).toString('hex');\n runWrangler(['secret', 'put', TEAM_API_KEY_SECRET, '--name', config.worker_name], {\n cwd: resolveDeployDir(vaultDir),\n input: apiKey,\n timeoutMs: WRANGLER_COMMAND_TIMEOUT_MS,\n });\n writeSecret(vaultDir, TEAM_API_KEY_SECRET, apiKey);\n currentApiKey = apiKey;\n nextConfig = {\n ...nextConfig,\n package_version: getTeamPackageVersion(),\n last_upgraded: new Date().toISOString(),\n };\n writeLocalConfig(vaultDir, nextConfig);\n }\n\n if (which === 'mcp' || which === 'all') {\n try {\n currentMcpToken = await rotateMcpTokenWithRetry(config.worker_url, currentApiKey);\n if (currentMcpToken) writeSecret(vaultDir, TEAM_MCP_TOKEN_SECRET, currentMcpToken);\n } catch (error) {\n writeLocalConfig(vaultDir, {\n ...nextConfig,\n last_upgraded: new Date().toISOString(),\n });\n throw new Error(\n `API key rotation completed, but MCP token rotation failed. Local config was updated to the new API key.\\n${(error as Error).message}`,\n );\n }\n }\n\n nextConfig.last_upgraded = new Date().toISOString();\n writeLocalConfig(vaultDir, nextConfig);\n\n console.log(`API Key: ${maskSecret(currentApiKey)}`);\n console.log(`MCP Token: ${maskSecret(currentMcpToken)}`);\n}\n\nexport async function teamDestroy(vaultDir: string): Promise<void> {\n const config = requireLocalConfig(vaultDir);\n const errors: string[] = [];\n const deployDir = resolveDeployDir(vaultDir);\n\n try {\n wrangler(['delete', config.worker_name], { cwd: deployDir });\n } catch (error) {\n errors.push(`worker delete failed: ${(error as Error).message}`);\n }\n\n try {\n wrangler(['vectorize', 'delete', `${config.worker_name}-vectors`]);\n } catch (error) {\n errors.push(`vectorize delete failed: ${(error as Error).message}`);\n }\n\n try {\n const databases = JSON.parse(wrangler(['d1', 'list', '--json'])) as Array<{ name: string; uuid: string }>;\n const database = databases.find((entry) => entry.name === config.worker_name);\n if (database) {\n wrangler(['d1', 'delete', database.name, '--skip-confirmation']);\n }\n } catch (error) {\n errors.push(`d1 delete failed: ${(error as Error).message}`);\n }\n\n try {\n const namespaces = extractJsonArray(wrangler(['kv', 'namespace', 'list'])) as Array<{ id: string; title: string }>;\n const namespace = namespaces.find((entry) => entry.title === `${config.worker_name}-secrets`);\n if (namespace) {\n wrangler(['kv', 'namespace', 'delete', '--namespace-id', namespace.id, '--skip-confirmation']);\n }\n } catch (error) {\n errors.push(`kv delete failed: ${(error as Error).message}`);\n }\n\n if (errors.length > 0) {\n throw new Error(`Team destroy incomplete. Local state preserved for retry.\\n${errors.join('\\n')}`);\n }\n\n fs.rmSync(path.join(vaultDir, TEAM_STATE_DIR), { recursive: true, force: true });\n console.log(`Destroyed local myco-team state for ${config.worker_name}.`);\n}\n","/**\n * Secrets file utilities for API key storage outside git.\n *\n * Secrets are stored in `secrets.env` inside the vault directory.\n * This file is gitignored (see VAULT_GITIGNORE) and never committed.\n * Format: KEY=value, one per line (same as .env).\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nconst SECRETS_FILE = 'secrets.env';\n\n/** Read all secrets from <vault>/secrets.env as key-value pairs. */\nexport function readSecrets(vaultDir: string): Record<string, string> {\n const secretsPath = path.join(vaultDir, SECRETS_FILE);\n if (!fs.existsSync(secretsPath)) return {};\n\n const secrets: Record<string, string> = {};\n for (const line of fs.readFileSync(secretsPath, 'utf-8').split('\\n')) {\n const match = line.match(/^\\s*([^#=]+?)\\s*=\\s*(.*?)\\s*$/);\n if (match) {\n secrets[match[1]] = match[2];\n }\n }\n return secrets;\n}\n\n/** Write a secret to <vault>/secrets.env, preserving existing entries. */\nexport function writeSecret(vaultDir: string, key: string, value: string): void {\n const secretsPath = path.join(vaultDir, SECRETS_FILE);\n const existing = readSecrets(vaultDir);\n existing[key] = value;\n\n const content = Object.entries(existing)\n .map(([k, v]) => `${k}=${v}`)\n .join('\\n') + '\\n';\n\n fs.writeFileSync(secretsPath, content, 'utf-8');\n}\n\n/** Remove one or more secrets from <vault>/secrets.env, preserving remaining entries. */\nexport function deleteSecrets(vaultDir: string, keys: string[]): void {\n const secretsPath = path.join(vaultDir, SECRETS_FILE);\n if (!fs.existsSync(secretsPath)) return;\n\n const existing = readSecrets(vaultDir);\n for (const key of keys) delete existing[key];\n\n const entries = Object.entries(existing);\n if (entries.length === 0) {\n fs.rmSync(secretsPath, { force: true });\n return;\n }\n\n const content = entries\n .map(([k, v]) => `${k}=${v}`)\n .join('\\n') + '\\n';\n\n fs.writeFileSync(secretsPath, content, 'utf-8');\n}\n\n/** Load secrets from <vault>/secrets.env into process.env (without overwriting existing vars). */\nexport function loadSecrets(vaultDir: string): void {\n const secrets = readSecrets(vaultDir);\n for (const [key, value] of Object.entries(secrets)) {\n if (!process.env[key]) {\n process.env[key] = value;\n }\n }\n}\n","import { execFileSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nconst WORKER_URL_REGEX = /(https:\\/\\/[^\\s]+\\.workers\\.dev)/;\nconst D1_ID_JSON_REGEX = /\"database_id\"\\s*:\\s*\"([0-9a-f-]{36})\"/i;\nconst D1_ID_TEXT_REGEX = /id:\\s*([0-9a-f-]{36})/i;\nconst KV_ID_REGEX = /\"id\":\\s*\"([0-9a-f]+)\"/i;\n\nexport interface WranglerOptions {\n cwd?: string;\n input?: string;\n timeoutMs: number;\n}\n\nexport interface TextPatch {\n filePath: string;\n transforms: Array<(text: string) => string>;\n}\n\nexport interface StageDeploymentDirOptions {\n sourceDir: string;\n deployDir: string;\n reset?: boolean;\n extraCopies?: Array<{ sourceDir: string; destinationSubdir: string }>;\n textPatches?: TextPatch[];\n installDepsTimeoutMs?: number | null;\n}\n\nexport function buildCommandEnv(): NodeJS.ProcessEnv {\n const nodeBinDir = path.dirname(process.execPath);\n const pathValue = process.env.PATH\n ? `${nodeBinDir}${path.delimiter}${process.env.PATH}`\n : nodeBinDir;\n return { ...process.env, PATH: pathValue };\n}\n\nexport function runWrangler(args: string[], options: WranglerOptions): string {\n try {\n return execFileSync('wrangler', args, {\n cwd: options.cwd,\n env: buildCommandEnv(),\n input: options.input,\n encoding: 'utf-8',\n timeout: options.timeoutMs,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n } catch (error) {\n const execError = error as Error & { stderr?: Buffer | string; stdout?: Buffer | string };\n const stderr = execError.stderr?.toString() ?? '';\n const stdout = execError.stdout?.toString() ?? '';\n const detail = [stderr, stdout].filter(Boolean).join('\\n').trim();\n throw new Error(detail || execError.message);\n }\n}\n\nexport function installDeploymentDeps(deployDir: string, timeoutMs: number): void {\n const packageJsonPath = path.join(deployDir, 'package.json');\n if (!fs.existsSync(packageJsonPath)) return;\n\n execFileSync('npm', ['install', '--silent', '--no-audit', '--no-fund'], {\n cwd: deployDir,\n env: buildCommandEnv(),\n encoding: 'utf-8',\n timeout: timeoutMs,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n}\n\nexport function parseWorkerUrl(output: string): string {\n const workerUrl = output.match(WORKER_URL_REGEX)?.[1];\n if (!workerUrl) {\n throw new Error(`Could not parse worker URL from deploy output:\\n${output}`);\n }\n return workerUrl;\n}\n\nexport function parseD1Id(output: string): string {\n const jsonMatch = output.match(D1_ID_JSON_REGEX);\n if (jsonMatch) return jsonMatch[1];\n\n const textMatch = output.match(D1_ID_TEXT_REGEX);\n if (textMatch) return textMatch[1];\n\n throw new Error(`Could not parse D1 database ID from wrangler output:\\n${output}`);\n}\n\nexport function parseKvNamespaceId(output: string): string {\n const kvId = output.match(KV_ID_REGEX)?.[1];\n if (!kvId) {\n throw new Error(`Could not parse KV namespace ID from wrangler output:\\n${output}`);\n }\n return kvId;\n}\n\nexport function extractJsonArray(output: string): unknown[] {\n const start = output.indexOf('[');\n const end = output.lastIndexOf(']');\n if (start === -1 || end === -1 || end < start) {\n throw new Error(`No JSON array found in output:\\n${output}`);\n }\n return JSON.parse(output.slice(start, end + 1)) as unknown[];\n}\n\nexport function stageDeploymentDir(options: StageDeploymentDirOptions): string {\n if (options.reset) {\n fs.rmSync(options.deployDir, { recursive: true, force: true });\n }\n fs.mkdirSync(options.deployDir, { recursive: true });\n fs.cpSync(options.sourceDir, options.deployDir, { recursive: true });\n\n for (const copy of options.extraCopies ?? []) {\n fs.cpSync(copy.sourceDir, path.join(options.deployDir, copy.destinationSubdir), { recursive: true });\n }\n\n for (const patch of options.textPatches ?? []) {\n const absolutePath = path.join(options.deployDir, patch.filePath);\n let nextText = fs.readFileSync(absolutePath, 'utf-8');\n for (const transform of patch.transforms) {\n nextText = transform(nextText);\n }\n fs.writeFileSync(absolutePath, nextText, 'utf-8');\n }\n\n if (options.installDepsTimeoutMs) {\n installDeploymentDeps(options.deployDir, options.installDepsTimeoutMs);\n }\n\n return options.deployDir;\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nconst MYCO_HOME_OVERRIDE_ENV = 'MYCO_HOME_OVERRIDE';\n\nexport function resolveHomeDir(): string {\n return process.env[MYCO_HOME_OVERRIDE_ENV]?.trim() || os.homedir();\n}\n\nexport function resolveHomeConfigPath(configDir: string, fileName: string): string {\n return path.join(resolveHomeDir(), configDir, fileName);\n}\n\nexport function resolveNamedHomeConfigPath(configDir: string, name: string, fileName: string): string {\n return path.join(resolveHomeDir(), configDir, name, fileName);\n}\n\nexport function resolveVaultConfigPath(vaultDir: string, configDir: string, fileName: string): string {\n return path.join(vaultDir, configDir, fileName);\n}\n\nexport function readJsonConfig<T>(configPath: string): T | null {\n if (!fs.existsSync(configPath)) return null;\n return JSON.parse(fs.readFileSync(configPath, 'utf-8')) as T;\n}\n\nexport function writeJsonConfig(configPath: string, value: unknown): void {\n fs.mkdirSync(path.dirname(configPath), { recursive: true });\n fs.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}\\n`, { encoding: 'utf-8', mode: 0o600 });\n fs.chmodSync(configPath, 0o600);\n}\n\nexport function maskSecret(secret: string | null): string | null {\n if (!secret) return null;\n if (secret.length <= 8) return secret;\n return `${secret.slice(0, 4)}...${secret.slice(-4)}`;\n}\n\nexport function createHexToken(byteLength: number): string {\n return crypto.randomBytes(byteLength).toString('hex');\n}\n"],"mappings":"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","names":["crypto","fs","path","fs","path","fs","path","path","fs","crypto","path","fs","body","path","fs","crypto"]}
@@ -0,0 +1,25 @@
1
+ import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
+
3
+ // src/vault/resolve.ts
4
+ import path from "path";
5
+ import { execFileSync } from "child_process";
6
+ function resolveVaultDir(cwd = process.cwd()) {
7
+ return path.join(resolveRepoRoot(cwd), ".myco");
8
+ }
9
+ function resolveRepoRoot(cwd) {
10
+ try {
11
+ const gitCommon = execFileSync(
12
+ "git",
13
+ ["rev-parse", "--git-common-dir"],
14
+ { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
15
+ ).trim();
16
+ return path.resolve(cwd, gitCommon, "..");
17
+ } catch {
18
+ return cwd;
19
+ }
20
+ }
21
+
22
+ export {
23
+ resolveVaultDir
24
+ };
25
+ //# sourceMappingURL=chunk-TSM6VESW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/vault/resolve.ts"],"sourcesContent":["import path from 'node:path';\nimport { execFileSync } from 'node:child_process';\n\n/**\n * Resolve the vault directory.\n *\n * Always `.myco/` in the project root. The vault is a SQLite database\n * that lives with the project — there is no escape hatch.\n *\n * Uses git to find the repo root so this works correctly in\n * git worktrees — worktree agents resolve to the same vault\n * as the main working tree.\n */\nexport function resolveVaultDir(cwd: string = process.cwd()): string {\n return path.join(resolveRepoRoot(cwd), '.myco');\n}\n\n/**\n * Find the main repo root, even from a git worktree.\n *\n * `git rev-parse --git-common-dir` returns the shared .git directory:\n * - In a normal repo: \".git\" (relative)\n * - In a worktree: \"/abs/path/to/main-repo/.git\" (absolute)\n *\n * The repo root is the parent of that path.\n * Falls back to cwd if not in a git repo.\n */\nfunction resolveRepoRoot(cwd: string): string {\n try {\n const gitCommon = execFileSync(\n 'git', ['rev-parse', '--git-common-dir'],\n { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] },\n ).trim();\n return path.resolve(cwd, gitCommon, '..');\n } catch {\n return cwd;\n }\n}\n"],"mappings":";;;AAAA,OAAO,UAAU;AACjB,SAAS,oBAAoB;AAYtB,SAAS,gBAAgB,MAAc,QAAQ,IAAI,GAAW;AACnE,SAAO,KAAK,KAAK,gBAAgB,GAAG,GAAG,OAAO;AAChD;AAYA,SAAS,gBAAgB,KAAqB;AAC5C,MAAI;AACF,UAAM,YAAY;AAAA,MAChB;AAAA,MAAO,CAAC,aAAa,kBAAkB;AAAA,MACvC,EAAE,KAAK,UAAU,SAAS,OAAO,CAAC,QAAQ,QAAQ,MAAM,EAAE;AAAA,IAC5D,EAAE,KAAK;AACP,WAAO,KAAK,QAAQ,KAAK,WAAW,IAAI;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -2,7 +2,7 @@ import { createRequire as __cr } from 'node:module'; const require = __cr(import
2
2
  import {
3
3
  loadAgentTasks,
4
4
  taskFromParsed
5
- } from "./chunk-JZS6GZ6T.js";
5
+ } from "./chunk-AUIXX33A.js";
6
6
  import {
7
7
  AgentTaskSchema
8
8
  } from "./chunk-OUJSQSKE.js";
@@ -102,4 +102,4 @@ export {
102
102
  deleteUserTask,
103
103
  copyTaskToUser
104
104
  };
105
- //# sourceMappingURL=chunk-ILJPRYES.js.map
105
+ //# sourceMappingURL=chunk-USVFEWYL.js.map
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  SymbiontInstaller
4
- } from "./chunk-VHNRMM4O.js";
4
+ } from "./chunk-OTQH5KZW.js";
5
5
  import {
6
6
  LmStudioBackend,
7
7
  OllamaBackend
@@ -13,7 +13,7 @@ import {
13
13
  } from "./chunk-MYX5NCRH.js";
14
14
  import {
15
15
  DaemonClient
16
- } from "./chunk-P66DLD6G.js";
16
+ } from "./chunk-6FBLL7MD.js";
17
17
 
18
18
  // src/cli/shared.ts
19
19
  import fs from "fs";
@@ -126,4 +126,4 @@ export {
126
126
  VAULT_GITIGNORE,
127
127
  registerSymbionts
128
128
  };
129
- //# sourceMappingURL=chunk-JR54LTPP.js.map
129
+ //# sourceMappingURL=chunk-W5L5IHP5.js.map
@@ -11,7 +11,7 @@ var cached;
11
11
  function getPluginVersion() {
12
12
  if (cached) return cached;
13
13
  if (true) {
14
- cached = "0.21.0";
14
+ cached = "0.21.2";
15
15
  return cached;
16
16
  }
17
17
  const root = findPackageRoot(path.dirname(fileURLToPath(import.meta.url)));
@@ -32,4 +32,4 @@ function getPluginVersion() {
32
32
  export {
33
33
  getPluginVersion
34
34
  };
35
- //# sourceMappingURL=chunk-BUTL6IFS.js.map
35
+ //# sourceMappingURL=chunk-Z55WGA2J.js.map
@@ -77,18 +77,23 @@ var RegistrationSchema = external_exports.object({
77
77
  mcpTarget: external_exports.string().optional(),
78
78
  mcpFormat: external_exports.enum(["json", "toml"]).default("json"),
79
79
  /**
80
- * Optional working directory injected into the Myco MCP server entry. Values
81
- * may use `{projectRoot}` and `{vaultDir}` placeholders, or remain relative
82
- * (for example `.` in a project-local config file) when the host agent
83
- * resolves paths against the config file location.
80
+ * Optional working directory injected verbatim into the Myco MCP server entry.
81
+ * Used by symbionts (for example Codex with `.`) whose MCP child would
82
+ * otherwise launch with a cwd that breaks vault discovery.
84
83
  */
85
84
  mcpCwd: external_exports.string().optional(),
86
85
  /**
87
- * Optional env vars injected into the Myco MCP server entry. Values may use
88
- * `{projectRoot}` and `{vaultDir}` placeholders so symbionts whose MCP child
89
- * starts outside the repo can still anchor Myco to the correct project.
86
+ * When true, the installer rewrites any `myco-run` references in this
87
+ * symbiont's templates to the project's `.myco/runtime.command` alias
88
+ * (e.g. `myco-dev`) at install time. Opt-in because most symbionts
89
+ * rely on `bin/myco-run` to read runtime.command at spawn time, which
90
+ * keeps the alias dynamic — change runtime.command and the next spawn
91
+ * picks it up with no re-install. Set this only for hosts that reorder
92
+ * PATH so `~/.local/bin` loses (opencode prepends `/opt/homebrew/bin`,
93
+ * so it can't reach the dev shim via PATH). Baking the alias at
94
+ * install time bypasses PATH resolution entirely for those hosts.
90
95
  */
91
- mcpEnv: external_exports.record(external_exports.string(), external_exports.string()).default({}),
96
+ substituteRuntimeCommand: external_exports.boolean().optional(),
92
97
  /**
93
98
  * JSON key under which MCP server entries are stored in the MCP config file.
94
99
  * Defaults to 'mcpServers' (used by Claude Code, Cursor, etc.). opencode uses 'mcp'.
@@ -181,4 +186,4 @@ export {
181
186
  detectSymbionts,
182
187
  resolvePackageRoot
183
188
  };
184
- //# sourceMappingURL=chunk-NGROSFOH.js.map
189
+ //# sourceMappingURL=chunk-Z66IT5KL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/symbionts/manifest-schema.ts","../src/symbionts/detect.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Declarative capture rules owned per-symbiont in its YAML manifest.\n *\n * Rules let each symbiont describe how Myco should filter or rewrite\n * captured events *without* adding symbiont-specific branches inside\n * the generic hook handlers. The hook loads the rules, a generic\n * evaluator decides the action, and the hook acts on the result.\n *\n * Condition types (in `when`):\n * - `transcript_path_missing`: structural. Fires when the hook's\n * transcript_path field is absent/empty. A legitimate user-facing\n * session records a transcript; an ephemeral sub-invocation (e.g.,\n * an agent's internal title-generation call) does not. Preferred\n * over text matching because it doesn't drift as UIs evolve.\n * - `transcript_meta_field_equals`: structural. Fires when a dot-path\n * field in the transcript's first JSON line equals a specific scalar\n * value (for example, `source = \"exec\"`). Use for source-kind or mode\n * filters that are stable in transcript metadata but not represented\n * by field presence alone.\n * - `prompt_starts_with` / `prompt_contains`: text fallback. Use\n * when no structural signal is available. Document the upgrade path\n * in the YAML so future maintainers can replace it when a better\n * signal appears.\n *\n * Scope semantics:\n * - `this_agent` (default): rule fires only when the detected agent\n * matches the manifest that owns the rule. Use for behavior that\n * is specific to the symbiont and can rely on detection working.\n * - `any_agent`: rule fires regardless of detected agent. Use for\n * patterns where detection itself might fail — e.g., an internal\n * sub-invocation that omits the fields agent detection keys on.\n *\n * Events:\n * - `session_start`: fires on SessionStart, before any prompts or\n * tools are captured. The same rules are also reusable at later\n * lifecycle boundaries that could still materialize a session row\n * (for example, transcript-backed stop processing after a missed\n * SessionStart). This is the durable \"session capture rules\"\n * contract for every symbiont.\n * - `user_prompt`: fires on UserPromptSubmit. Safety net for anything\n * that slips past session_start, and the only layer where\n * `rewrite_prompt` makes sense (prompt text doesn't exist until\n * the prompt is submitted).\n *\n * Actions:\n * - `drop`: discard the event entirely. For session_start, the hook\n * skips registering the session row. For user_prompt, the hook\n * skips posting the event and cascade-deletes any session row that\n * may have been registered before the drop rule could fire.\n * - `rewrite_prompt`: replace the captured prompt with the substring\n * after `extract_after`. Only valid for `user_prompt` events.\n */\nconst CaptureRuleSchema = z.object({\n event: z.enum(['session_start', 'user_prompt']),\n scope: z.enum(['this_agent', 'any_agent']).default('this_agent'),\n when: z.object({\n prompt_starts_with: z.string().optional(),\n prompt_contains: z.string().optional(),\n /** Structural: fires when transcript_path is absent or empty. */\n transcript_path_missing: z.boolean().optional(),\n /**\n * Structural: fires when a dot-path field exists (and is truthy) in the\n * transcript's first JSON line (session_meta). Use for detecting sub-agent\n * sessions that have real transcript files but are not user-initiated.\n *\n * Example: `source.subagent` matches a Codex thread-spawn session whose\n * session_meta has `\"source\": {\"subagent\": {...}}` but would NOT match\n * a user session with `\"source\": \"vscode\"`.\n *\n * The hook handler reads the transcript and passes the parsed meta to\n * the evaluator — the evaluator itself does no file I/O.\n */\n transcript_meta_field_exists: z.string().optional(),\n /**\n * Structural: fires when a dot-path field in session_meta exactly equals\n * a scalar value (string / number / boolean / null). Use for stable\n * source-kind markers like `source = \"exec\"`.\n */\n transcript_meta_field_equals: z.object({\n path: z.string(),\n value: z.union([z.string(), z.number(), z.boolean(), z.null()]),\n }).optional(),\n }),\n action: z.enum(['drop', 'rewrite_prompt']),\n /** Short audit string logged when the rule matches (e.g., \"codex-internal-title-gen\"). */\n reason: z.string().optional(),\n /** For rewrite_prompt: keep only the substring after this marker (first occurrence). */\n extract_after: z.string().optional(),\n /** For rewrite_prompt: trim whitespace from the extracted substring. Default true. */\n trim: z.boolean().default(true),\n});\n\nexport type CaptureRule = z.infer<typeof CaptureRuleSchema>;\n\nconst CaptureManifestSchema = z.object({\n planDirs: z.array(z.string()).default([]),\n planTags: z.array(z.string()).default([]),\n rules: z.array(CaptureRuleSchema).default([]),\n});\n\nconst RegistrationSchema = z.object({\n hooksTarget: z.string().optional(),\n /**\n * Format of the hooks target.\n * - 'json' (default): hooks template is merged into a JSON settings file.\n * - 'plugin-file': the hooks template is a verbatim file (e.g., an opencode TS plugin)\n * copied to hooksTarget without JSON parsing. Used for agents with plugin-based hook\n * systems rather than JSON hook entries.\n */\n hooksFormat: z.enum(['json', 'plugin-file']).default('json'),\n /**\n * Optional file path for a plugin deps package.json. When set, the installer writes\n * a package.json declaring the plugin SDK dependency so the agent's package manager\n * (e.g., opencode's Bun) can install it at startup. Preserved on uninstall so\n * contributors can keep their own deps.\n */\n pluginPackageTarget: z.string().optional(),\n mcpTarget: z.string().optional(),\n mcpFormat: z.enum(['json', 'toml']).default('json'),\n /**\n * Optional working directory injected verbatim into the Myco MCP server entry.\n * Used by symbionts (for example Codex with `.`) whose MCP child would\n * otherwise launch with a cwd that breaks vault discovery.\n */\n mcpCwd: z.string().optional(),\n /**\n * When true, the installer rewrites any `myco-run` references in this\n * symbiont's templates to the project's `.myco/runtime.command` alias\n * (e.g. `myco-dev`) at install time. Opt-in because most symbionts\n * rely on `bin/myco-run` to read runtime.command at spawn time, which\n * keeps the alias dynamic — change runtime.command and the next spawn\n * picks it up with no re-install. Set this only for hosts that reorder\n * PATH so `~/.local/bin` loses (opencode prepends `/opt/homebrew/bin`,\n * so it can't reach the dev shim via PATH). Baking the alias at\n * install time bypasses PATH resolution entirely for those hosts.\n */\n substituteRuntimeCommand: z.boolean().optional(),\n /**\n * JSON key under which MCP server entries are stored in the MCP config file.\n * Defaults to 'mcpServers' (used by Claude Code, Cursor, etc.). opencode uses 'mcp'.\n */\n mcpServersKey: z.string().default('mcpServers'),\n skillsTarget: z.string().optional(),\n settingsTarget: z.string().optional(),\n /** Format of the settings file. TOML-format agents (e.g., Codex) emit top-level template keys as TOML sections. */\n settingsFormat: z.enum(['json', 'toml']).default('json'),\n /** Instruction file that stubs out to AGENTS.md. Only for agents that don't read AGENTS.md natively. */\n instructionsFile: z.string().optional(),\n});\n\nexport const SymbiontManifestSchema = z.object({\n name: z.string(),\n displayName: z.string(),\n binary: z.string(),\n configDir: z.string(),\n pluginRootEnvVar: z.string(),\n settingsPath: z.string().optional(),\n hookFields: z.object({\n sessionId: z.string(),\n transcriptPath: z.string(),\n lastResponse: z.string(),\n prompt: z.string().default('prompt'),\n toolName: z.string().default('tool_name'),\n toolInput: z.string().default('tool_input'),\n toolOutput: z.string().default('tool_output'),\n /** Env var fallback for session ID (e.g., GEMINI_SESSION_ID). */\n sessionIdEnv: z.string().optional(),\n }),\n /** Resume command template with {sessionId} placeholder. Omit for IDE-based agents. */\n resumeCommand: z.string().optional(),\n capture: CaptureManifestSchema.optional(),\n registration: RegistrationSchema.optional(),\n});\n\nexport type SymbiontManifest = z.infer<typeof SymbiontManifestSchema>;\nexport type SymbiontRegistration = z.infer<typeof RegistrationSchema>;\n","import { SymbiontManifestSchema, type SymbiontManifest } from './manifest-schema.js';\nimport { findPackageRoot } from '../utils/find-package-root.js';\nimport { execFileSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport YAML from 'yaml';\n\nexport interface DetectedSymbiont {\n manifest: SymbiontManifest;\n binaryFound: boolean;\n configDirFound: boolean;\n}\n\nconst MANIFESTS_SUBDIR = 'symbionts/manifests';\n\n/** Cached manifests — static files that never change at runtime. */\nlet manifestCache: SymbiontManifest[] | null = null;\n\n/** Load all symbiont manifests from the package's dist directory. */\nexport function loadManifests(): SymbiontManifest[] {\n if (manifestCache) return manifestCache;\n const candidates = [\n // Source layout: src/symbionts/detect.ts → src/symbionts/manifests/\n path.resolve(import.meta.dirname, MANIFESTS_SUBDIR),\n // Dist layout: dist/src/symbionts/ → dist/src/symbionts/manifests/\n // (or dist/src/daemon/ → dist/src/symbionts/manifests/)\n path.resolve(import.meta.dirname, '..', MANIFESTS_SUBDIR),\n path.resolve(import.meta.dirname, '..', '..', MANIFESTS_SUBDIR),\n // Chunk layout: dist/chunk-*.js → dist/src/symbionts/manifests/\n path.resolve(import.meta.dirname, 'src', MANIFESTS_SUBDIR),\n ];\n\n for (const dir of candidates) {\n if (!fs.existsSync(dir)) continue;\n const files = fs.readdirSync(dir).filter(f => f.endsWith('.yaml'));\n if (files.length === 0) continue;\n manifestCache = files.map(f => {\n const raw = YAML.parse(fs.readFileSync(path.join(dir, f), 'utf-8'));\n return SymbiontManifestSchema.parse(raw);\n });\n return manifestCache;\n }\n return [];\n}\n\n/** Check if a binary is available on PATH. */\nfunction isBinaryOnPath(binary: string): boolean {\n try {\n execFileSync('which', [binary], { stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Detect which symbionts are available for a project. */\nexport function detectSymbionts(projectRoot: string): DetectedSymbiont[] {\n const manifests = loadManifests();\n return manifests.map(manifest => ({\n manifest,\n binaryFound: isBinaryOnPath(manifest.binary),\n configDirFound: fs.existsSync(path.join(projectRoot, manifest.configDir)),\n })).filter(d => d.binaryFound || d.configDirFound);\n}\n\n/** Find the Myco package root (where package.json lives). */\nexport function resolvePackageRoot(): string {\n return findPackageRoot(import.meta.dirname) ?? process.cwd();\n}\n"],"mappings":"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","names":["YAML"]}