@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,685 @@
1
+ #!/usr/bin/env node
2
+ // Prompt Builder — constructs focused prompts for claude --print sessions
3
+ // Each inbox item or backlog queue item gets a tailored prompt with identity,
4
+ // rules, context, and action instructions.
5
+
6
+ import { readFileSync, readdirSync } from "fs";
7
+ import { join } from "path";
8
+ import { compileContext } from "./context-compiler.mjs";
9
+ import { wrapExternalContent } from "../../lib/security/external-content.mjs";
10
+ import { isEnabled as orgEnabled } from "../../lib/org/client.mjs";
11
+ import { recall as orgRecall } from "../../lib/org/knowledge.mjs";
12
+ import { outcomeSourceShareable } from "./session-outcomes.mjs";
13
+
14
+ const AGENT_REPO_DIR = process.env.AGENT_DIR || join(new URL(".", import.meta.url).pathname, "../..");
15
+
16
+ // Load agent identity from canonical SOT for use in prompts and fallback preamble.
17
+ function loadAgent() {
18
+ try {
19
+ return JSON.parse(readFileSync(join(AGENT_REPO_DIR, "config/agent.json"), "utf-8"));
20
+ } catch {
21
+ return { firstName: "Agent", fullName: "Agent", title: "agent", company: "the company", principal: { firstName: "principal", fullName: "the principal", title: "principal" } };
22
+ }
23
+ }
24
+
25
+ // Legacy: Maximum lines of conversation history (used when DAEMON_CONTEXT_COMPILER is off)
26
+ const MAX_HISTORY_LINES = 30;
27
+
28
+ // Org shared-knowledge injection (org enrollment). Bounded so a large recall can
29
+ // never blow up the prompt: at most this many facts, each truncated.
30
+ const ORG_RECALL_MAX_FACTS = 6;
31
+ const ORG_RECALL_FACT_CHARS = 400;
32
+
33
+ // Cached org config (config/org.yaml) — read once per process. null until loaded.
34
+ let cachedOrgConfig;
35
+
36
+ /**
37
+ * Load the org config (config/org.yaml) for the recall binding. Cached for the
38
+ * process. Returns {} on any error so callers can null-check uniformly — the org
39
+ * binding must never take down the daemon's main path.
40
+ */
41
+ async function loadOrgConfig() {
42
+ if (cachedOrgConfig !== undefined) return cachedOrgConfig;
43
+ try {
44
+ const p = join(AGENT_REPO_DIR, "config/org.yaml");
45
+ const raw = readFileSync(p, "utf-8");
46
+ const m = await import("js-yaml");
47
+ const yaml = m && m.default ? m.default : m;
48
+ const doc = yaml.load(raw);
49
+ cachedOrgConfig = doc && typeof doc === "object" ? doc : {};
50
+ } catch {
51
+ cachedOrgConfig = {};
52
+ }
53
+ return cachedOrgConfig;
54
+ }
55
+
56
+ /** For tests: reset the cached org config so a fresh config/org.yaml is re-read. */
57
+ export function _resetOrgConfigCache() {
58
+ cachedOrgConfig = undefined;
59
+ }
60
+
61
+ /**
62
+ * Derive a short recall query from the item/classification being processed.
63
+ * Prefers the classifier summary, then subject, then a slice of the body.
64
+ */
65
+ function recallQueryFor(item, classResult, queueItem) {
66
+ const parts = [];
67
+ if (classResult && classResult.summary) parts.push(String(classResult.summary));
68
+ if (item && item.subject) parts.push(String(item.subject));
69
+ if (queueItem && queueItem.title) parts.push(String(queueItem.title));
70
+ if (parts.length === 0 && item && item.content) parts.push(String(item.content).slice(0, 200));
71
+ return parts.join(" — ").trim();
72
+ }
73
+
74
+ /**
75
+ * Render the org's shared knowledge as a clearly-delimited context block, or
76
+ * null when the integration is disabled / nothing relevant came back. Fail-open
77
+ * and bounded: never throws, caps the number and size of injected facts. The
78
+ * SERVER enforces ACL — we render exactly what recall() returns.
79
+ *
80
+ * @param {object} item inbox item (may be null for backlog)
81
+ * @param {object} classResult classifier output
82
+ * @param {object} queueItem backlog queue item (may be null)
83
+ * @returns {Promise<string|null>}
84
+ */
85
+ async function buildOrgKnowledgeBlock(item, classResult, queueItem) {
86
+ try {
87
+ const cfg = await loadOrgConfig();
88
+ if (!orgEnabled(cfg)) return null;
89
+ const query = recallQueryFor(item, classResult, queueItem);
90
+ if (!query) return null;
91
+
92
+ const facts = await orgRecall(query, { cfg, limit: ORG_RECALL_MAX_FACTS });
93
+ if (!Array.isArray(facts) || facts.length === 0) return null;
94
+
95
+ const lines = ["--- ORG SHARED KNOWLEDGE ---"];
96
+ lines.push("Facts the org already knows that may bear on this item (ACL-filtered by the");
97
+ lines.push("server; treat as trusted internal context). Use them to inform your reply:");
98
+ let n = 0;
99
+ for (const f of facts) {
100
+ if (n >= ORG_RECALL_MAX_FACTS) break;
101
+ const textRaw = typeof f === "string" ? f : (f && (f.text || f.content || f.body) || "");
102
+ const text = String(textRaw).trim();
103
+ if (!text) continue;
104
+ const clipped = text.length > ORG_RECALL_FACT_CHARS ? `${text.slice(0, ORG_RECALL_FACT_CHARS)}…` : text;
105
+ const id = f && typeof f === "object" && f.id != null ? ` [${String(f.id)}]` : "";
106
+ lines.push(`- ${clipped}${id}`);
107
+ n += 1;
108
+ }
109
+ if (n === 0) return null;
110
+ lines.push("--- END ORG SHARED KNOWLEDGE ---");
111
+ return lines.join("\n");
112
+ } catch {
113
+ // Fail-open: org recall must never take down the prompt build.
114
+ return null;
115
+ }
116
+ }
117
+
118
+ // Cached identity preamble — extracted from CLAUDE.md on first call
119
+ let cachedPreamble = null;
120
+
121
+ function loadPreamble() {
122
+ if (cachedPreamble) return cachedPreamble;
123
+
124
+ try {
125
+ const raw = readFileSync(join(AGENT_REPO_DIR, "CLAUDE.md"), "utf-8");
126
+ cachedPreamble = extractPreamble(raw);
127
+ } catch (err) {
128
+ console.error(`[prompt-builder] Failed to read CLAUDE.md: ${err.message}`);
129
+ cachedPreamble = FALLBACK_PREAMBLE;
130
+ }
131
+ return cachedPreamble;
132
+ }
133
+
134
+ /**
135
+ * Extract the Identity, Operating Principles, Communication Rules,
136
+ * and Document Sharing sections from CLAUDE.md — keep it concise.
137
+ */
138
+ function extractPreamble(raw) {
139
+ const sections = [];
140
+ const lines = raw.split("\n");
141
+ const targetHeaders = [
142
+ "## Identity",
143
+ "## Company Context",
144
+ "## Operating Principles",
145
+ "## Communication Rules",
146
+ ];
147
+
148
+ let capturing = false;
149
+ let depth = 0;
150
+
151
+ for (const line of lines) {
152
+ // Start capturing when we hit a target header
153
+ if (targetHeaders.some((h) => line.startsWith(h))) {
154
+ capturing = true;
155
+ depth = 2; // ## level
156
+ sections.push(line);
157
+ continue;
158
+ }
159
+
160
+ // Stop capturing when we hit another ## header that is NOT a sub-section of what we want
161
+ if (capturing && /^## [A-Z]/.test(line) && !targetHeaders.some((h) => line.startsWith(h))) {
162
+ capturing = false;
163
+ sections.push(""); // blank line separator
164
+ continue;
165
+ }
166
+
167
+ // Also stop at ### Document Sharing end — capture it but stop at next ##
168
+ if (capturing) {
169
+ sections.push(line);
170
+ }
171
+ }
172
+
173
+ const extracted = sections.join("\n").trim();
174
+
175
+ // If extraction got something reasonable, use it; otherwise fall back
176
+ if (extracted.length > 200) return extracted;
177
+ return FALLBACK_PREAMBLE;
178
+ }
179
+
180
+ // Generic preamble derived from config/agent.json. This is only used when
181
+ // CLAUDE.md is unreadable; ordinarily the daemon pulls the full identity
182
+ // block straight from the agent's CLAUDE.md.
183
+ function buildFallbackPreamble() {
184
+ const a = loadAgent();
185
+ const principal = a.principal || {};
186
+ const principalName = principal.fullName || "the principal";
187
+ const principalTitle = principal.title || "principal";
188
+ return `You are ${a.fullName}, ${a.title} to ${principalName}, ${principalTitle} of ${a.company}.
189
+ You operate as the autonomous executive command layer for the company.
190
+
191
+ ${a.companyDescription || a.company}.
192
+
193
+ Operating Principles:
194
+ 1. Follow-through over brilliance — track every commitment until it closes
195
+ 2. Concise over comprehensive — sharp recommendations, not exhaustive reports
196
+ 3. Evidence over opinion — cite sources
197
+ 4. Bias to action — act decisively on operational matters; escalate only strategic commitments
198
+ 5. Audit everything — every action is logged
199
+ 6. Full autonomy, full accountability
200
+
201
+ Autonomy Model:
202
+ - ${a.firstName} sends autonomously: all internal messages, external operational messages, candidate comms, follow-ups, calendar coordination
203
+ - ${a.firstName} escalates only: binding legal/financial obligations, regulatory submissions, acquisition deal terms, public statements
204
+
205
+ Document Sharing (CRITICAL):
206
+ - NEVER reference local file paths in outbound communications
207
+ - Upload to Google Drive or generate branded PDF for sharing
208
+ - Inline short content directly in messages
209
+ - Never attach raw .md or .yaml files`;
210
+ }
211
+ const FALLBACK_PREAMBLE = buildFallbackPreamble();
212
+
213
+ /**
214
+ * Proactive self-learning habit (WS3). Injected near the top of every reactive /
215
+ * backlog prompt so the agent treats skill-authoring as a normal reflex. The
216
+ * heavy lifting (the actual write + secret-scan + pruning) is autonomous and
217
+ * happens out-of-band in the Stop-hook reflect worker — this paragraph just
218
+ * sets the disposition.
219
+ */
220
+ const SKILLS_GUIDANCE = [
221
+ "SELF-LEARNING: You can author your own Claude Code skills. When you work out a",
222
+ "repeatable, multi-step method that is likely to recur, prefer capturing it as a",
223
+ "skill (under plugins/agent-skills/) over re-deriving it next time — a future",
224
+ "session loads it automatically. Bias toward capturing the genuinely reusable",
225
+ "procedure; do NOT capture one-off steps, transient failures, or \"tool X doesn't",
226
+ "work\" claims. Skills are pruned automatically when they go stale, so capturing",
227
+ "is cheap and safe. To recall what you did before, the `session-search` skill",
228
+ "searches your own past sessions verbatim.",
229
+ ].join("\n");
230
+
231
+ /**
232
+ * OUTCOME DISCIPLINE (parity with hq's meeting chair). A turn that decides things
233
+ * and commits to next steps must not leave them in a session log — nobody reads a
234
+ * session log, so the commitment is lost and the next turn re-decides it.
235
+ *
236
+ * This asks for ONE machine-readable block at the very end of the turn. The
237
+ * landing itself is DRIVEN, not left to the model: the daemon's post-session hook
238
+ * (scripts/daemon/session-outcomes.mjs) parses this block off the turn's final
239
+ * text and writes it through the org protocol (board.create + knowledge.append).
240
+ * So the model's only job is to be honest about what it committed to.
241
+ *
242
+ * Injected only when DAEMON_SESSION_OUTCOMES=1 (the same flag that arms the hook)
243
+ * AND the turn's source is org-visible (the same fail-closed gate the hook
244
+ * applies) — a landed outcome is readable by the whole org, so a private DM turn
245
+ * is neither asked for the block nor swept for one.
246
+ */
247
+ const OUTCOME_DISCIPLINE = [
248
+ "OUTCOMES BLOCK (required, last thing in your reply):",
249
+ "End your turn with a block in exactly this shape:",
250
+ "",
251
+ "OUTCOMES:",
252
+ "- <the commitment, as a concrete next step> — owner: <name> — due: <when>",
253
+ "",
254
+ "Rules:",
255
+ "- List ONLY things that are genuinely committed to and NOT already done in this",
256
+ " turn — a future action someone now owes. Work you completed here is not an",
257
+ " outcome.",
258
+ "- `owner:` and `due:` are optional; include them only when they were actually",
259
+ " stated. Never guess an owner.",
260
+ "- Most turns commit to nothing. When that is true, write `OUTCOMES: none` — an",
261
+ " invented next step is worse than an empty block.",
262
+ "- Maximum 8 lines, one clause each, no sub-bullets.",
263
+ "- This block is machine-read and landed on the org board automatically; do not",
264
+ " also create board items for the same commitments by hand, and do not mention",
265
+ " the block in the message you send to the user.",
266
+ ].join("\n");
267
+
268
+ const ACTION_INSTRUCTIONS = {
269
+ respond: `ACTION: Respond to this message.
270
+ - Read the message carefully and understand the request
271
+ - Check for any relevant context in thread history
272
+ - Draft the substantive response directly — do NOT re-acknowledge if a holding message was already sent
273
+
274
+ READING / CONTEXT (MCP tools are fine here):
275
+ - Use MCP Slack for reading: \`mcp__claude_ai_Slack__slack_search_messages\`, \`slack_conversations_history\`, \`slack_users_info\`, etc.
276
+ - Use MCP Gmail for reading: \`mcp__claude_ai_Gmail__gmail_search\`, \`gmail_read_email\`, etc.
277
+ - Use MCP Google Calendar for scheduling lookups and event reads
278
+ - These are authorised by the user session and are the preferred way to gather context before composing a reply
279
+
280
+ SENDING (CLI scripts only — MCP writes will be blocked by pre-tool hooks):
281
+ - Slack send: \`./scripts/slack-send.sh "<channel_id>" "<message_text>" --responding_to "<inbound_message_ts>"\`
282
+ - Add \`--thread_ts "<thread_ts>"\` if replying inside an existing thread
283
+ - The channel_id is in the inbox item as \`channel_id\` (starts with C, D, or G)
284
+ - Always pass \`--responding_to\` with the inbound message ts so dedup works
285
+ - Gmail send: \`python3 scripts/send-email-threaded.py "<to>" "<subject>" "<body>" --reply-to-subject "<original_subject>"\`
286
+ - Use \`--attachment <path>\` to include files
287
+ - The \`From\` header and HTML signature are injected automatically
288
+ - Workspace email (service \`orgmail\`) send: \`node scripts/org/send-orgmail.mjs --thread "<thread_id>" --body-file <path>\`
289
+ - NEVER raw curl and never the MCP \`email_send\` tool — this CLI is the sanctioned lane
290
+ - Recipients and subject derive from the thread server-side (reply rule); the thread_id is the inbox item's \`thread_id\`/\`channel_id\`
291
+ - A first-contact recipient returns approval_required — re-run with \`--request-approval --wait\` after a human approves in Cohort
292
+ - SMS send: \`./scripts/send-sms.sh --to "<e164_number>" --body "<text>"\`
293
+ - WhatsApp send: \`./scripts/send-whatsapp.sh --to "whatsapp:<e164_number>" --body "<text>"\`
294
+
295
+ Why split read vs write: MCP tools are ideal for reading because they have full API coverage and the session is already authorised. But writes must go through the CLI scripts so the outbound message carries the agent's own User OAuth Token (xoxp-) and appears as the agent's Slack identity — not tagged as "Sent using @Claude". The pre-tool-use hook \`scripts/hooks/block-mcp-slack-send.sh\` will reject any attempt to call \`slack_send_message\` / \`slack_schedule_message\` / \`slack_send_message_draft\` and remind you to use the CLI. Gmail write hooks behave the same way, and \`scripts/hooks/block-mcp-cohort-send.sh\` blocks the org-MCP outbound writes (\`mcp__cohort__messaging_send\` / \`email_send\` / \`org_rpc\`).
296
+
297
+ - Match the tone and urgency of the sender
298
+ - If the sender is CEO, prioritise speed and directness
299
+ - If you need to share documents, upload to Google Drive or send as PDF — NEVER reference local file paths
300
+ - Verify the send succeeded by checking the script's exit code and stdout before considering the task complete`,
301
+
302
+ draft: `ACTION: Draft a response or document — do NOT send it yet.
303
+ - Read the message and understand what is being requested
304
+ - Draft the response or document
305
+ - Save the draft to outputs/ with a descriptive filename
306
+ - If it is a document (memo, brief, letter), use the appropriate format
307
+ - Log that a draft has been created and is awaiting review`,
308
+
309
+ research: `ACTION: Research this topic and write findings.
310
+ - Investigate the topic thoroughly using available tools and knowledge
311
+ - Check relevant files in knowledge/, docs/, and memory/
312
+ - Write concise findings to outputs/research/ with today's date prefix
313
+ - Include sources and evidence for all claims
314
+ - Highlight actionable recommendations`,
315
+
316
+ queue: `ACTION: Create a queue item for later processing.
317
+ - This item needs tracking but not immediate action
318
+ - Create or update a queue item in the appropriate state/queues/ file
319
+ - Set appropriate priority, owner, SLA deadline, and next_action
320
+ - Log the queuing decision`,
321
+
322
+ archive: `ACTION: Archive — no action needed.
323
+ - Mark this item as processed
324
+ - Log that it was reviewed and archived with a brief reason
325
+ - No response required`,
326
+
327
+ ignore: `ACTION: Ignore — skip entirely.
328
+ - This item requires no processing
329
+ - Log only that it was classified as ignore`,
330
+ };
331
+
332
+ function today() {
333
+ return new Date().toISOString().split("T")[0];
334
+ }
335
+
336
+ /**
337
+ * Load recent conversation history for a sender/channel from interaction logs.
338
+ * Searches memory/interactions/slack/{channel-or-sender}/ for recent JSONL entries.
339
+ * Returns the most recent entries as formatted context text.
340
+ */
341
+ function loadConversationHistory(item) {
342
+ if (!item) return null;
343
+ const date = today();
344
+
345
+ // Try multiple paths where interaction logs may live
346
+ const channelId = extractChannelId(item);
347
+ const senderSlug = item.sender ? item.sender.replace(/\s+/g, "-").toLowerCase() : null;
348
+
349
+ const candidateDirs = [];
350
+ if (channelId) candidateDirs.push(join(AGENT_REPO_DIR, "memory", "interactions", "slack", channelId));
351
+ if (senderSlug) {
352
+ candidateDirs.push(join(AGENT_REPO_DIR, "memory", "interactions", "slack", `dm-${senderSlug}`));
353
+ candidateDirs.push(join(AGENT_REPO_DIR, "memory", "interactions", "slack", senderSlug));
354
+ }
355
+
356
+ const entries = [];
357
+
358
+ for (const dir of candidateDirs) {
359
+ try {
360
+ // Read today's and yesterday's logs
361
+ const files = readdirSync(dir)
362
+ .filter((f) => f.endsWith(".jsonl"))
363
+ .sort()
364
+ .reverse()
365
+ .slice(0, 2); // Today + yesterday at most
366
+
367
+ for (const file of files) {
368
+ try {
369
+ const content = readFileSync(join(dir, file), "utf-8").trim();
370
+ if (!content) continue;
371
+ const lines = content.split("\n").filter(Boolean);
372
+ for (const line of lines) {
373
+ try {
374
+ entries.push(JSON.parse(line));
375
+ } catch { /* skip malformed lines */ }
376
+ }
377
+ } catch { /* skip unreadable files */ }
378
+ }
379
+ } catch { /* dir doesn't exist — try next */ }
380
+ }
381
+
382
+ if (entries.length === 0) return null;
383
+
384
+ // Sort by timestamp and take recent entries
385
+ entries.sort((a, b) => {
386
+ const tsA = a.ts || a.received_at || "";
387
+ const tsB = b.ts || b.received_at || "";
388
+ return tsA.localeCompare(tsB);
389
+ });
390
+
391
+ const recent = entries.slice(-MAX_HISTORY_LINES);
392
+
393
+ // Format into readable context
394
+ const lines = ["## Recent conversation history with this sender"];
395
+ for (const entry of recent) {
396
+ const from = entry.from || entry.user_name || entry.sender || "unknown";
397
+ const text = entry.content || entry.text || "(no text)";
398
+ const ts = entry.received_at || entry.processed_at || entry.ts || "";
399
+ const actions = entry.actions_taken
400
+ ? ` [Actions: ${Array.isArray(entry.actions_taken) ? entry.actions_taken.join(", ") : entry.actions_taken}]`
401
+ : entry.action_taken
402
+ ? ` [Action: ${entry.action_taken}]`
403
+ : "";
404
+ const responded = entry.response_sent ? " [Responded]" : "";
405
+ lines.push(`[${ts}] ${from}: ${text.substring(0, 300)}${actions}${responded}`);
406
+ }
407
+
408
+ return lines.join("\n");
409
+ }
410
+
411
+ /**
412
+ * Extract Slack channel ID from item metadata.
413
+ */
414
+ function extractChannelId(item) {
415
+ if (!item) return null;
416
+ // Direct channel ID field
417
+ if (item.channel_id) return item.channel_id;
418
+ // From raw_ref format: "slack:D099N1JGKRQ:1775331885.690669"
419
+ if (item.raw_ref) {
420
+ const match = item.raw_ref.match(/slack:([DC][A-Z0-9]+):/);
421
+ if (match) return match[1];
422
+ }
423
+ // Channel field if it looks like an ID
424
+ if (item.channel && /^[DC][A-Z0-9]{8,}$/.test(item.channel)) return item.channel;
425
+ return null;
426
+ }
427
+
428
+ /**
429
+ * Build a sender identifier string for the prompt
430
+ */
431
+ function formatSender(item) {
432
+ const parts = [];
433
+ if (item.sender) parts.push(item.sender);
434
+ if (item.sender_privilege && item.sender_privilege !== "unknown") {
435
+ parts.push(`(${item.sender_privilege})`);
436
+ }
437
+ return parts.join(" ") || "unknown sender";
438
+ }
439
+
440
+ /**
441
+ * Build the item context block for inbox items
442
+ */
443
+ export function buildInboxContext(item) {
444
+ const lines = [];
445
+ lines.push("--- INCOMING MESSAGE ---");
446
+ lines.push(`From: ${formatSender(item)}`);
447
+ lines.push(`Service: ${item.service || "unknown"}`);
448
+ lines.push(`Channel: ${item.channel || "unknown"}`);
449
+ if (item.subject) lines.push(`Subject: ${item.subject}`);
450
+ lines.push(`Timestamp: ${item.timestamp || new Date().toISOString()}`);
451
+ if (item.is_reply) lines.push(`Is reply: yes`);
452
+ if (item.thread_id) lines.push(`Thread ID: ${item.thread_id}`);
453
+ if (item.raw_ref) lines.push(`Reference: ${item.raw_ref}`);
454
+ lines.push("");
455
+
456
+ const source = item.service || item.channel || "external";
457
+
458
+ if (item.thread_context) {
459
+ lines.push("Thread context:");
460
+ // Thread history is attacker-influenced; fence it as untrusted DATA (H2).
461
+ lines.push(wrapExternalContent(item.thread_context, { source }));
462
+ lines.push("");
463
+ }
464
+
465
+ lines.push("Message content:");
466
+ // The message body is the primary prompt-injection surface: inbound email/
467
+ // Slack/etc. text flows into a claude --print session. Fence it so an
468
+ // embedded "ignore previous instructions" arrives as data, not commands,
469
+ // and annotate a heuristic suspicion score. See lib/security/external-content.mjs.
470
+ lines.push(
471
+ item.content
472
+ ? wrapExternalContent(item.content, { source, annotateSuspicion: true })
473
+ : "(empty)"
474
+ );
475
+ lines.push("--- END MESSAGE ---");
476
+
477
+ return lines.join("\n");
478
+ }
479
+
480
+ /**
481
+ * Build the item context block for backlog queue items
482
+ */
483
+ function buildBacklogContext(queueItem) {
484
+ const lines = [];
485
+ lines.push("--- QUEUE ITEM ---");
486
+ lines.push(`ID: ${queueItem.id || "unknown"}`);
487
+ lines.push(`Title: ${queueItem.title || "untitled"}`);
488
+ lines.push(`Status: ${queueItem.status || "open"}`);
489
+ lines.push(`Priority: ${queueItem.priority || "normal"}`);
490
+ lines.push(`Owner: ${queueItem.owner || loadAgent().firstName.toLowerCase()}`);
491
+ if (queueItem.source) lines.push(`Source: ${queueItem.source}`);
492
+ if (queueItem.source_ref) lines.push(`Source ref: ${queueItem.source_ref}`);
493
+ if (queueItem.due) lines.push(`Due: ${queueItem.due}`);
494
+ if (queueItem.sla_deadline) lines.push(`SLA deadline: ${queueItem.sla_deadline}`);
495
+ lines.push("");
496
+
497
+ if (queueItem.context) {
498
+ lines.push("Context:");
499
+ lines.push(queueItem.context);
500
+ lines.push("");
501
+ }
502
+
503
+ if (queueItem.next_action) {
504
+ lines.push("Next action required:");
505
+ lines.push(queueItem.next_action);
506
+ lines.push("");
507
+ }
508
+
509
+ if (queueItem.blocked_by) {
510
+ lines.push(`Blocked by: ${queueItem.blocked_by}`);
511
+ lines.push("");
512
+ }
513
+
514
+ if (queueItem.success_criteria) {
515
+ lines.push("Success criteria:");
516
+ if (Array.isArray(queueItem.success_criteria)) {
517
+ for (const c of queueItem.success_criteria) lines.push(`- ${c}`);
518
+ } else {
519
+ lines.push(queueItem.success_criteria);
520
+ }
521
+ lines.push("");
522
+ }
523
+
524
+ // Include recent history for context
525
+ if (queueItem.history && Array.isArray(queueItem.history) && queueItem.history.length > 0) {
526
+ const recent = queueItem.history.slice(-3);
527
+ lines.push("Recent history:");
528
+ for (const h of recent) {
529
+ lines.push(` [${h.timestamp}] ${h.action} (by ${h.by || "unknown"})`);
530
+ }
531
+ lines.push("");
532
+ }
533
+
534
+ lines.push("--- END QUEUE ITEM ---");
535
+ return lines.join("\n");
536
+ }
537
+
538
+ /**
539
+ * Build a focused prompt for a claude --print session.
540
+ *
541
+ * @param {object} item - The inbox item (sender, content, channel, service, subject, thread_context, etc.)
542
+ * @param {object} classResult - Classifier output (priority, action, model, summary, category)
543
+ * @param {object} options - { type: "inbox" | "backlog", queueItem?: object }
544
+ * @returns {string} Prompt string ready for claude --print
545
+ */
546
+ export async function buildPrompt(item, classResult, options = {}) {
547
+ const { type = "inbox", queueItem, holdingMessage } = options;
548
+ const preamble = loadPreamble();
549
+ const action = classResult.action || "respond";
550
+ const actionBlock = ACTION_INSTRUCTIONS[action] || ACTION_INSTRUCTIONS.respond;
551
+ const date = today();
552
+
553
+ const parts = [];
554
+
555
+ // 1. Identity preamble (cached)
556
+ parts.push(preamble);
557
+ parts.push("");
558
+
559
+ // 1b. Self-learning habit (WS3) — sets the disposition to capture reusable
560
+ // procedures as skills. Cheap, always-on; the autonomous write happens later
561
+ // in the Stop-hook reflect worker.
562
+ parts.push(SKILLS_GUIDANCE);
563
+ parts.push("");
564
+
565
+ // 1a. Holding message warning — TOP OF PROMPT so Claude sees it before action instructions.
566
+ // This is the most critical instruction in the prompt: prevents double-replies.
567
+ // We repeat it at section 7a as well, immediately before the action block.
568
+ if (holdingMessage) {
569
+ parts.push("===== STOP — READ THIS FIRST =====");
570
+ parts.push(`A HOLDING MESSAGE has ALREADY been sent to the sender by the daemon. The exact text was:`);
571
+ parts.push(` "${holdingMessage}"`);
572
+ parts.push("");
573
+ parts.push("Your job is to deliver the FULL substantive response or complete the actual work — NOT to acknowledge again.");
574
+ parts.push("- Do NOT start your reply with 'Got it', 'On it', 'Looking into this', 'Will get back to you', 'Thanks for reaching out', or any similar acknowledgment phrase.");
575
+ parts.push("- Do NOT echo what the user asked — they already received the holding note confirming receipt.");
576
+ parts.push("- Open with the actual answer, the actual draft, or the actual finding. Be direct.");
577
+ parts.push("- If after investigation you still cannot deliver a substantive response and need more time, send a SECOND-LEVEL UPDATE (specific blocker, ETA, what you need from the user) — never a generic 'still looking into it'.");
578
+ parts.push("===== END WARNING =====");
579
+ parts.push("");
580
+ }
581
+
582
+ // 2. Session context
583
+ parts.push(`Date: ${date}`);
584
+ parts.push(`Classification: priority=${classResult.priority}, action=${action}, category=${classResult.category || "unclassified"}`);
585
+ parts.push(`Summary: ${classResult.summary || "No summary"}`);
586
+ parts.push("");
587
+
588
+ // 3. Item context — inbox or backlog
589
+ if (type === "backlog" && queueItem) {
590
+ parts.push(buildBacklogContext(queueItem));
591
+ } else {
592
+ parts.push(buildInboxContext(item));
593
+ }
594
+ parts.push("");
595
+
596
+ // 3b. Org shared knowledge (org enrollment). When config/org.yaml enables the
597
+ // Cohort plane, consult the org-wide ACL'd RAG source for this item's topic and
598
+ // inject what the fleet already knows. Fail-open + bounded: a disabled/unreachable
599
+ // server (or empty recall) injects nothing; the SERVER enforces ACL — we just
600
+ // render what recall() returns.
601
+ // When the compiled-context path is active it performs the org recall itself
602
+ // (context-compiler.mjs → loadOrgMemory), so skip here to avoid a duplicate
603
+ // recall + duplicated context in the same prompt. Backlog items pass their
604
+ // queueItem into compileContext (below) so entity coverage is preserved.
605
+ if (process.env.DAEMON_CONTEXT_COMPILER !== "1") {
606
+ const orgBlock = await buildOrgKnowledgeBlock(item, classResult, queueItem);
607
+ if (orgBlock) {
608
+ parts.push(orgBlock);
609
+ parts.push("");
610
+ }
611
+ }
612
+
613
+ // 4. Compiled context (profile + history + disclosure boundaries + active sessions + sent messages)
614
+ if (process.env.DAEMON_CONTEXT_COMPILER === "1" && (type === "inbox" ? item : true)) {
615
+ try {
616
+ const { contextBlock } = await compileContext(
617
+ item || {},
618
+ classResult,
619
+ { type, queueItem }
620
+ );
621
+ parts.push(contextBlock);
622
+ parts.push("");
623
+ } catch (err) {
624
+ console.error(`[prompt-builder] Context compilation failed: ${err.message}`);
625
+ // Fall through to legacy behaviour
626
+ if (type === "inbox" && item) {
627
+ const history = loadConversationHistory(item);
628
+ if (history) { parts.push(history); parts.push(""); }
629
+ }
630
+ }
631
+ } else if (type === "inbox" && item) {
632
+ // Legacy path (feature flag off)
633
+ const history = loadConversationHistory(item);
634
+ if (history) { parts.push(history); parts.push(""); }
635
+ }
636
+
637
+ // 5. Action instructions
638
+ // If a holding message was already sent, prepend a second reminder so the
639
+ // action block is unambiguous about not re-acknowledging.
640
+ if (holdingMessage) {
641
+ parts.push("REMINDER: A holding message was already sent (see top of prompt). The action below describes WHAT to do — but you must NOT begin your reply with another acknowledgment. Open with substance.");
642
+ parts.push("");
643
+ }
644
+ parts.push(actionBlock);
645
+ parts.push("");
646
+
647
+ // 6. User profile lookup — legacy only (when feature flag is off, profile is in compiled context)
648
+ if (process.env.DAEMON_CONTEXT_COMPILER !== "1" && type === "inbox" && item && item.sender) {
649
+ const profileName = item.sender.replace(/\s+/g, "-").toLowerCase();
650
+ parts.push(`Before responding, read memory/profiles/users/${profileName}.yaml for sender preferences, standing instructions, and relationship context. If the file does not exist, proceed without it.`);
651
+ parts.push("");
652
+ }
653
+
654
+ // 7. Backlog-specific: queue update instructions (with history dedup — ib-20260405-001)
655
+ if (type === "backlog" && queueItem) {
656
+ parts.push(`When this task is complete:
657
+ - Update the queue item status to "resolved" in the appropriate state/queues/ file
658
+ - HISTORY DEDUP RULE (CRITICAL): Before appending a history entry, read the item's existing history and check the LAST entry. If the last entry has substantially the same action text (same status, same priority, same blocked_by, same conclusion — e.g. "confirmed no changes", "still open", "re-verified"), do NOT append a new history entry. Instead, ONLY update the last_updated timestamp on the queue item. Only append a new history entry when something actually changed (status changed, new information, blocker resolved, action taken that differs from the last entry).
659
+ - When you DO add a new history entry, include: timestamp, action taken, and by: agent-daemon
660
+ - If the task cannot be completed, set status to "blocked" and record what is blocking it`);
661
+ parts.push("");
662
+ }
663
+
664
+ // 8. (Holding message warning is now at the TOP of the prompt — see section 1a.
665
+ // A second reminder is injected before the action block in section 5.)
666
+
667
+ // 8b. Outcome discipline — ask for the machine-read OUTCOMES block so the
668
+ // post-session hook can land this turn's commitments on the org board. Gated by
669
+ // the same flag that arms the hook AND by the same fail-closed confidentiality
670
+ // gate the hook applies, so the block is only ever asked for on a turn whose
671
+ // commitments may actually become org-visible. Asking a private DM turn for a
672
+ // block that would then be discarded is just noise the user can see.
673
+ if (
674
+ process.env.DAEMON_SESSION_OUTCOMES === "1" &&
675
+ outcomeSourceShareable(item).shareable
676
+ ) {
677
+ parts.push(OUTCOME_DISCIPLINE);
678
+ parts.push("");
679
+ }
680
+
681
+ // 9. Audit instruction
682
+ parts.push(`Log all actions taken to logs/audit/${date}-actions.jsonl as a JSON line with: timestamp, action_type, target, result, session context.`);
683
+
684
+ return parts.join("\n");
685
+ }