@cohortapp/agent-sdk 2.3.0

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 (731) hide show
  1. package/.claude/commands/init-agent.md +104 -0
  2. package/.claude/commands/init-maestro.md +1187 -0
  3. package/.claude/settings.json +161 -0
  4. package/.env.example +216 -0
  5. package/README.md +632 -0
  6. package/agents/browser-operator/agent.md +52 -0
  7. package/agents/calendar-ops/agent.md +50 -0
  8. package/agents/communications/agent.md +96 -0
  9. package/agents/decision-log/agent.md +65 -0
  10. package/agents/desktop-operator/agent.md +59 -0
  11. package/agents/gmail-operator/agent.md +62 -0
  12. package/agents/inbound-dispatcher/agent.md +66 -0
  13. package/agents/inbox-processor/agent.md +39 -0
  14. package/agents/pmo-execution/agent.md +60 -0
  15. package/agents/session-spawner/agent.md +64 -0
  16. package/agents/slack-operator/agent.md +60 -0
  17. package/agents/whatsapp-operator/agent.md +60 -0
  18. package/agents/workflow-automation/agent.md +61 -0
  19. package/archetypes/altitudes/c-suite.yaml +58 -0
  20. package/archetypes/altitudes/founder.yaml +68 -0
  21. package/archetypes/altitudes/senior-manager.yaml +63 -0
  22. package/archetypes/altitudes/svp.yaml +60 -0
  23. package/archetypes/altitudes/vp.yaml +50 -0
  24. package/archetypes/archetype.schema.json +77 -0
  25. package/archetypes/base.yaml +47 -0
  26. package/archetypes/capabilities/commercial-leader.yaml +159 -0
  27. package/archetypes/capabilities/compliance-officer.yaml +159 -0
  28. package/archetypes/capabilities/executive-operator.yaml +169 -0
  29. package/archetypes/capabilities/finance-leader.yaml +162 -0
  30. package/archetypes/capabilities/operations-leader.yaml +154 -0
  31. package/archetypes/capabilities/product-leader.yaml +148 -0
  32. package/archetypes/capabilities/technical-leader.yaml +146 -0
  33. package/archetypes/functions/commercial-leader.yaml +62 -0
  34. package/archetypes/functions/compliance-officer.yaml +64 -0
  35. package/archetypes/functions/executive-operator.yaml +70 -0
  36. package/archetypes/functions/finance-leader.yaml +70 -0
  37. package/archetypes/functions/operations-leader.yaml +62 -0
  38. package/archetypes/functions/product-leader.yaml +61 -0
  39. package/archetypes/functions/technical-leader.yaml +57 -0
  40. package/bin/cohort-mcp.mjs +81 -0
  41. package/bin/maestro.mjs +3516 -0
  42. package/bin/maestro.test.mjs +1015 -0
  43. package/desktop-control/README.md +56 -0
  44. package/desktop-control/app-profiles/gmail.yaml +120 -0
  45. package/desktop-control/app-profiles/slack.yaml +315 -0
  46. package/desktop-control/app-profiles/whatsapp.yaml +107 -0
  47. package/docs/architecture/agent-topology.md +2239 -0
  48. package/docs/architecture/archetype-agent-factory.md +110 -0
  49. package/docs/architecture/collective-memory-and-org-mesh.md +115 -0
  50. package/docs/architecture/continuous-monitoring.md +221 -0
  51. package/docs/architecture/mcp-capability-map.md +585 -0
  52. package/docs/architecture/system-architecture.md +1272 -0
  53. package/docs/company-context/README.md +40 -0
  54. package/docs/guides/agent-persona-setup.md +600 -0
  55. package/docs/guides/agents-observe-setup.md +64 -0
  56. package/docs/guides/billing-console-keys.md +88 -0
  57. package/docs/guides/ccxray-diagnostics.md +65 -0
  58. package/docs/guides/channel-bus.md +127 -0
  59. package/docs/guides/claude-mem-setup.md +79 -0
  60. package/docs/guides/claude-pace-setup.md +56 -0
  61. package/docs/guides/claudraband-sessions.md +98 -0
  62. package/docs/guides/clawteam-swarm.md +116 -0
  63. package/docs/guides/code-review-graph-setup.md +86 -0
  64. package/docs/guides/email-setup.md +431 -0
  65. package/docs/guides/mac-mini.md +119 -0
  66. package/docs/guides/media-generation-setup.md +349 -0
  67. package/docs/guides/model-routing.md +162 -0
  68. package/docs/guides/observability-otel.md +265 -0
  69. package/docs/guides/org-onboarding.md +132 -0
  70. package/docs/guides/outbound-governance-setup.md +437 -0
  71. package/docs/guides/pdf-generation-setup.md +315 -0
  72. package/docs/guides/poller-daemon-setup.md +563 -0
  73. package/docs/guides/rag-context-setup.md +459 -0
  74. package/docs/guides/self-optimization-pattern.md +82 -0
  75. package/docs/guides/setup-wizard.md +178 -0
  76. package/docs/guides/slack-setup.md +350 -0
  77. package/docs/guides/telegram-setup.md +227 -0
  78. package/docs/guides/twilio-subaccounts-setup.md +223 -0
  79. package/docs/guides/verification.md +128 -0
  80. package/docs/guides/voice-mode.md +188 -0
  81. package/docs/guides/voice-sms-setup.md +698 -0
  82. package/docs/guides/webhook-relay-setup.md +349 -0
  83. package/docs/guides/whatsapp-setup.md +288 -0
  84. package/docs/prompts/board-pack-cover-template.md +36 -0
  85. package/docs/prompts/decision-recommendation-template.md +88 -0
  86. package/docs/prompts/followup-message-template.md +141 -0
  87. package/docs/prompts/investor-letter-template.md +52 -0
  88. package/docs/prompts/morning-brief-template.md +82 -0
  89. package/docs/prompts/presentation-template.md +58 -0
  90. package/docs/prompts/weekly-strategic-memo-template.md +104 -0
  91. package/docs/research/hallucinated-tool-output-investigation.md +151 -0
  92. package/docs/runbooks/backup-restore.md +205 -0
  93. package/docs/runbooks/cohort-cutover.md +129 -0
  94. package/docs/runbooks/fleet-operations.md +200 -0
  95. package/docs/runbooks/incident-response.md +226 -0
  96. package/docs/runbooks/mac-mini-bootstrap.md +431 -0
  97. package/docs/runbooks/perpetual-operations.md +509 -0
  98. package/docs/runbooks/recovery-and-failover.md +260 -0
  99. package/framework-features.json +267 -0
  100. package/ingest/README.md +87 -0
  101. package/lib/action-executor.js +689 -0
  102. package/lib/action-executor.test.mjs +871 -0
  103. package/lib/agent-root.mjs +37 -0
  104. package/lib/archetype.mjs +236 -0
  105. package/lib/archetype.test.mjs +132 -0
  106. package/lib/autonomy.mjs +114 -0
  107. package/lib/autonomy.test.mjs +66 -0
  108. package/lib/backlog.mjs +358 -0
  109. package/lib/backlog.test.mjs +266 -0
  110. package/lib/budget-guard.mjs +279 -0
  111. package/lib/budget-guard.test.mjs +291 -0
  112. package/lib/cadence-bus-schedule.test.mjs +194 -0
  113. package/lib/cadence-bus.mjs +1120 -0
  114. package/lib/cadence-bus.test.mjs +720 -0
  115. package/lib/cadences.mjs +205 -0
  116. package/lib/cadences.test.mjs +125 -0
  117. package/lib/capability.mjs +154 -0
  118. package/lib/capability.test.mjs +78 -0
  119. package/lib/channels/base-adapter.mjs +719 -0
  120. package/lib/channels/base-adapter.test.mjs +590 -0
  121. package/lib/channels/channel.mjs +128 -0
  122. package/lib/channels/channels.test.mjs +371 -0
  123. package/lib/channels/contract.mjs +215 -0
  124. package/lib/channels/contract.test.mjs +137 -0
  125. package/lib/channels/conversation-resolver.mjs +95 -0
  126. package/lib/channels/gmail/adapter.mjs +87 -0
  127. package/lib/channels/inbox-item.mjs +255 -0
  128. package/lib/channels/inbox-item.test.mjs +335 -0
  129. package/lib/channels/index.mjs +94 -0
  130. package/lib/channels/orgmail/adapter.mjs +353 -0
  131. package/lib/channels/orgmail/adapter.test.mjs +311 -0
  132. package/lib/channels/pairing.mjs +363 -0
  133. package/lib/channels/pairing.test.mjs +270 -0
  134. package/lib/channels/registry.mjs +164 -0
  135. package/lib/channels/slack/adapter.mjs +317 -0
  136. package/lib/channels/slack-adapter.test.mjs +212 -0
  137. package/lib/channels/sms/adapter.mjs +43 -0
  138. package/lib/channels/telegram/adapter.mjs +432 -0
  139. package/lib/channels/telegram-adapter.test.mjs +306 -0
  140. package/lib/channels/voice/adapter.mjs +301 -0
  141. package/lib/channels/voice/adapter.test.mjs +278 -0
  142. package/lib/channels/whatsapp/adapter-baileys.mjs +587 -0
  143. package/lib/channels/whatsapp/adapter-baileys.test.mjs +359 -0
  144. package/lib/channels/whatsapp/adapter-twilio.mjs +65 -0
  145. package/lib/channels/whatsapp/baileys-typing.test.mjs +154 -0
  146. package/lib/charter.mjs +256 -0
  147. package/lib/charter.test.mjs +89 -0
  148. package/lib/claude-bin.mjs +134 -0
  149. package/lib/claude-bin.test.mjs +75 -0
  150. package/lib/collective/capture.mjs +185 -0
  151. package/lib/collective/capture.test.mjs +121 -0
  152. package/lib/collective/cards.mjs +201 -0
  153. package/lib/collective/cards.test.mjs +114 -0
  154. package/lib/collective/config.mjs +186 -0
  155. package/lib/collective/config.test.mjs +123 -0
  156. package/lib/collective/global-config.mjs +113 -0
  157. package/lib/collective/global-config.test.mjs +75 -0
  158. package/lib/collective/presence.mjs +201 -0
  159. package/lib/collective/presence.test.mjs +95 -0
  160. package/lib/collective/recall.mjs +215 -0
  161. package/lib/collective/recall.test.mjs +116 -0
  162. package/lib/comms/send-gate.mjs +554 -0
  163. package/lib/comms/send-gate.test.mjs +577 -0
  164. package/lib/comms.mjs +67 -0
  165. package/lib/comms.test.mjs +41 -0
  166. package/lib/diagnostics/alerts.mjs +424 -0
  167. package/lib/diagnostics/alerts.test.mjs +318 -0
  168. package/lib/diagnostics/backup-freshness.mjs +188 -0
  169. package/lib/diagnostics/backup-freshness.test.mjs +185 -0
  170. package/lib/diagnostics/counters.mjs +269 -0
  171. package/lib/diagnostics/counters.test.mjs +206 -0
  172. package/lib/diagnostics/events.mjs +188 -0
  173. package/lib/diagnostics/events.test.mjs +290 -0
  174. package/lib/diagnostics/otel.mjs +237 -0
  175. package/lib/diagnostics/otel.test.mjs +196 -0
  176. package/lib/diagnostics/trace.mjs +216 -0
  177. package/lib/diagnostics/trace.test.mjs +251 -0
  178. package/lib/env-compat.mjs +74 -0
  179. package/lib/env-compat.test.mjs +104 -0
  180. package/lib/feature-init.mjs +331 -0
  181. package/lib/fs-atomic.mjs +112 -0
  182. package/lib/fs-atomic.test.mjs +72 -0
  183. package/lib/fs-ownership.mjs +111 -0
  184. package/lib/fs-ownership.test.mjs +158 -0
  185. package/lib/hooks/bus.mjs +347 -0
  186. package/lib/hooks/bus.test.mjs +387 -0
  187. package/lib/index.js +16 -0
  188. package/lib/learning/config.mjs +106 -0
  189. package/lib/learning/config.test.mjs +75 -0
  190. package/lib/learning/counters.mjs +156 -0
  191. package/lib/learning/counters.test.mjs +69 -0
  192. package/lib/learning/curator-consolidate.test.mjs +238 -0
  193. package/lib/learning/curator.mjs +453 -0
  194. package/lib/learning/curator.test.mjs +106 -0
  195. package/lib/learning/log.mjs +40 -0
  196. package/lib/learning/reflect.mjs +534 -0
  197. package/lib/learning/reflect.test.mjs +0 -0
  198. package/lib/learning/session-index.mjs +352 -0
  199. package/lib/learning/session-index.test.mjs +125 -0
  200. package/lib/learning/skill-writer.mjs +474 -0
  201. package/lib/learning/skill-writer.test.mjs +210 -0
  202. package/lib/mcp/server.mjs +328 -0
  203. package/lib/mcp/server.test.mjs +400 -0
  204. package/lib/model-router/auth-profiles.mjs +758 -0
  205. package/lib/model-router/auth-profiles.test.mjs +580 -0
  206. package/lib/model-router/catalog/anthropic.yaml +153 -0
  207. package/lib/model-router/catalog/deepseek.yaml +86 -0
  208. package/lib/model-router/catalog/moonshot.yaml +81 -0
  209. package/lib/model-router/catalog/qwen.yaml +114 -0
  210. package/lib/model-router/catalog.mjs +925 -0
  211. package/lib/model-router/catalog.test.mjs +385 -0
  212. package/lib/model-router/economics.mjs +564 -0
  213. package/lib/model-router/economics.test.mjs +344 -0
  214. package/lib/model-router/failover.mjs +298 -0
  215. package/lib/model-router/failover.test.mjs +439 -0
  216. package/lib/model-router/health.mjs +453 -0
  217. package/lib/model-router/health.test.mjs +338 -0
  218. package/lib/model-router/integration-coverage.test.mjs +829 -0
  219. package/lib/model-router/integration.test.mjs +564 -0
  220. package/lib/model-router/ledger.mjs +402 -0
  221. package/lib/model-router/ledger.test.mjs +382 -0
  222. package/lib/model-router/llm-task.mjs +515 -0
  223. package/lib/model-router/llm-task.test.mjs +392 -0
  224. package/lib/model-router/org-credentials.mjs +260 -0
  225. package/lib/model-router/org-credentials.test.mjs +265 -0
  226. package/lib/model-router/pricing-refresh.mjs +463 -0
  227. package/lib/model-router/pricing-refresh.test.mjs +286 -0
  228. package/lib/model-router/reconcile.mjs +429 -0
  229. package/lib/model-router/reconcile.test.mjs +316 -0
  230. package/lib/model-router/repair.mjs +471 -0
  231. package/lib/model-router/repair.test.mjs +180 -0
  232. package/lib/model-router/resolve.mjs +1206 -0
  233. package/lib/model-router/spawn.mjs +497 -0
  234. package/lib/model-router/spawn.test.mjs +425 -0
  235. package/lib/model-router/taxonomy.mjs +893 -0
  236. package/lib/model-router/taxonomy.test.mjs +410 -0
  237. package/lib/model-router.mjs +677 -0
  238. package/lib/model-router.test.mjs +907 -0
  239. package/lib/org/activity.mjs +211 -0
  240. package/lib/org/activity.test.mjs +134 -0
  241. package/lib/org/approvals.mjs +448 -0
  242. package/lib/org/approvals.test.mjs +216 -0
  243. package/lib/org/awareness.mjs +222 -0
  244. package/lib/org/awareness.test.mjs +159 -0
  245. package/lib/org/board.mjs +229 -0
  246. package/lib/org/board.test.mjs +177 -0
  247. package/lib/org/bootstrap-context.mjs +169 -0
  248. package/lib/org/bootstrap-context.test.mjs +153 -0
  249. package/lib/org/client.mjs +1628 -0
  250. package/lib/org/client.test.mjs +1107 -0
  251. package/lib/org/cohort-client.mjs +67 -0
  252. package/lib/org/cohort-client.test.mjs +126 -0
  253. package/lib/org/cost-sync.mjs +227 -0
  254. package/lib/org/cost-sync.test.mjs +153 -0
  255. package/lib/org/doctor.mjs +212 -0
  256. package/lib/org/doctor.test.mjs +212 -0
  257. package/lib/org/handoff.mjs +293 -0
  258. package/lib/org/handoff.test.mjs +269 -0
  259. package/lib/org/integration-tools.mjs +182 -0
  260. package/lib/org/integration-tools.test.mjs +160 -0
  261. package/lib/org/keys.mjs +131 -0
  262. package/lib/org/keys.test.mjs +92 -0
  263. package/lib/org/knowledge.mjs +463 -0
  264. package/lib/org/knowledge.test.mjs +319 -0
  265. package/lib/org/leases.mjs +335 -0
  266. package/lib/org/leases.test.mjs +235 -0
  267. package/lib/org/mesh-integration.test.mjs +127 -0
  268. package/lib/org/mesh.mjs +459 -0
  269. package/lib/org/mesh.test.mjs +345 -0
  270. package/lib/org/messaging.mjs +503 -0
  271. package/lib/org/messaging.test.mjs +238 -0
  272. package/lib/org/policy.mjs +345 -0
  273. package/lib/org/policy.test.mjs +237 -0
  274. package/lib/org/protocol.checksum +1 -0
  275. package/lib/org/protocol.checksum.test.mjs +90 -0
  276. package/lib/org/protocol.mjs +967 -0
  277. package/lib/org/protocol.test.mjs +264 -0
  278. package/lib/org/registry.mjs +194 -0
  279. package/lib/org/registry.test.mjs +100 -0
  280. package/lib/org/tool-surface-integration.test.mjs +120 -0
  281. package/lib/org/tool-surface.mjs +2535 -0
  282. package/lib/org/tool-surface.test.mjs +589 -0
  283. package/lib/org/ui-parity.mjs +3236 -0
  284. package/lib/org/ui-parity.test.mjs +348 -0
  285. package/lib/org/verify.mjs +176 -0
  286. package/lib/org/verify.test.mjs +194 -0
  287. package/lib/rag/embed.mjs +188 -0
  288. package/lib/rag/indexer.mjs +425 -0
  289. package/lib/rag/rag.test.mjs +505 -0
  290. package/lib/rag/search.mjs +475 -0
  291. package/lib/rate-guard.mjs +246 -0
  292. package/lib/rate-guard.test.mjs +201 -0
  293. package/lib/render.mjs +112 -0
  294. package/lib/render.test.mjs +68 -0
  295. package/lib/resource-governor.mjs +297 -0
  296. package/lib/resource-governor.test.mjs +262 -0
  297. package/lib/scheduling/dynamic-jobs.mjs +675 -0
  298. package/lib/scheduling/dynamic-jobs.test.mjs +344 -0
  299. package/lib/scheduling/jitter.mjs +0 -0
  300. package/lib/scheduling/jitter.test.mjs +140 -0
  301. package/lib/secrets/broker.mjs +315 -0
  302. package/lib/secrets/broker.test.mjs +280 -0
  303. package/lib/secrets/providers.mjs +461 -0
  304. package/lib/secrets/providers.test.mjs +274 -0
  305. package/lib/security/audit-engine.mjs +684 -0
  306. package/lib/security/audit-engine.test.mjs +389 -0
  307. package/lib/security/coerce-args.mjs +552 -0
  308. package/lib/security/coerce-args.test.mjs +281 -0
  309. package/lib/security/dangerous-tools.mjs +97 -0
  310. package/lib/security/dangerous-tools.test.mjs +68 -0
  311. package/lib/security/external-content.mjs +145 -0
  312. package/lib/security/external-content.test.mjs +67 -0
  313. package/lib/security/redact.mjs +592 -0
  314. package/lib/security/redact.test.mjs +441 -0
  315. package/lib/security/secret-equal.mjs +73 -0
  316. package/lib/security/secret-equal.test.mjs +55 -0
  317. package/lib/session-permissions.mjs +101 -0
  318. package/lib/session-permissions.test.mjs +100 -0
  319. package/lib/setup/claude-probe.mjs +74 -0
  320. package/lib/setup/completeness.mjs +175 -0
  321. package/lib/setup/completeness.test.mjs +110 -0
  322. package/lib/setup/context-pack.mjs +173 -0
  323. package/lib/setup/context-pack.test.mjs +89 -0
  324. package/lib/setup/enrich.mjs +277 -0
  325. package/lib/setup/enrich.test.mjs +115 -0
  326. package/lib/setup/enroll-from-cohort.mjs +441 -0
  327. package/lib/setup/enroll-from-cohort.test.mjs +233 -0
  328. package/lib/setup/integration.test.mjs +162 -0
  329. package/lib/setup/io.mjs +360 -0
  330. package/lib/setup/io.test.mjs +77 -0
  331. package/lib/setup/run-generator.mjs +81 -0
  332. package/lib/setup/runner.mjs +244 -0
  333. package/lib/setup/runner.test.mjs +132 -0
  334. package/lib/setup/sections/comms.mjs +173 -0
  335. package/lib/setup/sections/company.mjs +120 -0
  336. package/lib/setup/sections/enrich.mjs +138 -0
  337. package/lib/setup/sections/identity.mjs +182 -0
  338. package/lib/setup/sections/identity.test.mjs +140 -0
  339. package/lib/setup/sections/learning.mjs +153 -0
  340. package/lib/setup/sections/learning.test.mjs +81 -0
  341. package/lib/setup/sections/messaging.mjs +219 -0
  342. package/lib/setup/sections/messaging.test.mjs +127 -0
  343. package/lib/setup/sections/model.mjs +102 -0
  344. package/lib/setup/sections/operating-model.mjs +78 -0
  345. package/lib/setup/sections/org.mjs +475 -0
  346. package/lib/setup/sections/org.test.mjs +313 -0
  347. package/lib/setup/sections/orgmail.mjs +173 -0
  348. package/lib/setup/sections/orgmail.test.mjs +118 -0
  349. package/lib/setup/sections/recovery.mjs +159 -0
  350. package/lib/setup/sections/recovery.test.mjs +98 -0
  351. package/lib/setup/sections/tools.mjs +132 -0
  352. package/lib/setup/sections/verify.mjs +97 -0
  353. package/lib/setup/sot.mjs +205 -0
  354. package/lib/setup/sot.test.mjs +81 -0
  355. package/lib/setup/state.mjs +151 -0
  356. package/lib/setup/state.test.mjs +92 -0
  357. package/lib/singleton.js +229 -0
  358. package/lib/singleton.test.mjs +135 -0
  359. package/lib/telemetry/alerts.mjs +216 -0
  360. package/lib/telemetry/alerts.test.mjs +109 -0
  361. package/lib/telemetry/collect.mjs +512 -0
  362. package/lib/telemetry/collect.test.mjs +202 -0
  363. package/lib/tool-definitions-integration.test.mjs +83 -0
  364. package/lib/tool-definitions.js +738 -0
  365. package/lib/tool-definitions.test.mjs +437 -0
  366. package/lib/util/fetch-timeout.mjs +136 -0
  367. package/lib/util/fetch-timeout.test.mjs +202 -0
  368. package/lib/util/reconnect.mjs +343 -0
  369. package/lib/util/reconnect.test.mjs +369 -0
  370. package/lib/util/unhandled.mjs +205 -0
  371. package/lib/util/unhandled.test.mjs +216 -0
  372. package/lib/voice/context-loader.mjs +466 -0
  373. package/lib/voice/index.mjs +100 -0
  374. package/lib/voice/openai-realtime.mjs +510 -0
  375. package/lib/voice/outbound.mjs +542 -0
  376. package/lib/voice/outbound.test.mjs +69 -0
  377. package/lib/voice/post-call-brief.mjs +428 -0
  378. package/lib/voice/provider.mjs +52 -0
  379. package/lib/voice/session-rotation.mjs +257 -0
  380. package/lib/voice/stt.mjs +161 -0
  381. package/lib/voice/stt.test.mjs +226 -0
  382. package/lib/voice/tool-bridge.mjs +370 -0
  383. package/lib/voice/tts.mjs +104 -0
  384. package/lib/voice/twilio-sip-bridge.mjs +288 -0
  385. package/lib/voice/voice.test.mjs +990 -0
  386. package/mcp/README.md +80 -0
  387. package/package.json +151 -0
  388. package/plugins/maestro-skills/plugin.json +139 -0
  389. package/plugins/maestro-skills/skills/agents-observe.md +110 -0
  390. package/plugins/maestro-skills/skills/board-deck.md +68 -0
  391. package/plugins/maestro-skills/skills/books-close.md +77 -0
  392. package/plugins/maestro-skills/skills/brand-steward.md +121 -0
  393. package/plugins/maestro-skills/skills/calendar-plan.md +57 -0
  394. package/plugins/maestro-skills/skills/call-working-sessions.md +124 -0
  395. package/plugins/maestro-skills/skills/ccxray-diagnostics.md +91 -0
  396. package/plugins/maestro-skills/skills/claude-pace.md +61 -0
  397. package/plugins/maestro-skills/skills/code-review-graph.md +99 -0
  398. package/plugins/maestro-skills/skills/crm-pipeline.md +65 -0
  399. package/plugins/maestro-skills/skills/decision-brief.md +89 -0
  400. package/plugins/maestro-skills/skills/directory-hygiene.md +125 -0
  401. package/plugins/maestro-skills/skills/draft-comms.md +84 -0
  402. package/plugins/maestro-skills/skills/evening-wrap.md +53 -0
  403. package/plugins/maestro-skills/skills/files-find.md +65 -0
  404. package/plugins/maestro-skills/skills/generative-ui.md +228 -0
  405. package/plugins/maestro-skills/skills/hiring-triage.md +74 -0
  406. package/plugins/maestro-skills/skills/inbox-triage.md +61 -0
  407. package/plugins/maestro-skills/skills/mail-triage.md +86 -0
  408. package/plugins/maestro-skills/skills/morning-brief.md +54 -0
  409. package/plugins/maestro-skills/skills/native-artifacts.md +157 -0
  410. package/plugins/maestro-skills/skills/org-board.md +133 -0
  411. package/plugins/maestro-skills/skills/org-credential.md +68 -0
  412. package/plugins/maestro-skills/skills/org-recall.md +81 -0
  413. package/plugins/maestro-skills/skills/pipeline-review.md +76 -0
  414. package/plugins/maestro-skills/skills/regulatory-status.md +81 -0
  415. package/plugins/maestro-skills/skills/router-why.md +78 -0
  416. package/plugins/maestro-skills/skills/schedule-meeting.md +91 -0
  417. package/plugins/maestro-skills/skills/session-search.md +71 -0
  418. package/plugins/maestro-skills/skills/set-reminder.md +93 -0
  419. package/plugins/maestro-skills/skills/slack-followup.md +64 -0
  420. package/plugins/maestro-skills/skills/team-activity.md +86 -0
  421. package/plugins/maestro-skills/skills/weekly-memo.md +70 -0
  422. package/policies/action-classification.yaml +114 -0
  423. package/policies/ai-disclosure.yaml +294 -0
  424. package/policies/communication-style.md +139 -0
  425. package/policies/information-barriers.yaml +118 -0
  426. package/policies/prompt-injection-defence.yaml +138 -0
  427. package/public/assets/icon-dark.png +0 -0
  428. package/public/assets/icon-dark.svg +9 -0
  429. package/public/assets/icon-light.svg +9 -0
  430. package/public/assets/logo-dark.svg +15 -0
  431. package/public/assets/logo-light.svg +15 -0
  432. package/scaffold/.mcp.json +7 -0
  433. package/scaffold/CLAUDE.md +368 -0
  434. package/scaffold/config/agent.json +55 -0
  435. package/scaffold/config/agent.ts +76 -0
  436. package/scaffold/config/agent.ts.example +89 -0
  437. package/scaffold/config/alerts.yaml +23 -0
  438. package/scaffold/config/allowlist.yaml.example +25 -0
  439. package/scaffold/config/caller-id-map.yaml +46 -0
  440. package/scaffold/config/collective.yaml +49 -0
  441. package/scaffold/config/company.json +20 -0
  442. package/scaffold/config/known-agents.json +6 -0
  443. package/scaffold/config/learning.yaml +55 -0
  444. package/scaffold/config/model-routing.yaml.example +104 -0
  445. package/scaffold/config/org.yaml +25 -0
  446. package/scaffold/config/orgmail.yaml.example +19 -0
  447. package/scaffold/config/recovery.yaml +72 -0
  448. package/scaffold/config/secrets.yaml +27 -0
  449. package/scaffold/config/slack.yaml.example +35 -0
  450. package/scaffold/config/telegram.yaml.example +38 -0
  451. package/scaffold/config/voice.yaml.example +89 -0
  452. package/scaffold/config/whatsapp.yaml.example +39 -0
  453. package/schedules/README.md +49 -0
  454. package/schedules/triggers/backlog-executor.md +102 -0
  455. package/schedules/triggers/brand-steward.md +72 -0
  456. package/schedules/triggers/daily-evening-wrap.md +159 -0
  457. package/schedules/triggers/daily-midday-sweep.md +58 -0
  458. package/schedules/triggers/daily-morning-brief.md +55 -0
  459. package/schedules/triggers/directory-hygiene.md +81 -0
  460. package/schedules/triggers/dynamic-jobs.md +40 -0
  461. package/schedules/triggers/inbox-processor.md +115 -0
  462. package/schedules/triggers/meeting-action-capture.md +60 -0
  463. package/schedules/triggers/meeting-prep.md +69 -0
  464. package/schedules/triggers/messaging-inbound.md +50 -0
  465. package/schedules/triggers/org-pulse.md +24 -0
  466. package/schedules/triggers/quarterly-self-assessment.md +54 -0
  467. package/schedules/triggers/weekly-engineering-health.md +37 -0
  468. package/schedules/triggers/weekly-execution.md +65 -0
  469. package/schedules/triggers/weekly-hiring.md +53 -0
  470. package/schedules/triggers/weekly-priorities.md +38 -0
  471. package/schedules/triggers/weekly-strategic-memo.md +124 -0
  472. package/scripts/archive-email.sh +55 -0
  473. package/scripts/cadence/cadence-status.mjs +36 -0
  474. package/scripts/cadence/enqueue-cadence-tick.mjs +174 -0
  475. package/scripts/cadence/enqueue-cadence-tick.test.mjs +187 -0
  476. package/scripts/cadence/launchd-cadence-wrapper.sh +85 -0
  477. package/scripts/cadence/launchd-cloud-relay-wrapper.sh +95 -0
  478. package/scripts/cadence/launchd-socket-mode-wrapper.sh +95 -0
  479. package/scripts/ci/check-docs-accuracy.mjs +493 -0
  480. package/scripts/ci/check-docs-accuracy.test.mjs +409 -0
  481. package/scripts/ci/check-exports-exist.mjs +140 -0
  482. package/scripts/ci/check-files-exist.mjs +107 -0
  483. package/scripts/ci/check-no-build-artifacts.mjs +111 -0
  484. package/scripts/ci/check-no-build-artifacts.test.mjs +71 -0
  485. package/scripts/ci/check-no-confidential.mjs +198 -0
  486. package/scripts/ci/check-no-conflict-markers.mjs +169 -0
  487. package/scripts/ci/check-no-residual-identity.mjs +163 -0
  488. package/scripts/ci/check-no-residual-identity.test.mjs +89 -0
  489. package/scripts/ci/check-tarball-fidelity.mjs +205 -0
  490. package/scripts/ci/check-unresolved-tokens.mjs +83 -0
  491. package/scripts/ci/check.mjs +109 -0
  492. package/scripts/ci/check.test.mjs +194 -0
  493. package/scripts/ci/run-coverage.mjs +82 -0
  494. package/scripts/ci/run-tests.mjs +71 -0
  495. package/scripts/cloud-relay/README.md +59 -0
  496. package/scripts/cloud-relay/index.mjs +233 -0
  497. package/scripts/cloud-relay/package.json +15 -0
  498. package/scripts/cloud-relay/railway.json +13 -0
  499. package/scripts/cloud-relay/voice/README.md +94 -0
  500. package/scripts/cloud-relay/voice/package-lock.json +39 -0
  501. package/scripts/cloud-relay/voice/package.json +16 -0
  502. package/scripts/cloud-relay/voice/railway.json +13 -0
  503. package/scripts/cloud-relay/voice/server.mjs +532 -0
  504. package/scripts/collective/hook-runner.mjs +211 -0
  505. package/scripts/collective/hook-runner.test.mjs +90 -0
  506. package/scripts/collective/org-pulse.mjs +72 -0
  507. package/scripts/collective/org-sync.mjs +61 -0
  508. package/scripts/collective/recall.mjs +45 -0
  509. package/scripts/collective/who.mjs +30 -0
  510. package/scripts/comms-monitor.sh +288 -0
  511. package/scripts/configure-whatsapp-sandbox.sh +201 -0
  512. package/scripts/continuous-monitor.sh +91 -0
  513. package/scripts/cost/fleet-digest.mjs +407 -0
  514. package/scripts/cost/fleet-digest.test.mjs +207 -0
  515. package/scripts/cost/track-claude-usage.mjs +169 -0
  516. package/scripts/daemon/agent-daemon.mjs +989 -0
  517. package/scripts/daemon/agent-daemon.test.mjs +525 -0
  518. package/scripts/daemon/cadence-consumer-governance.test.mjs +220 -0
  519. package/scripts/daemon/cadence-consumer.mjs +1080 -0
  520. package/scripts/daemon/cadence-consumer.test.mjs +770 -0
  521. package/scripts/daemon/cadence-handlers.mjs +1121 -0
  522. package/scripts/daemon/cadence-handlers.test.mjs +617 -0
  523. package/scripts/daemon/classifier.mjs +704 -0
  524. package/scripts/daemon/classifier.test.mjs +238 -0
  525. package/scripts/daemon/classify-kind.mjs +54 -0
  526. package/scripts/daemon/classify-kind.test.mjs +40 -0
  527. package/scripts/daemon/context-compiler.mjs +605 -0
  528. package/scripts/daemon/context-compiler.test.mjs +300 -0
  529. package/scripts/daemon/dispatcher-cooldown.test.mjs +122 -0
  530. package/scripts/daemon/dispatcher-governance.test.mjs +886 -0
  531. package/scripts/daemon/dispatcher.mjs +1516 -0
  532. package/scripts/daemon/health.mjs +72 -0
  533. package/scripts/daemon/inbox-deferral.mjs +210 -0
  534. package/scripts/daemon/inbox-deferral.test.mjs +242 -0
  535. package/scripts/daemon/integration.test.mjs +149 -0
  536. package/scripts/daemon/launchd-wrapper-generic.sh +96 -0
  537. package/scripts/daemon/launchd-wrapper-slack-events.sh +37 -0
  538. package/scripts/daemon/launchd-wrapper.sh +91 -0
  539. package/scripts/daemon/lib/session-router.mjs +274 -0
  540. package/scripts/daemon/lib/session-router.test.mjs +295 -0
  541. package/scripts/daemon/maestro-daemon.mjs +275 -0
  542. package/scripts/daemon/prompt-builder.mjs +685 -0
  543. package/scripts/daemon/prompt-builder.test.mjs +213 -0
  544. package/scripts/daemon/responder.mjs +854 -0
  545. package/scripts/daemon/session-lock.mjs +721 -0
  546. package/scripts/daemon/session-lock.test.mjs +252 -0
  547. package/scripts/daemon/session-outcomes.mjs +640 -0
  548. package/scripts/daemon/session-outcomes.test.mjs +533 -0
  549. package/scripts/daemon/typing-registry.mjs +90 -0
  550. package/scripts/daemon/typing-registry.test.mjs +77 -0
  551. package/scripts/daemon/voice-webhook-server.mjs +804 -0
  552. package/scripts/decisions/capture-decision.mjs +116 -0
  553. package/scripts/disclosure_assessment.py +873 -0
  554. package/scripts/disclosure_boundaries.py +562 -0
  555. package/scripts/email-signature-principal.html +52 -0
  556. package/scripts/email-signature.html +60 -0
  557. package/scripts/email_quote_thread.py +167 -0
  558. package/scripts/email_thread_dedup.py +362 -0
  559. package/scripts/emergency-stop.sh +81 -0
  560. package/scripts/healthcheck.sh +116 -0
  561. package/scripts/hooks/block-mcp-cohort-send.sh +15 -0
  562. package/scripts/hooks/block-mcp-slack-send.sh +7 -0
  563. package/scripts/hooks/post-action-log.sh +126 -0
  564. package/scripts/hooks/pre-send-audit.sh +174 -0
  565. package/scripts/hooks/pre-send-audit.test.mjs +215 -0
  566. package/scripts/hooks/session-end-log.sh +27 -0
  567. package/scripts/hooks/session-start-banner.sh +115 -0
  568. package/scripts/huddle/audio-bridge.mjs +664 -0
  569. package/scripts/huddle/boot-slack-cdp.sh +102 -0
  570. package/scripts/huddle/huddle-controller.mjs +942 -0
  571. package/scripts/huddle/huddle-server.mjs +1229 -0
  572. package/scripts/huddle/launch-slack.sh +232 -0
  573. package/scripts/huddle/openai-realtime-bridge.mjs +462 -0
  574. package/scripts/huddle/package-lock.json +62 -0
  575. package/scripts/huddle/package.json +22 -0
  576. package/scripts/huddle/setup-audio.sh +239 -0
  577. package/scripts/huddle/start-call.mjs +318 -0
  578. package/scripts/huddle/test-pipeline.mjs +263 -0
  579. package/scripts/learning/consolidate-skills.mjs +72 -0
  580. package/scripts/learning/session-search.mjs +125 -0
  581. package/scripts/llm_email_dedup.py +442 -0
  582. package/scripts/local-triggers/generate-plists.sh +432 -0
  583. package/scripts/local-triggers/generate-plists.test.mjs +413 -0
  584. package/scripts/local-triggers/install-all.sh +49 -0
  585. package/scripts/local-triggers/plists/.gitkeep +0 -0
  586. package/scripts/local-triggers/run-trigger.sh +63 -0
  587. package/scripts/local-triggers/templates/rag-reindex.plist.template +47 -0
  588. package/scripts/local-triggers/templates/voice-relay-poller.plist.template +54 -0
  589. package/scripts/local-triggers/templates/voice-tunnel.plist.template +55 -0
  590. package/scripts/local-triggers/templates/voice-webhook.plist.template +51 -0
  591. package/scripts/maintenance/backup-to-cloud.sh +124 -0
  592. package/scripts/maintenance/health-check.sh +377 -0
  593. package/scripts/media-generation/README.md +105 -0
  594. package/scripts/media-generation/gemini-image-client.mjs +173 -0
  595. package/scripts/media-generation/generate-assets.mjs +289 -0
  596. package/scripts/media-generation/veo-video-client.mjs +219 -0
  597. package/scripts/org/send-orgmail.mjs +227 -0
  598. package/scripts/outbound-dedup-cleanup.sh +43 -0
  599. package/scripts/outbound-dedup.sh +477 -0
  600. package/scripts/outbound_dedup.py +115 -0
  601. package/scripts/parse-voice-transcript.mjs +481 -0
  602. package/scripts/pdf-generation/README.md +63 -0
  603. package/scripts/pdf-generation/build-document.mjs +247 -0
  604. package/scripts/pdf-generation/templates/board-pack.latex +136 -0
  605. package/scripts/pdf-generation/templates/corporate-letter.latex +126 -0
  606. package/scripts/pdf-generation/templates/memo.latex +114 -0
  607. package/scripts/poll-slack-events.sh +35 -0
  608. package/scripts/poller/calendar-poller.mjs +12 -0
  609. package/scripts/poller/gmail-poller.mjs +192 -0
  610. package/scripts/poller/imap-client.mjs +289 -0
  611. package/scripts/poller/inbox-scan-poller.mjs +156 -0
  612. package/scripts/poller/inbox-scan-poller.test.mjs +231 -0
  613. package/scripts/poller/index.mjs +73 -0
  614. package/scripts/poller/intra-session-check.mjs +285 -0
  615. package/scripts/poller/lib/cloud-relay-dedup.mjs +88 -0
  616. package/scripts/poller/lib/cloud-relay-dedup.test.mjs +133 -0
  617. package/scripts/poller/lib/slash-command-handlers.mjs +177 -0
  618. package/scripts/poller/secondary-gmail-poller.mjs +132 -0
  619. package/scripts/poller/slack-cloud-relay-client.mjs +368 -0
  620. package/scripts/poller/slack-poller.mjs +854 -0
  621. package/scripts/poller/slack-socket-mode.mjs +917 -0
  622. package/scripts/poller/slack-socket-mode.test.mjs +753 -0
  623. package/scripts/poller/trigger.mjs +75 -0
  624. package/scripts/poller/utils.mjs +371 -0
  625. package/scripts/poller/voice-cloud-relay-client.mjs +179 -0
  626. package/scripts/poller/voice-poller.mjs +236 -0
  627. package/scripts/poller-launchd/install.sh +66 -0
  628. package/scripts/poller-launchd/poller.plist.template +40 -0
  629. package/scripts/poller-launchd/whatsapp-handler.plist.template +39 -0
  630. package/scripts/post-interaction-indexer.py +1598 -0
  631. package/scripts/pre-draft-context.py +994 -0
  632. package/scripts/pre_draft_lookup.py +258 -0
  633. package/scripts/rag/build-index.mjs +47 -0
  634. package/scripts/rag/ingest.mjs +111 -0
  635. package/scripts/rag/search.mjs +119 -0
  636. package/scripts/rag-indexer.py +629 -0
  637. package/scripts/restore-from-backup.sh +248 -0
  638. package/scripts/restore-from-backup.test.mjs +178 -0
  639. package/scripts/resume-operations.sh +80 -0
  640. package/scripts/search-secondary-inbox.py +181 -0
  641. package/scripts/secondary-inbox-poller.py +437 -0
  642. package/scripts/self-optimization/compute-metrics.py +398 -0
  643. package/scripts/send-email-as-principal.py +369 -0
  644. package/scripts/send-email-threaded.py +392 -0
  645. package/scripts/send-email-with-attachment.py +377 -0
  646. package/scripts/send-email.sh +131 -0
  647. package/scripts/send-sms.sh +175 -0
  648. package/scripts/send-whatsapp.sh +292 -0
  649. package/scripts/session-start.sh +106 -0
  650. package/scripts/setup/boot-claude-session.sh +94 -0
  651. package/scripts/setup/configure-macos.sh +674 -0
  652. package/scripts/setup/configure-twilio-sip-trunk.mjs +207 -0
  653. package/scripts/setup/configure-voice-tunnel.mjs +182 -0
  654. package/scripts/setup/generate-agent-env.mjs +92 -0
  655. package/scripts/setup/generate-agent-package-json.mjs +222 -0
  656. package/scripts/setup/generate-agent-package-json.test.mjs +143 -0
  657. package/scripts/setup/generate-autonomy.mjs +60 -0
  658. package/scripts/setup/generate-backlog.mjs +101 -0
  659. package/scripts/setup/generate-cadences.mjs +92 -0
  660. package/scripts/setup/generate-capability.mjs +162 -0
  661. package/scripts/setup/generate-charter.mjs +76 -0
  662. package/scripts/setup/generate-comms.mjs +59 -0
  663. package/scripts/setup/generate-company.mjs +92 -0
  664. package/scripts/setup/init-agent.sh +547 -0
  665. package/scripts/setup/init-agent.test.mjs +151 -0
  666. package/scripts/setup/init-archetype.mjs +74 -0
  667. package/scripts/setup/init-backup.mjs +54 -0
  668. package/scripts/setup/init-cadence-bus.mjs +60 -0
  669. package/scripts/setup/init-channel-bus.mjs +46 -0
  670. package/scripts/setup/init-cost-tracking.mjs +45 -0
  671. package/scripts/setup/init-decision-capture.mjs +66 -0
  672. package/scripts/setup/init-known-agents.mjs +57 -0
  673. package/scripts/setup/init-learning.mjs +70 -0
  674. package/scripts/setup/init-memory-executive.mjs +45 -0
  675. package/scripts/setup/init-model-router.mjs +124 -0
  676. package/scripts/setup/init-rag.mjs +174 -0
  677. package/scripts/setup/init-session-router.mjs +38 -0
  678. package/scripts/setup/init-slack-socket-mode.mjs +260 -0
  679. package/scripts/setup/init-telegram.mjs +165 -0
  680. package/scripts/setup/init-voice-realtime.mjs +204 -0
  681. package/scripts/setup/init-whatsapp-baileys.mjs +77 -0
  682. package/scripts/setup/install-dev-tools.sh +150 -0
  683. package/scripts/setup/lib/install-plist.mjs +95 -0
  684. package/scripts/setup/migrate-agent-to-sot.mjs +192 -0
  685. package/scripts/setup/render-environment-yaml.mjs +133 -0
  686. package/scripts/slack-events-ctl.sh +177 -0
  687. package/scripts/slack-events-server.mjs +1045 -0
  688. package/scripts/slack-react.mjs +89 -0
  689. package/scripts/slack-responded.sh +232 -0
  690. package/scripts/slack-send.sh +287 -0
  691. package/scripts/slack-typing.mjs +196 -0
  692. package/scripts/slack-upload-v2.py +95 -0
  693. package/scripts/sms-handler.mjs +450 -0
  694. package/scripts/spawn-session.sh +120 -0
  695. package/scripts/sync-protocol.mjs +217 -0
  696. package/scripts/system-verify.sh +184 -0
  697. package/scripts/test-email-thread-dedup.py +239 -0
  698. package/scripts/test-information-barriers.py +484 -0
  699. package/scripts/test-llm-email-dedup.py +251 -0
  700. package/scripts/test-pre-draft-integration.py +203 -0
  701. package/scripts/test-rag-phase2.sh +442 -0
  702. package/scripts/test-rag-search.sh +251 -0
  703. package/scripts/test-voice-parser.mjs +316 -0
  704. package/scripts/user-context-search.py +659 -0
  705. package/scripts/validate_outbound.py +1504 -0
  706. package/scripts/watchdog/ai.maestro.memory-watchdog.plist +41 -0
  707. package/scripts/watchdog/force-reboot.sh +157 -0
  708. package/scripts/watchdog/memory-watchdog.sh +473 -0
  709. package/scripts/whatsapp-handler.mjs +538 -0
  710. package/teams/desktop-operations.yaml +34 -0
  711. package/teams/executive-office.yaml +27 -0
  712. package/teams/legal-and-regulatory.yaml +24 -0
  713. package/teams/platform-and-engineering.yaml +23 -0
  714. package/teams/strategy-and-growth.yaml +29 -0
  715. package/workflows/continuous/backlog-executor.yaml +141 -0
  716. package/workflows/continuous/inbound-monitor.yaml +168 -0
  717. package/workflows/daily/applicant-triage.yaml +197 -0
  718. package/workflows/daily/comms-triage.yaml +80 -0
  719. package/workflows/daily/evening-wrap.yaml +105 -0
  720. package/workflows/daily/morning-brief.yaml +164 -0
  721. package/workflows/daily/slack-followup-sweep.yaml +87 -0
  722. package/workflows/event-driven/README.md +50 -0
  723. package/workflows/event-driven/agent-failure-investigation.yaml +137 -0
  724. package/workflows/event-driven/pr-review.yaml +107 -0
  725. package/workflows/monthly/board-readiness.yaml +76 -0
  726. package/workflows/quarterly/strategic-scenario-analysis.yaml +85 -0
  727. package/workflows/session-protocol.md +171 -0
  728. package/workflows/weekly/engineering-health.yaml +154 -0
  729. package/workflows/weekly/hiring-review.yaml +169 -0
  730. package/workflows/weekly/rollup-pipeline-review.yaml +76 -0
  731. package/workflows/weekly/strategic-memo.yaml +79 -0
@@ -0,0 +1,629 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ rag-indexer.py — SQLite FTS5 indexer for per-user ring-fenced RAG (Phase 2)
4
+
5
+ Indexes all content sources into a SQLite FTS5 database for fast full-text search
6
+ with per-user access control (ring-fencing).
7
+
8
+ Sources indexed:
9
+ - memory/interactions/**/*.jsonl (Slack DMs, channels, emails — each line = one doc)
10
+ - knowledge/sources/*.yaml (public)
11
+ - knowledge/decisions/*.yaml (leadership+)
12
+ - knowledge/syntheses/*.yaml (public, except strategy-state.yaml = ceo)
13
+ - state/queues/*.yaml (leadership+)
14
+
15
+ Each document gets a user_scope:
16
+ - "public" — visible to all authenticated users
17
+ - "leadership" — visible to leadership + CEO
18
+ - "ceo" — CEO only
19
+ - "{user-slug}" — private to that specific user (DMs, emails)
20
+
21
+ Incremental indexing: tracks file modification times in state/rag/index-state.json.
22
+ Only re-indexes files that have changed since last run.
23
+
24
+ Usage:
25
+ python3 scripts/rag-indexer.py # incremental index
26
+ python3 scripts/rag-indexer.py --full # full re-index (drop + rebuild)
27
+ python3 scripts/rag-indexer.py --stats # show index statistics
28
+ python3 scripts/rag-indexer.py --db /path/to/db # custom DB path
29
+
30
+ Author: Northwind
31
+ Date: 2026-04-03
32
+ """
33
+
34
+ import argparse
35
+ import hashlib
36
+ import json
37
+ import os
38
+ import re
39
+ import sqlite3
40
+ import sys
41
+ import time
42
+ from pathlib import Path
43
+
44
+ # ── Constants ──────────────────────────────────────────────────────────────────
45
+
46
+ BASE_DIR = Path(os.environ.get("AGENT_DIR", str(Path(__file__).resolve().parent.parent))) # agent repo root
47
+ DEFAULT_DB_PATH = BASE_DIR / "state" / "rag" / "search.db"
48
+ INDEX_STATE_PATH = BASE_DIR / "state" / "rag" / "index-state.json"
49
+
50
+
51
+ # ── Database Schema ────────────────────────────────────────────────────────────
52
+
53
+ SCHEMA_SQL = """
54
+ CREATE TABLE IF NOT EXISTS documents (
55
+ id TEXT PRIMARY KEY,
56
+ source_type TEXT NOT NULL,
57
+ source_path TEXT NOT NULL,
58
+ user_scope TEXT NOT NULL,
59
+ content TEXT NOT NULL,
60
+ title TEXT,
61
+ timestamp TEXT,
62
+ metadata_json TEXT
63
+ );
64
+
65
+ CREATE INDEX IF NOT EXISTS idx_user_scope ON documents(user_scope);
66
+ CREATE INDEX IF NOT EXISTS idx_source_type ON documents(source_type);
67
+ CREATE INDEX IF NOT EXISTS idx_source_path ON documents(source_path);
68
+ CREATE INDEX IF NOT EXISTS idx_timestamp ON documents(timestamp);
69
+ """
70
+
71
+ FTS_SQL = """
72
+ CREATE VIRTUAL TABLE IF NOT EXISTS documents_fts USING fts5(
73
+ content,
74
+ title,
75
+ content='documents',
76
+ content_rowid='rowid'
77
+ );
78
+ """
79
+
80
+ # Triggers to keep FTS in sync with documents table
81
+ TRIGGER_SQL = """
82
+ CREATE TRIGGER IF NOT EXISTS documents_ai AFTER INSERT ON documents BEGIN
83
+ INSERT INTO documents_fts(rowid, content, title)
84
+ VALUES (new.rowid, new.content, new.title);
85
+ END;
86
+
87
+ CREATE TRIGGER IF NOT EXISTS documents_ad AFTER DELETE ON documents BEGIN
88
+ INSERT INTO documents_fts(documents_fts, rowid, content, title)
89
+ VALUES ('delete', old.rowid, old.content, old.title);
90
+ END;
91
+
92
+ CREATE TRIGGER IF NOT EXISTS documents_au AFTER UPDATE ON documents BEGIN
93
+ INSERT INTO documents_fts(documents_fts, rowid, content, title)
94
+ VALUES ('delete', old.rowid, old.content, old.title);
95
+ INSERT INTO documents_fts(rowid, content, title)
96
+ VALUES (new.rowid, new.content, new.title);
97
+ END;
98
+ """
99
+
100
+
101
+ # ── Database Initialization ───────────────────────────────────────────────────
102
+
103
+ def init_db(db_path):
104
+ """Create or open the SQLite database with FTS5 schema."""
105
+ db_path.parent.mkdir(parents=True, exist_ok=True)
106
+ conn = sqlite3.connect(str(db_path))
107
+ conn.execute("PRAGMA journal_mode=WAL")
108
+ conn.execute("PRAGMA synchronous=NORMAL")
109
+
110
+ # Create main table and indexes
111
+ conn.executescript(SCHEMA_SQL)
112
+
113
+ # Create FTS5 virtual table
114
+ conn.executescript(FTS_SQL)
115
+
116
+ # Create sync triggers
117
+ conn.executescript(TRIGGER_SQL)
118
+
119
+ conn.commit()
120
+ return conn
121
+
122
+
123
+ def drop_and_rebuild(db_path):
124
+ """Drop all tables and recreate from scratch."""
125
+ if db_path.exists():
126
+ os.remove(str(db_path))
127
+ # Also clear the index state
128
+ if INDEX_STATE_PATH.exists():
129
+ os.remove(str(INDEX_STATE_PATH))
130
+ return init_db(db_path)
131
+
132
+
133
+ # ── Index State Management ────────────────────────────────────────────────────
134
+
135
+ def load_index_state():
136
+ """Load the incremental indexing state (file modification times)."""
137
+ if INDEX_STATE_PATH.exists():
138
+ with open(INDEX_STATE_PATH, "r") as f:
139
+ return json.load(f)
140
+ return {"files": {}}
141
+
142
+
143
+ def save_index_state(state):
144
+ """Save the incremental indexing state."""
145
+ INDEX_STATE_PATH.parent.mkdir(parents=True, exist_ok=True)
146
+ with open(INDEX_STATE_PATH, "w") as f:
147
+ json.dump(state, f, indent=2)
148
+
149
+
150
+ # ── Source Classification ─────────────────────────────────────────────────────
151
+
152
+ def classify_source(rel_path):
153
+ """
154
+ Determine source_type and user_scope from a file's relative path.
155
+
156
+ Returns (source_type, user_scope) where:
157
+ source_type: "slack_dm", "slack_channel", "email", "knowledge", "decision",
158
+ "synthesis", "queue"
159
+ user_scope: "public", "leadership", "ceo", or a user slug for private data
160
+ """
161
+ parts = Path(rel_path).parts
162
+
163
+ # memory/interactions/slack/dm-{user-slug}/**
164
+ if (len(parts) >= 4 and parts[0] == "memory" and parts[1] == "interactions"
165
+ and parts[2] == "slack"):
166
+ dir_name = parts[3]
167
+ if dir_name.startswith("dm-"):
168
+ user_slug = dir_name[3:] # "dm-alex-chen" -> "alex-chen"
169
+ return "slack_dm", user_slug
170
+ elif dir_name == "dm":
171
+ # memory/interactions/slack/dm/{something} — treat as private
172
+ if len(parts) >= 5:
173
+ return "slack_dm", parts[4]
174
+ return "slack_dm", "unknown"
175
+ else:
176
+ # Channel (ceo-office, channels, etc.)
177
+ return "slack_channel", "leadership"
178
+
179
+ # memory/interactions/slack/{user-slug}/** (alternate DM format like alex-chen/)
180
+ if (len(parts) >= 4 and parts[0] == "memory" and parts[1] == "interactions"
181
+ and parts[2] == "slack"):
182
+ dir_name = parts[3]
183
+ # Check if it looks like a user slug (contains a hyphen, isn't a known channel)
184
+ known_channels = {"ceo-office", "channels", "dm", "general", "engineering"}
185
+ if dir_name not in known_channels and "-" in dir_name:
186
+ return "slack_dm", dir_name
187
+
188
+ # memory/interactions/email/{user-slug}/**
189
+ if (len(parts) >= 4 and parts[0] == "memory" and parts[1] == "interactions"
190
+ and parts[2] == "email"):
191
+ user_slug = parts[3]
192
+ return "email", user_slug
193
+
194
+ # knowledge/sources/*.yaml — public
195
+ if (len(parts) >= 2 and parts[0] == "knowledge" and parts[1] == "sources"):
196
+ return "knowledge", "public"
197
+
198
+ # knowledge/decisions/**/*.yaml — leadership+
199
+ if (len(parts) >= 2 and parts[0] == "knowledge" and parts[1] == "decisions"):
200
+ return "decision", "leadership"
201
+
202
+ # knowledge/syntheses/*.yaml — public, except strategy-state = ceo
203
+ if (len(parts) >= 2 and parts[0] == "knowledge" and parts[1] == "syntheses"):
204
+ filename = parts[-1]
205
+ if filename == "strategy-state.yaml":
206
+ return "synthesis", "ceo"
207
+ return "synthesis", "public"
208
+
209
+ # state/queues/*.yaml — leadership+
210
+ if (len(parts) >= 2 and parts[0] == "state" and parts[1] == "queues"):
211
+ return "queue", "leadership"
212
+
213
+ # Fallback
214
+ return "unknown", "public"
215
+
216
+
217
+ # ── Document ID Generation ────────────────────────────────────────────────────
218
+
219
+ def make_doc_id(source_path, line_num=None):
220
+ """
221
+ Generate a deterministic document ID.
222
+ For JSONL: source_path:line_number
223
+ For YAML/other: hash of source_path
224
+ """
225
+ if line_num is not None:
226
+ return f"{source_path}:{line_num}"
227
+ return hashlib.sha256(source_path.encode()).hexdigest()[:16]
228
+
229
+
230
+ # ── Content Parsing ───────────────────────────────────────────────────────────
231
+
232
+ def parse_jsonl_file(filepath, rel_path, source_type, user_scope):
233
+ """
234
+ Parse a JSONL file into individual documents.
235
+ Each line becomes one document.
236
+ """
237
+ documents = []
238
+ try:
239
+ with open(filepath, "r") as f:
240
+ for line_num, line in enumerate(f, 1):
241
+ line = line.strip()
242
+ if not line:
243
+ continue
244
+ try:
245
+ record = json.loads(line)
246
+ except json.JSONDecodeError:
247
+ continue
248
+
249
+ # Extract content from various field names
250
+ content = (record.get("content")
251
+ or record.get("text")
252
+ or record.get("content_preview")
253
+ or "")
254
+
255
+ if not content:
256
+ continue
257
+
258
+ # Build a rich text representation
259
+ sender = record.get("sender") or record.get("from") or ""
260
+ channel = record.get("channel") or ""
261
+ direction = record.get("direction") or ""
262
+ action = record.get("action_taken") or ""
263
+ context = record.get("context") or ""
264
+
265
+ # Build title from available fields
266
+ title_parts = []
267
+ if sender:
268
+ title_parts.append(f"From: {sender}")
269
+ if channel:
270
+ title_parts.append(f"Channel: {channel}")
271
+ if direction:
272
+ title_parts.append(f"({direction})")
273
+ title = " | ".join(title_parts) if title_parts else None
274
+
275
+ # Include action_taken and context in searchable content
276
+ full_content = content
277
+ if action:
278
+ full_content += f"\nAction: {action}"
279
+ if context:
280
+ full_content += f"\nContext: {context}"
281
+
282
+ # Extract timestamp
283
+ timestamp = (record.get("timestamp")
284
+ or record.get("processed_at")
285
+ or record.get("ts")
286
+ or "")
287
+
288
+ # Metadata
289
+ metadata = {
290
+ "sender": sender,
291
+ "channel": channel,
292
+ "direction": direction,
293
+ "classification": record.get("classification", ""),
294
+ "ref": record.get("ref", ""),
295
+ "message_ts": record.get("message_ts", ""),
296
+ }
297
+
298
+ doc_id = make_doc_id(rel_path, line_num)
299
+ documents.append({
300
+ "id": doc_id,
301
+ "source_type": source_type,
302
+ "source_path": rel_path,
303
+ "user_scope": user_scope,
304
+ "content": full_content,
305
+ "title": title,
306
+ "timestamp": timestamp,
307
+ "metadata_json": json.dumps(metadata),
308
+ })
309
+
310
+ except Exception as e:
311
+ print(f" Warning: Failed to parse {filepath}: {e}", file=sys.stderr)
312
+
313
+ return documents
314
+
315
+
316
+ def parse_yaml_file(filepath, rel_path, source_type, user_scope):
317
+ """
318
+ Parse a YAML file into a single document.
319
+ Uses the full file content as the searchable text.
320
+ For simplicity and no-dependency constraint, we read the raw text.
321
+ """
322
+ documents = []
323
+ try:
324
+ with open(filepath, "r") as f:
325
+ content = f.read()
326
+
327
+ if not content.strip():
328
+ return documents
329
+
330
+ # Extract a title from the first comment line or YAML key
331
+ title = None
332
+ for line in content.split("\n"):
333
+ line = line.strip()
334
+ if line.startswith("# ") and not line.startswith("# =="):
335
+ title = line[2:].strip()
336
+ break
337
+ elif line and not line.startswith("#") and ":" in line:
338
+ title = line.split(":")[0].strip()
339
+ break
340
+
341
+ # For large YAML files, split into sections by top-level keys
342
+ # But keep it simple: one document per file
343
+ doc_id = make_doc_id(rel_path)
344
+ documents.append({
345
+ "id": doc_id,
346
+ "source_type": source_type,
347
+ "source_path": rel_path,
348
+ "user_scope": user_scope,
349
+ "content": content[:50000], # cap at 50K chars
350
+ "title": title,
351
+ "timestamp": "",
352
+ "metadata_json": json.dumps({"filename": Path(filepath).name}),
353
+ })
354
+
355
+ except Exception as e:
356
+ print(f" Warning: Failed to parse {filepath}: {e}", file=sys.stderr)
357
+
358
+ return documents
359
+
360
+
361
+ def parse_markdown_file(filepath, rel_path, source_type, user_scope):
362
+ """Parse a Markdown file into a single document."""
363
+ documents = []
364
+ try:
365
+ with open(filepath, "r") as f:
366
+ content = f.read()
367
+
368
+ if not content.strip():
369
+ return documents
370
+
371
+ # Extract title from first heading
372
+ title = None
373
+ for line in content.split("\n"):
374
+ line = line.strip()
375
+ if line.startswith("# "):
376
+ title = line[2:].strip()
377
+ break
378
+
379
+ doc_id = make_doc_id(rel_path)
380
+ documents.append({
381
+ "id": doc_id,
382
+ "source_type": source_type,
383
+ "source_path": rel_path,
384
+ "user_scope": user_scope,
385
+ "content": content[:50000],
386
+ "title": title,
387
+ "timestamp": "",
388
+ "metadata_json": json.dumps({"filename": Path(filepath).name}),
389
+ })
390
+
391
+ except Exception as e:
392
+ print(f" Warning: Failed to parse {filepath}: {e}", file=sys.stderr)
393
+
394
+ return documents
395
+
396
+
397
+ # ── File Discovery ────────────────────────────────────────────────────────────
398
+
399
+ def discover_files():
400
+ """
401
+ Walk the configured source directories and return a list of
402
+ (absolute_path, relative_path) tuples for indexable files.
403
+ """
404
+ source_dirs = [
405
+ "memory/interactions",
406
+ "knowledge/sources",
407
+ "knowledge/decisions",
408
+ "knowledge/syntheses",
409
+ "state/queues",
410
+ ]
411
+
412
+ indexable_extensions = {".jsonl", ".yaml", ".yml", ".md"}
413
+ files = []
414
+
415
+ for src_dir in source_dirs:
416
+ abs_dir = BASE_DIR / src_dir
417
+ if not abs_dir.exists():
418
+ continue
419
+
420
+ for root, _dirs, filenames in os.walk(str(abs_dir)):
421
+ for fname in filenames:
422
+ ext = os.path.splitext(fname)[1].lower()
423
+ if ext not in indexable_extensions:
424
+ continue
425
+
426
+ abs_path = os.path.join(root, fname)
427
+ rel_path = os.path.relpath(abs_path, BASE_DIR)
428
+ files.append((abs_path, rel_path))
429
+
430
+ return files
431
+
432
+
433
+ # ── Indexing Pipeline ─────────────────────────────────────────────────────────
434
+
435
+ def index_file(filepath, rel_path):
436
+ """
437
+ Parse a file into documents based on its type and path.
438
+ Returns a list of document dicts.
439
+ """
440
+ source_type, user_scope = classify_source(rel_path)
441
+ ext = os.path.splitext(filepath)[1].lower()
442
+
443
+ if ext == ".jsonl":
444
+ return parse_jsonl_file(filepath, rel_path, source_type, user_scope)
445
+ elif ext in (".yaml", ".yml"):
446
+ return parse_yaml_file(filepath, rel_path, source_type, user_scope)
447
+ elif ext == ".md":
448
+ return parse_markdown_file(filepath, rel_path, source_type, user_scope)
449
+ else:
450
+ return []
451
+
452
+
453
+ def upsert_documents(conn, documents):
454
+ """Insert or replace documents in the database."""
455
+ if not documents:
456
+ return 0
457
+
458
+ # We need to handle FTS sync carefully:
459
+ # 1. Delete existing docs by ID (triggers will remove from FTS)
460
+ # 2. Insert new docs (triggers will add to FTS)
461
+ count = 0
462
+ for doc in documents:
463
+ # Delete existing (triggers handle FTS cleanup)
464
+ conn.execute("DELETE FROM documents WHERE id = ?", (doc["id"],))
465
+
466
+ # Insert new
467
+ conn.execute(
468
+ """INSERT INTO documents (id, source_type, source_path, user_scope,
469
+ content, title, timestamp, metadata_json)
470
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
471
+ (doc["id"], doc["source_type"], doc["source_path"], doc["user_scope"],
472
+ doc["content"], doc["title"], doc["timestamp"], doc["metadata_json"]),
473
+ )
474
+ count += 1
475
+
476
+ return count
477
+
478
+
479
+ def remove_stale_documents(conn, source_path):
480
+ """Remove all documents from a given source path (before re-indexing it)."""
481
+ conn.execute("DELETE FROM documents WHERE source_path = ?", (source_path,))
482
+
483
+
484
+ def run_index(db_path, full=False):
485
+ """
486
+ Run the indexing pipeline.
487
+ If full=True, drops and rebuilds the entire index.
488
+ Otherwise, only re-indexes changed files.
489
+ """
490
+ if full:
491
+ print("Full re-index: dropping and rebuilding database...")
492
+ conn = drop_and_rebuild(db_path)
493
+ state = {"files": {}}
494
+ else:
495
+ conn = init_db(db_path)
496
+ state = load_index_state()
497
+
498
+ files = discover_files()
499
+ print(f"Discovered {len(files)} indexable files")
500
+
501
+ indexed_count = 0
502
+ doc_count = 0
503
+ skipped_count = 0
504
+
505
+ for abs_path, rel_path in files:
506
+ try:
507
+ mtime = os.path.getmtime(abs_path)
508
+ except OSError:
509
+ continue
510
+
511
+ # Check if file has changed since last index
512
+ last_mtime = state["files"].get(rel_path, 0)
513
+ if not full and mtime <= last_mtime:
514
+ skipped_count += 1
515
+ continue
516
+
517
+ # Remove old documents from this file
518
+ remove_stale_documents(conn, rel_path)
519
+
520
+ # Parse and index
521
+ documents = index_file(abs_path, rel_path)
522
+ if documents:
523
+ count = upsert_documents(conn, documents)
524
+ doc_count += count
525
+
526
+ # Update state
527
+ state["files"][rel_path] = mtime
528
+ indexed_count += 1
529
+
530
+ conn.commit()
531
+ conn.close()
532
+
533
+ # Save index state
534
+ save_index_state(state)
535
+
536
+ print(f"Indexed {indexed_count} files ({doc_count} documents)")
537
+ if skipped_count > 0:
538
+ print(f"Skipped {skipped_count} unchanged files")
539
+
540
+ return indexed_count, doc_count
541
+
542
+
543
+ # ── Statistics ────────────────────────────────────────────────────────────────
544
+
545
+ def show_stats(db_path):
546
+ """Show index statistics."""
547
+ if not db_path.exists():
548
+ print("No index database found. Run indexer first.")
549
+ return
550
+
551
+ conn = sqlite3.connect(str(db_path))
552
+
553
+ # Total documents
554
+ total = conn.execute("SELECT COUNT(*) FROM documents").fetchone()[0]
555
+ print(f"Total documents: {total}")
556
+
557
+ # By source type
558
+ print("\nBy source type:")
559
+ rows = conn.execute(
560
+ "SELECT source_type, COUNT(*) FROM documents GROUP BY source_type ORDER BY COUNT(*) DESC"
561
+ ).fetchall()
562
+ for source_type, count in rows:
563
+ print(f" {source_type}: {count}")
564
+
565
+ # By user scope
566
+ print("\nBy user scope:")
567
+ rows = conn.execute(
568
+ "SELECT user_scope, COUNT(*) FROM documents GROUP BY user_scope ORDER BY COUNT(*) DESC"
569
+ ).fetchall()
570
+ for scope, count in rows:
571
+ print(f" {scope}: {count}")
572
+
573
+ # By source path (top 10)
574
+ print("\nTop 10 source files:")
575
+ rows = conn.execute(
576
+ "SELECT source_path, COUNT(*) FROM documents GROUP BY source_path ORDER BY COUNT(*) DESC LIMIT 10"
577
+ ).fetchall()
578
+ for path, count in rows:
579
+ print(f" {path}: {count}")
580
+
581
+ # Index state
582
+ state = load_index_state()
583
+ print(f"\nTracked files in index state: {len(state.get('files', {}))}")
584
+
585
+ conn.close()
586
+
587
+
588
+ # ── Main ───────────────────────────────────────────────────────────────────────
589
+
590
+ def main():
591
+ parser = argparse.ArgumentParser(
592
+ description="SQLite FTS5 indexer for per-user ring-fenced RAG (Phase 2). "
593
+ "Indexes the agent repo's content sources into a full-text search database.",
594
+ epilog="Examples:\n"
595
+ " %(prog)s # incremental index\n"
596
+ " %(prog)s --full # full re-index\n"
597
+ " %(prog)s --stats # show statistics\n"
598
+ " %(prog)s --db /tmp/test.db # custom DB path\n",
599
+ formatter_class=argparse.RawDescriptionHelpFormatter,
600
+ )
601
+ parser.add_argument(
602
+ "--full", action="store_true",
603
+ help="Full re-index: drop and rebuild the entire database"
604
+ )
605
+ parser.add_argument(
606
+ "--stats", action="store_true",
607
+ help="Show index statistics and exit"
608
+ )
609
+ parser.add_argument(
610
+ "--db", type=str, default=None,
611
+ help=f"Path to SQLite database (default: {DEFAULT_DB_PATH})"
612
+ )
613
+
614
+ args = parser.parse_args()
615
+
616
+ db_path = Path(args.db) if args.db else DEFAULT_DB_PATH
617
+
618
+ if args.stats:
619
+ show_stats(db_path)
620
+ return
621
+
622
+ start = time.time()
623
+ indexed, docs = run_index(db_path, full=args.full)
624
+ elapsed = time.time() - start
625
+ print(f"Completed in {elapsed:.2f}s")
626
+
627
+
628
+ if __name__ == "__main__":
629
+ main()