@adaptic/maestro 1.0.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 (854) hide show
  1. package/.claude/commands/init-agent.md +99 -0
  2. package/.claude/commands/init-maestro.md +565 -0
  3. package/.claude/settings.json +114 -0
  4. package/.env.example +152 -0
  5. package/README.md +491 -0
  6. package/agents/board-prep/agent.md +80 -0
  7. package/agents/browser-operator/agent.md +52 -0
  8. package/agents/calendar-ops/agent.md +50 -0
  9. package/agents/capital-raising/agent.md +69 -0
  10. package/agents/ceo-briefing/agent.md +72 -0
  11. package/agents/communications/agent.md +69 -0
  12. package/agents/competitive-intelligence/agent.md +49 -0
  13. package/agents/corporate-development/agent.md +66 -0
  14. package/agents/decision-log/agent.md +65 -0
  15. package/agents/desktop-operator/agent.md +59 -0
  16. package/agents/engineering-coordination/agent.md +51 -0
  17. package/agents/founder-voice/agent.md +72 -0
  18. package/agents/fund-ops/agent.md +52 -0
  19. package/agents/gmail-operator/agent.md +62 -0
  20. package/agents/hiring-org-design/agent.md +119 -0
  21. package/agents/inbound-dispatcher/agent.md +66 -0
  22. package/agents/legal-structuring/agent.md +65 -0
  23. package/agents/market-research/agent.md +48 -0
  24. package/agents/partnerships/agent.md +59 -0
  25. package/agents/platform-architecture/agent.md +57 -0
  26. package/agents/pmo-execution/agent.md +60 -0
  27. package/agents/product-strategy/agent.md +50 -0
  28. package/agents/regulatory-dfsa/agent.md +96 -0
  29. package/agents/risk-register/agent.md +62 -0
  30. package/agents/rollup-target-sourcing/agent.md +59 -0
  31. package/agents/session-spawner/agent.md +64 -0
  32. package/agents/slack-operator/agent.md +60 -0
  33. package/agents/sophie-chief-of-staff/agent.md +134 -0
  34. package/agents/strategic-planning/agent.md +54 -0
  35. package/agents/whatsapp-operator/agent.md +60 -0
  36. package/agents/workflow-automation/agent.md +61 -0
  37. package/bin/maestro.mjs +388 -0
  38. package/desktop-control/README.md +56 -0
  39. package/desktop-control/app-profiles/gmail.yaml +120 -0
  40. package/desktop-control/app-profiles/slack.yaml +315 -0
  41. package/desktop-control/app-profiles/whatsapp.yaml +107 -0
  42. package/docs/architecture/agent-topology.md +2222 -0
  43. package/docs/architecture/continuous-monitoring.md +221 -0
  44. package/docs/architecture/mcp-capability-map.md +560 -0
  45. package/docs/architecture/system-architecture.md +1273 -0
  46. package/docs/business-synthesis/ADAPTIC-GROUP-FINAL-OWNERSHIP-STRUCTURE.pdf +13667 -10
  47. package/docs/business-synthesis/adaptic-overview.md +296 -0
  48. package/docs/business-synthesis/executive-operating-model.md +261 -0
  49. package/docs/governance/action-approval-model.md +331 -0
  50. package/docs/governance/communications-policy.md +410 -0
  51. package/docs/governance/desktop-control-safety.md +499 -0
  52. package/docs/guides/agent-persona-setup.md +600 -0
  53. package/docs/operating-charter.md +87 -0
  54. package/docs/prompts/board-pack-cover-template.md +37 -0
  55. package/docs/prompts/decision-recommendation-template.md +88 -0
  56. package/docs/prompts/followup-message-template.md +141 -0
  57. package/docs/prompts/investor-letter-template.md +52 -0
  58. package/docs/prompts/morning-brief-template.md +82 -0
  59. package/docs/prompts/presentation-template.md +58 -0
  60. package/docs/prompts/weekly-strategic-memo-template.md +104 -0
  61. package/docs/runbooks/mac-mini-bootstrap.md +404 -0
  62. package/docs/runbooks/perpetual-operations.md +505 -0
  63. package/docs/runbooks/recovery-and-failover.md +588 -0
  64. package/docs/superpowers/plans/2026-04-02-phase0-operational-foundation.md +2550 -0
  65. package/docs/superpowers/plans/2026-04-03-phase1-executive-core.md +1085 -0
  66. package/docs/superpowers/plans/2026-04-03-phase2-people-product-commercial.md +739 -0
  67. package/docs/superpowers/plans/2026-04-05-information-barrier-implementation.md +926 -0
  68. package/docs/superpowers/plans/2026-04-06-session-context-dedup.md +1994 -0
  69. package/docs/superpowers/specs/2026-04-02-phase0-operational-foundation-design.md +842 -0
  70. package/docs/superpowers/specs/2026-04-03-phase1-executive-core-design.md +516 -0
  71. package/docs/superpowers/specs/2026-04-03-phase2-people-product-commercial-design.md +452 -0
  72. package/docs/superpowers/specs/2026-04-03-phase3-4-final-towers-design.md +129 -0
  73. package/docs/superpowers/specs/2026-04-05-information-barrier-design.md +678 -0
  74. package/docs/superpowers/specs/2026-04-05-reactive-daemon-design.md +237 -0
  75. package/docs/superpowers/specs/2026-04-06-session-context-dedup-design.md +369 -0
  76. package/docs/workflows/executive-cadence.md +218 -0
  77. package/ingest/README.md +87 -0
  78. package/mcp/README.md +51 -0
  79. package/package.json +48 -0
  80. package/plugins/maestro-skills/plugin.json +55 -0
  81. package/plugins/maestro-skills/skills/board-deck.md +68 -0
  82. package/plugins/maestro-skills/skills/decision-brief.md +89 -0
  83. package/plugins/maestro-skills/skills/draft-comms.md +84 -0
  84. package/plugins/maestro-skills/skills/evening-wrap.md +53 -0
  85. package/plugins/maestro-skills/skills/hiring-triage.md +74 -0
  86. package/plugins/maestro-skills/skills/inbox-triage.md +61 -0
  87. package/plugins/maestro-skills/skills/morning-brief.md +54 -0
  88. package/plugins/maestro-skills/skills/pipeline-review.md +76 -0
  89. package/plugins/maestro-skills/skills/regulatory-status.md +81 -0
  90. package/plugins/maestro-skills/skills/schedule-meeting.md +91 -0
  91. package/plugins/maestro-skills/skills/slack-followup.md +64 -0
  92. package/plugins/maestro-skills/skills/weekly-memo.md +70 -0
  93. package/policies/action-classification.yaml +110 -0
  94. package/policies/information-barriers.yaml +119 -0
  95. package/policies/prompt-injection-defence.yaml +138 -0
  96. package/public/assets/adaptic-icon-dark.png +0 -0
  97. package/public/assets/adaptic-icon-dark.svg +4 -0
  98. package/public/assets/adaptic-icon-light.svg +4 -0
  99. package/public/assets/adaptic-logo-dark.svg +17 -0
  100. package/public/assets/adaptic-logo-light.svg +17 -0
  101. package/scaffold/CLAUDE.md +21 -0
  102. package/scaffold/config/agent.ts +69 -0
  103. package/scaffold/config/agent.ts.example +218 -0
  104. package/schedules/README.md +49 -0
  105. package/schedules/triggers/backlog-executor.md +102 -0
  106. package/schedules/triggers/daily-evening-wrap.md +142 -0
  107. package/schedules/triggers/daily-midday-sweep.md +58 -0
  108. package/schedules/triggers/daily-morning-brief.md +55 -0
  109. package/schedules/triggers/inbox-processor.md +115 -0
  110. package/schedules/triggers/meeting-action-capture.md +60 -0
  111. package/schedules/triggers/meeting-prep.md +69 -0
  112. package/schedules/triggers/quarterly-self-assessment.md +54 -0
  113. package/schedules/triggers/weekly-engineering-health.md +37 -0
  114. package/schedules/triggers/weekly-execution.md +67 -0
  115. package/schedules/triggers/weekly-hiring.md +53 -0
  116. package/schedules/triggers/weekly-priorities.md +38 -0
  117. package/schedules/triggers/weekly-strategic-memo.md +124 -0
  118. package/scripts/__pycache__/disclosure_assessment.cpython-313.pyc +0 -0
  119. package/scripts/__pycache__/disclosure_boundaries.cpython-313.pyc +0 -0
  120. package/scripts/__pycache__/email_quote_thread.cpython-313.pyc +0 -0
  121. package/scripts/__pycache__/email_thread_dedup.cpython-313.pyc +0 -0
  122. package/scripts/__pycache__/mehran-inbox-poller.cpython-313.pyc +0 -0
  123. package/scripts/__pycache__/outbound_dedup.cpython-313.pyc +0 -0
  124. package/scripts/__pycache__/pre-draft-context.cpython-313.pyc +0 -0
  125. package/scripts/__pycache__/pre_draft_lookup.cpython-313.pyc +0 -0
  126. package/scripts/__pycache__/send-email-as-mehran.cpython-313.pyc +0 -0
  127. package/scripts/__pycache__/send-email-threaded.cpython-313.pyc +0 -0
  128. package/scripts/__pycache__/send-email-with-attachment.cpython-313.pyc +0 -0
  129. package/scripts/__pycache__/validate_outbound.cpython-313.pyc +0 -0
  130. package/scripts/airtable-crm-populate.md +99 -0
  131. package/scripts/archive-email.sh +41 -0
  132. package/scripts/comms-monitor.sh +285 -0
  133. package/scripts/configure-whatsapp-sandbox.sh +201 -0
  134. package/scripts/continuous-monitor.sh +86 -0
  135. package/scripts/daemon/classifier.mjs +355 -0
  136. package/scripts/daemon/context-compiler.mjs +490 -0
  137. package/scripts/daemon/dispatcher.mjs +385 -0
  138. package/scripts/daemon/health.mjs +72 -0
  139. package/scripts/daemon/prompt-builder.mjs +426 -0
  140. package/scripts/daemon/responder.mjs +547 -0
  141. package/scripts/daemon/session-lock.mjs +520 -0
  142. package/scripts/daemon/sophie-daemon.mjs +521 -0
  143. package/scripts/daemon/test-context-compiler.mjs +238 -0
  144. package/scripts/daemon/test-integration.mjs +130 -0
  145. package/scripts/daemon/test-session-lock.mjs +215 -0
  146. package/scripts/disclosure_assessment.py +873 -0
  147. package/scripts/disclosure_boundaries.py +562 -0
  148. package/scripts/email-signature-mehran.html +52 -0
  149. package/scripts/email-signature.html +44 -0
  150. package/scripts/email_quote_thread.py +167 -0
  151. package/scripts/email_thread_dedup.py +361 -0
  152. package/scripts/emergency-stop.sh +41 -0
  153. package/scripts/healthcheck.sh +110 -0
  154. package/scripts/hooks/block-mcp-slack-send.sh +7 -0
  155. package/scripts/hooks/post-action-log.sh +17 -0
  156. package/scripts/hooks/pre-send-audit.sh +57 -0
  157. package/scripts/hooks/session-end-log.sh +27 -0
  158. package/scripts/huddle/audio-bridge.mjs +664 -0
  159. package/scripts/huddle/boot-slack-cdp.sh +102 -0
  160. package/scripts/huddle/huddle-controller.mjs +942 -0
  161. package/scripts/huddle/huddle-server.mjs +1059 -0
  162. package/scripts/huddle/launch-slack.sh +232 -0
  163. package/scripts/huddle/node_modules/.package-lock.json +50 -0
  164. package/scripts/huddle/node_modules/@anthropic-ai/sdk/CHANGELOG.md +1677 -0
  165. package/scripts/huddle/node_modules/@anthropic-ai/sdk/LICENSE +8 -0
  166. package/scripts/huddle/node_modules/@anthropic-ai/sdk/README.md +674 -0
  167. package/scripts/huddle/node_modules/@anthropic-ai/sdk/_vendor/partial-json-parser/parser.d.mts +3 -0
  168. package/scripts/huddle/node_modules/@anthropic-ai/sdk/_vendor/partial-json-parser/parser.d.mts.map +1 -0
  169. package/scripts/huddle/node_modules/@anthropic-ai/sdk/_vendor/partial-json-parser/parser.d.ts +3 -0
  170. package/scripts/huddle/node_modules/@anthropic-ai/sdk/_vendor/partial-json-parser/parser.d.ts.map +1 -0
  171. package/scripts/huddle/node_modules/@anthropic-ai/sdk/_vendor/partial-json-parser/parser.js +226 -0
  172. package/scripts/huddle/node_modules/@anthropic-ai/sdk/_vendor/partial-json-parser/parser.js.map +1 -0
  173. package/scripts/huddle/node_modules/@anthropic-ai/sdk/_vendor/partial-json-parser/parser.mjs +223 -0
  174. package/scripts/huddle/node_modules/@anthropic-ai/sdk/_vendor/partial-json-parser/parser.mjs.map +1 -0
  175. package/scripts/huddle/node_modules/@anthropic-ai/sdk/api-promise.d.mts +2 -0
  176. package/scripts/huddle/node_modules/@anthropic-ai/sdk/api-promise.d.mts.map +1 -0
  177. package/scripts/huddle/node_modules/@anthropic-ai/sdk/api-promise.d.ts +2 -0
  178. package/scripts/huddle/node_modules/@anthropic-ai/sdk/api-promise.d.ts.map +1 -0
  179. package/scripts/huddle/node_modules/@anthropic-ai/sdk/api-promise.js +6 -0
  180. package/scripts/huddle/node_modules/@anthropic-ai/sdk/api-promise.js.map +1 -0
  181. package/scripts/huddle/node_modules/@anthropic-ai/sdk/api-promise.mjs +2 -0
  182. package/scripts/huddle/node_modules/@anthropic-ai/sdk/api-promise.mjs.map +1 -0
  183. package/scripts/huddle/node_modules/@anthropic-ai/sdk/bin/cli +53 -0
  184. package/scripts/huddle/node_modules/@anthropic-ai/sdk/bin/migration-config.json +7 -0
  185. package/scripts/huddle/node_modules/@anthropic-ai/sdk/client.d.mts +225 -0
  186. package/scripts/huddle/node_modules/@anthropic-ai/sdk/client.d.mts.map +1 -0
  187. package/scripts/huddle/node_modules/@anthropic-ai/sdk/client.d.ts +225 -0
  188. package/scripts/huddle/node_modules/@anthropic-ai/sdk/client.d.ts.map +1 -0
  189. package/scripts/huddle/node_modules/@anthropic-ai/sdk/client.js +536 -0
  190. package/scripts/huddle/node_modules/@anthropic-ai/sdk/client.js.map +1 -0
  191. package/scripts/huddle/node_modules/@anthropic-ai/sdk/client.mjs +531 -0
  192. package/scripts/huddle/node_modules/@anthropic-ai/sdk/client.mjs.map +1 -0
  193. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/api-promise.d.mts +49 -0
  194. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/api-promise.d.mts.map +1 -0
  195. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/api-promise.d.ts +49 -0
  196. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/api-promise.d.ts.map +1 -0
  197. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/api-promise.js +76 -0
  198. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/api-promise.js.map +1 -0
  199. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/api-promise.mjs +72 -0
  200. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/api-promise.mjs.map +1 -0
  201. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/error.d.mts +47 -0
  202. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/error.d.mts.map +1 -0
  203. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/error.d.ts +47 -0
  204. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/error.d.ts.map +1 -0
  205. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/error.js +114 -0
  206. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/error.js.map +1 -0
  207. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/error.mjs +98 -0
  208. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/error.mjs.map +1 -0
  209. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/pagination.d.mts +63 -0
  210. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/pagination.d.mts.map +1 -0
  211. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/pagination.d.ts +63 -0
  212. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/pagination.d.ts.map +1 -0
  213. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/pagination.js +123 -0
  214. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/pagination.js.map +1 -0
  215. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/pagination.mjs +117 -0
  216. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/pagination.mjs.map +1 -0
  217. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/resource.d.mts +6 -0
  218. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/resource.d.mts.map +1 -0
  219. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/resource.d.ts +6 -0
  220. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/resource.d.ts.map +1 -0
  221. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/resource.js +11 -0
  222. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/resource.js.map +1 -0
  223. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/resource.mjs +7 -0
  224. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/resource.mjs.map +1 -0
  225. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/streaming.d.mts +31 -0
  226. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/streaming.d.mts.map +1 -0
  227. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/streaming.d.ts +31 -0
  228. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/streaming.d.ts.map +1 -0
  229. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/streaming.js +282 -0
  230. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/streaming.js.map +1 -0
  231. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/streaming.mjs +277 -0
  232. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/streaming.mjs.map +1 -0
  233. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/uploads.d.mts +3 -0
  234. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/uploads.d.mts.map +1 -0
  235. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/uploads.d.ts +3 -0
  236. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/uploads.d.ts.map +1 -0
  237. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/uploads.js +6 -0
  238. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/uploads.js.map +1 -0
  239. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/uploads.mjs +2 -0
  240. package/scripts/huddle/node_modules/@anthropic-ai/sdk/core/uploads.mjs.map +1 -0
  241. package/scripts/huddle/node_modules/@anthropic-ai/sdk/error.d.mts +2 -0
  242. package/scripts/huddle/node_modules/@anthropic-ai/sdk/error.d.mts.map +1 -0
  243. package/scripts/huddle/node_modules/@anthropic-ai/sdk/error.d.ts +2 -0
  244. package/scripts/huddle/node_modules/@anthropic-ai/sdk/error.d.ts.map +1 -0
  245. package/scripts/huddle/node_modules/@anthropic-ai/sdk/error.js +6 -0
  246. package/scripts/huddle/node_modules/@anthropic-ai/sdk/error.js.map +1 -0
  247. package/scripts/huddle/node_modules/@anthropic-ai/sdk/error.mjs +2 -0
  248. package/scripts/huddle/node_modules/@anthropic-ai/sdk/error.mjs.map +1 -0
  249. package/scripts/huddle/node_modules/@anthropic-ai/sdk/index.d.mts +7 -0
  250. package/scripts/huddle/node_modules/@anthropic-ai/sdk/index.d.mts.map +1 -0
  251. package/scripts/huddle/node_modules/@anthropic-ai/sdk/index.d.ts +7 -0
  252. package/scripts/huddle/node_modules/@anthropic-ai/sdk/index.d.ts.map +1 -0
  253. package/scripts/huddle/node_modules/@anthropic-ai/sdk/index.js +35 -0
  254. package/scripts/huddle/node_modules/@anthropic-ai/sdk/index.js.map +1 -0
  255. package/scripts/huddle/node_modules/@anthropic-ai/sdk/index.mjs +8 -0
  256. package/scripts/huddle/node_modules/@anthropic-ai/sdk/index.mjs.map +1 -0
  257. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/builtin-types.d.mts +73 -0
  258. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/builtin-types.d.mts.map +1 -0
  259. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/builtin-types.d.ts +73 -0
  260. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/builtin-types.d.ts.map +1 -0
  261. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/builtin-types.js +4 -0
  262. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/builtin-types.js.map +1 -0
  263. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/builtin-types.mjs +3 -0
  264. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/builtin-types.mjs.map +1 -0
  265. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/constants.d.mts +5 -0
  266. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/constants.d.mts.map +1 -0
  267. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/constants.d.ts +5 -0
  268. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/constants.d.ts.map +1 -0
  269. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/constants.js +15 -0
  270. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/constants.js.map +1 -0
  271. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/constants.mjs +12 -0
  272. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/constants.mjs.map +1 -0
  273. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/jsonl.d.mts +10 -0
  274. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/jsonl.d.mts.map +1 -0
  275. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/jsonl.d.ts +10 -0
  276. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/jsonl.d.ts.map +1 -0
  277. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/jsonl.js +39 -0
  278. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/jsonl.js.map +1 -0
  279. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/jsonl.mjs +35 -0
  280. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/jsonl.mjs.map +1 -0
  281. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/line.d.mts +17 -0
  282. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/line.d.mts.map +1 -0
  283. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/line.d.ts +17 -0
  284. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/line.d.ts.map +1 -0
  285. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/line.js +113 -0
  286. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/line.js.map +1 -0
  287. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/line.mjs +108 -0
  288. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/decoders/line.mjs.map +1 -0
  289. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/detect-platform.d.mts +15 -0
  290. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/detect-platform.d.mts.map +1 -0
  291. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/detect-platform.d.ts +15 -0
  292. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/detect-platform.d.ts.map +1 -0
  293. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/detect-platform.js +162 -0
  294. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/detect-platform.js.map +1 -0
  295. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/detect-platform.mjs +157 -0
  296. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/detect-platform.mjs.map +1 -0
  297. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/errors.d.mts +3 -0
  298. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/errors.d.mts.map +1 -0
  299. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/errors.d.ts +3 -0
  300. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/errors.d.ts.map +1 -0
  301. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/errors.js +41 -0
  302. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/errors.js.map +1 -0
  303. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/errors.mjs +36 -0
  304. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/errors.mjs.map +1 -0
  305. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/headers.d.mts +22 -0
  306. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/headers.d.mts.map +1 -0
  307. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/headers.d.ts +22 -0
  308. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/headers.d.ts.map +1 -0
  309. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/headers.js +79 -0
  310. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/headers.js.map +1 -0
  311. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/headers.mjs +74 -0
  312. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/headers.mjs.map +1 -0
  313. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/parse.d.mts +17 -0
  314. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/parse.d.mts.map +1 -0
  315. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/parse.d.ts +17 -0
  316. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/parse.d.ts.map +1 -0
  317. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/parse.js +55 -0
  318. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/parse.js.map +1 -0
  319. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/parse.mjs +51 -0
  320. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/parse.mjs.map +1 -0
  321. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/request-options.d.mts +34 -0
  322. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/request-options.d.mts.map +1 -0
  323. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/request-options.d.ts +34 -0
  324. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/request-options.d.ts.map +1 -0
  325. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/request-options.js +14 -0
  326. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/request-options.js.map +1 -0
  327. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/request-options.mjs +10 -0
  328. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/request-options.mjs.map +1 -0
  329. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/shim-types.d.mts +28 -0
  330. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/shim-types.d.ts +28 -0
  331. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/shims.d.mts +20 -0
  332. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/shims.d.mts.map +1 -0
  333. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/shims.d.ts +20 -0
  334. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/shims.d.ts.map +1 -0
  335. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/shims.js +92 -0
  336. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/shims.js.map +1 -0
  337. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/shims.mjs +85 -0
  338. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/shims.mjs.map +1 -0
  339. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/stream-utils.d.mts +8 -0
  340. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/stream-utils.d.mts.map +1 -0
  341. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/stream-utils.d.ts +8 -0
  342. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/stream-utils.d.ts.map +1 -0
  343. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/stream-utils.js +38 -0
  344. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/stream-utils.js.map +1 -0
  345. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/stream-utils.mjs +35 -0
  346. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/stream-utils.mjs.map +1 -0
  347. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/to-file.d.mts +45 -0
  348. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/to-file.d.mts.map +1 -0
  349. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/to-file.d.ts +45 -0
  350. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/to-file.d.ts.map +1 -0
  351. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/to-file.js +96 -0
  352. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/to-file.js.map +1 -0
  353. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/to-file.mjs +93 -0
  354. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/to-file.mjs.map +1 -0
  355. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/tslib.js +81 -0
  356. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/tslib.mjs +17 -0
  357. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/types.d.mts +67 -0
  358. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/types.d.mts.map +1 -0
  359. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/types.d.ts +67 -0
  360. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/types.d.ts.map +1 -0
  361. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/types.js +4 -0
  362. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/types.js.map +1 -0
  363. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/types.mjs +3 -0
  364. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/types.mjs.map +1 -0
  365. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/uploads.d.mts +42 -0
  366. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/uploads.d.mts.map +1 -0
  367. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/uploads.d.ts +42 -0
  368. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/uploads.d.ts.map +1 -0
  369. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/uploads.js +146 -0
  370. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/uploads.js.map +1 -0
  371. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/uploads.mjs +136 -0
  372. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/uploads.mjs.map +1 -0
  373. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/base64.d.mts +3 -0
  374. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/base64.d.mts.map +1 -0
  375. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/base64.d.ts +3 -0
  376. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/base64.d.ts.map +1 -0
  377. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/base64.js +38 -0
  378. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/base64.js.map +1 -0
  379. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/base64.mjs +33 -0
  380. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/base64.mjs.map +1 -0
  381. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/bytes.d.mts +4 -0
  382. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/bytes.d.mts.map +1 -0
  383. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/bytes.d.ts +4 -0
  384. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/bytes.d.ts.map +1 -0
  385. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/bytes.js +31 -0
  386. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/bytes.js.map +1 -0
  387. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/bytes.mjs +26 -0
  388. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/bytes.mjs.map +1 -0
  389. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/env.d.mts +9 -0
  390. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/env.d.mts.map +1 -0
  391. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/env.d.ts +9 -0
  392. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/env.d.ts.map +1 -0
  393. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/env.js +22 -0
  394. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/env.js.map +1 -0
  395. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/env.mjs +18 -0
  396. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/env.mjs.map +1 -0
  397. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/log.d.mts +37 -0
  398. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/log.d.mts.map +1 -0
  399. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/log.d.ts +37 -0
  400. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/log.d.ts.map +1 -0
  401. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/log.js +86 -0
  402. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/log.js.map +1 -0
  403. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/log.mjs +80 -0
  404. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/log.mjs.map +1 -0
  405. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/path.d.mts +15 -0
  406. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/path.d.mts.map +1 -0
  407. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/path.d.ts +15 -0
  408. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/path.d.ts.map +1 -0
  409. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/path.js +58 -0
  410. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/path.js.map +1 -0
  411. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/path.mjs +53 -0
  412. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/path.mjs.map +1 -0
  413. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/sleep.d.mts +2 -0
  414. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/sleep.d.mts.map +1 -0
  415. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/sleep.d.ts +2 -0
  416. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/sleep.d.ts.map +1 -0
  417. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/sleep.js +7 -0
  418. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/sleep.js.map +1 -0
  419. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/sleep.mjs +3 -0
  420. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/sleep.mjs.map +1 -0
  421. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/uuid.d.mts +5 -0
  422. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/uuid.d.mts.map +1 -0
  423. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/uuid.d.ts +5 -0
  424. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/uuid.d.ts.map +1 -0
  425. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/uuid.js +19 -0
  426. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/uuid.js.map +1 -0
  427. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/uuid.mjs +15 -0
  428. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/uuid.mjs.map +1 -0
  429. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/values.d.mts +16 -0
  430. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/values.d.mts.map +1 -0
  431. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/values.d.ts +16 -0
  432. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/values.d.ts.map +1 -0
  433. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/values.js +109 -0
  434. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/values.js.map +1 -0
  435. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/values.mjs +92 -0
  436. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils/values.mjs.map +1 -0
  437. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils.d.mts +7 -0
  438. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils.d.mts.map +1 -0
  439. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils.d.ts +7 -0
  440. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils.d.ts.map +1 -0
  441. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils.js +11 -0
  442. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils.js.map +1 -0
  443. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils.mjs +8 -0
  444. package/scripts/huddle/node_modules/@anthropic-ai/sdk/internal/utils.mjs.map +1 -0
  445. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/BetaMessageStream.d.mts +114 -0
  446. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/BetaMessageStream.d.mts.map +1 -0
  447. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/BetaMessageStream.d.ts +114 -0
  448. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/BetaMessageStream.d.ts.map +1 -0
  449. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/BetaMessageStream.js +553 -0
  450. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/BetaMessageStream.js.map +1 -0
  451. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/BetaMessageStream.mjs +549 -0
  452. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/BetaMessageStream.mjs.map +1 -0
  453. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/MessageStream.d.mts +114 -0
  454. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/MessageStream.d.mts.map +1 -0
  455. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/MessageStream.d.ts +114 -0
  456. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/MessageStream.d.ts.map +1 -0
  457. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/MessageStream.js +553 -0
  458. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/MessageStream.js.map +1 -0
  459. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/MessageStream.mjs +549 -0
  460. package/scripts/huddle/node_modules/@anthropic-ai/sdk/lib/MessageStream.mjs.map +1 -0
  461. package/scripts/huddle/node_modules/@anthropic-ai/sdk/package.json +185 -0
  462. package/scripts/huddle/node_modules/@anthropic-ai/sdk/pagination.d.mts +2 -0
  463. package/scripts/huddle/node_modules/@anthropic-ai/sdk/pagination.d.mts.map +1 -0
  464. package/scripts/huddle/node_modules/@anthropic-ai/sdk/pagination.d.ts +2 -0
  465. package/scripts/huddle/node_modules/@anthropic-ai/sdk/pagination.d.ts.map +1 -0
  466. package/scripts/huddle/node_modules/@anthropic-ai/sdk/pagination.js +6 -0
  467. package/scripts/huddle/node_modules/@anthropic-ai/sdk/pagination.js.map +1 -0
  468. package/scripts/huddle/node_modules/@anthropic-ai/sdk/pagination.mjs +2 -0
  469. package/scripts/huddle/node_modules/@anthropic-ai/sdk/pagination.mjs.map +1 -0
  470. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resource.d.mts +2 -0
  471. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resource.d.mts.map +1 -0
  472. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resource.d.ts +2 -0
  473. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resource.d.ts.map +1 -0
  474. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resource.js +6 -0
  475. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resource.js.map +1 -0
  476. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resource.mjs +2 -0
  477. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resource.mjs.map +1 -0
  478. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/beta.d.mts +61 -0
  479. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/beta.d.mts.map +1 -0
  480. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/beta.d.ts +61 -0
  481. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/beta.d.ts.map +1 -0
  482. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/beta.js +25 -0
  483. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/beta.js.map +1 -0
  484. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/beta.mjs +20 -0
  485. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/beta.mjs.map +1 -0
  486. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/files.d.mts +151 -0
  487. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/files.d.mts.map +1 -0
  488. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/files.d.ts +151 -0
  489. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/files.d.ts.map +1 -0
  490. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/files.js +122 -0
  491. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/files.js.map +1 -0
  492. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/files.mjs +118 -0
  493. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/files.mjs.map +1 -0
  494. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/index.d.mts +5 -0
  495. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/index.d.mts.map +1 -0
  496. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/index.d.ts +5 -0
  497. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/index.d.ts.map +1 -0
  498. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/index.js +13 -0
  499. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/index.js.map +1 -0
  500. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/index.mjs +6 -0
  501. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/index.mjs.map +1 -0
  502. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/batches.d.mts +343 -0
  503. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/batches.d.mts.map +1 -0
  504. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/batches.d.ts +343 -0
  505. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/batches.d.ts.map +1 -0
  506. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/batches.js +204 -0
  507. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/batches.js.map +1 -0
  508. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/batches.mjs +200 -0
  509. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/batches.mjs.map +1 -0
  510. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/index.d.mts +3 -0
  511. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/index.d.mts.map +1 -0
  512. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/index.d.ts +3 -0
  513. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/index.d.ts.map +1 -0
  514. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/index.js +9 -0
  515. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/index.js.map +1 -0
  516. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/index.mjs +4 -0
  517. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/index.mjs.map +1 -0
  518. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/messages.d.mts +1561 -0
  519. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/messages.d.mts.map +1 -0
  520. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/messages.d.ts +1561 -0
  521. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/messages.d.ts.map +1 -0
  522. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/messages.js +86 -0
  523. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/messages.js.map +1 -0
  524. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/messages.mjs +81 -0
  525. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages/messages.mjs.map +1 -0
  526. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages.d.mts +2 -0
  527. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages.d.mts.map +1 -0
  528. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages.d.ts +2 -0
  529. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages.d.ts.map +1 -0
  530. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages.js +6 -0
  531. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages.js.map +1 -0
  532. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages.mjs +3 -0
  533. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/messages.mjs.map +1 -0
  534. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/models.d.mts +74 -0
  535. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/models.d.mts.map +1 -0
  536. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/models.d.ts +74 -0
  537. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/models.d.ts.map +1 -0
  538. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/models.js +60 -0
  539. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/models.js.map +1 -0
  540. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/models.mjs +56 -0
  541. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta/models.mjs.map +1 -0
  542. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta.d.mts +2 -0
  543. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta.d.mts.map +1 -0
  544. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta.d.ts +2 -0
  545. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta.d.ts.map +1 -0
  546. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta.js +6 -0
  547. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta.js.map +1 -0
  548. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta.mjs +3 -0
  549. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/beta.mjs.map +1 -0
  550. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/completions.d.mts +183 -0
  551. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/completions.d.mts.map +1 -0
  552. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/completions.d.ts +183 -0
  553. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/completions.d.ts.map +1 -0
  554. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/completions.js +23 -0
  555. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/completions.js.map +1 -0
  556. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/completions.mjs +19 -0
  557. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/completions.mjs.map +1 -0
  558. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/index.d.mts +6 -0
  559. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/index.d.mts.map +1 -0
  560. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/index.d.ts +6 -0
  561. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/index.d.ts.map +1 -0
  562. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/index.js +15 -0
  563. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/index.js.map +1 -0
  564. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/index.mjs +7 -0
  565. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/index.mjs.map +1 -0
  566. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/batches.d.mts +304 -0
  567. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/batches.d.mts.map +1 -0
  568. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/batches.d.ts +304 -0
  569. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/batches.d.ts.map +1 -0
  570. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/batches.js +153 -0
  571. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/batches.js.map +1 -0
  572. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/batches.mjs +149 -0
  573. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/batches.mjs.map +1 -0
  574. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/index.d.mts +3 -0
  575. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/index.d.mts.map +1 -0
  576. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/index.d.ts +3 -0
  577. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/index.d.ts.map +1 -0
  578. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/index.js +9 -0
  579. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/index.js.map +1 -0
  580. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/index.mjs +4 -0
  581. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/index.mjs.map +1 -0
  582. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/messages.d.mts +1264 -0
  583. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/messages.d.mts.map +1 -0
  584. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/messages.d.ts +1264 -0
  585. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/messages.d.ts.map +1 -0
  586. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/messages.js +72 -0
  587. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/messages.js.map +1 -0
  588. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/messages.mjs +67 -0
  589. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages/messages.mjs.map +1 -0
  590. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages.d.mts +2 -0
  591. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages.d.mts.map +1 -0
  592. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages.d.ts +2 -0
  593. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages.d.ts.map +1 -0
  594. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages.js +6 -0
  595. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages.js.map +1 -0
  596. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages.mjs +3 -0
  597. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/messages.mjs.map +1 -0
  598. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/models.d.mts +59 -0
  599. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/models.d.mts.map +1 -0
  600. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/models.d.ts +59 -0
  601. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/models.d.ts.map +1 -0
  602. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/models.js +45 -0
  603. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/models.js.map +1 -0
  604. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/models.mjs +41 -0
  605. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/models.mjs.map +1 -0
  606. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/shared.d.mts +42 -0
  607. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/shared.d.mts.map +1 -0
  608. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/shared.d.ts +42 -0
  609. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/shared.d.ts.map +1 -0
  610. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/shared.js +4 -0
  611. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/shared.js.map +1 -0
  612. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/shared.mjs +3 -0
  613. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/shared.mjs.map +1 -0
  614. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/top-level.d.mts +2 -0
  615. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/top-level.d.mts.map +1 -0
  616. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/top-level.d.ts +2 -0
  617. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/top-level.d.ts.map +1 -0
  618. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/top-level.js +4 -0
  619. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/top-level.js.map +1 -0
  620. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/top-level.mjs +3 -0
  621. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources/top-level.mjs.map +1 -0
  622. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources.d.mts +2 -0
  623. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources.d.mts.map +1 -0
  624. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources.d.ts +2 -0
  625. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources.d.ts.map +1 -0
  626. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources.js +5 -0
  627. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources.js.map +1 -0
  628. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources.mjs +2 -0
  629. package/scripts/huddle/node_modules/@anthropic-ai/sdk/resources.mjs.map +1 -0
  630. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/_vendor/partial-json-parser/README.md +3 -0
  631. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/_vendor/partial-json-parser/parser.ts +264 -0
  632. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/api-promise.ts +2 -0
  633. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/client.ts +1070 -0
  634. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/core/README.md +3 -0
  635. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/core/api-promise.ts +101 -0
  636. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/core/error.ts +133 -0
  637. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/core/pagination.ts +201 -0
  638. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/core/resource.ts +11 -0
  639. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/core/streaming.ts +331 -0
  640. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/core/uploads.ts +2 -0
  641. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/error.ts +2 -0
  642. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/index.ts +23 -0
  643. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/README.md +3 -0
  644. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/builtin-types.ts +93 -0
  645. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/constants.ts +12 -0
  646. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/decoders/jsonl.ts +48 -0
  647. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/decoders/line.ts +135 -0
  648. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/detect-platform.ts +196 -0
  649. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/errors.ts +33 -0
  650. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/headers.ts +99 -0
  651. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/parse.ts +84 -0
  652. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/request-options.ts +39 -0
  653. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/shim-types.d.ts +28 -0
  654. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/shims.ts +107 -0
  655. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/stream-utils.ts +32 -0
  656. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/to-file.ts +159 -0
  657. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/types.ts +92 -0
  658. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/uploads.ts +193 -0
  659. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/utils/base64.ts +40 -0
  660. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/utils/bytes.ts +32 -0
  661. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/utils/env.ts +18 -0
  662. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/utils/log.ts +127 -0
  663. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/utils/path.ts +65 -0
  664. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/utils/sleep.ts +3 -0
  665. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/utils/uuid.ts +17 -0
  666. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/utils/values.ts +102 -0
  667. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/internal/utils.ts +8 -0
  668. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/lib/.keep +4 -0
  669. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/lib/BetaMessageStream.ts +683 -0
  670. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/lib/MessageStream.ts +684 -0
  671. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/pagination.ts +2 -0
  672. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resource.ts +2 -0
  673. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/beta/beta.ts +380 -0
  674. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/beta/files.ts +258 -0
  675. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/beta/index.ts +148 -0
  676. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/beta/messages/batches.ts +502 -0
  677. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/beta/messages/index.ts +135 -0
  678. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/beta/messages/messages.ts +2249 -0
  679. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/beta/messages.ts +3 -0
  680. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/beta/models.ts +118 -0
  681. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/beta.ts +3 -0
  682. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/completions.ts +231 -0
  683. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/index.ts +121 -0
  684. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/messages/batches.ts +396 -0
  685. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/messages/index.ts +110 -0
  686. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/messages/messages.ts +1783 -0
  687. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/messages.ts +3 -0
  688. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/models.ts +103 -0
  689. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/shared.ts +72 -0
  690. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources/top-level.ts +3 -0
  691. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/resources.ts +1 -0
  692. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/streaming.ts +2 -0
  693. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/tsconfig.json +11 -0
  694. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/uploads.ts +2 -0
  695. package/scripts/huddle/node_modules/@anthropic-ai/sdk/src/version.ts +1 -0
  696. package/scripts/huddle/node_modules/@anthropic-ai/sdk/streaming.d.mts +2 -0
  697. package/scripts/huddle/node_modules/@anthropic-ai/sdk/streaming.d.mts.map +1 -0
  698. package/scripts/huddle/node_modules/@anthropic-ai/sdk/streaming.d.ts +2 -0
  699. package/scripts/huddle/node_modules/@anthropic-ai/sdk/streaming.d.ts.map +1 -0
  700. package/scripts/huddle/node_modules/@anthropic-ai/sdk/streaming.js +6 -0
  701. package/scripts/huddle/node_modules/@anthropic-ai/sdk/streaming.js.map +1 -0
  702. package/scripts/huddle/node_modules/@anthropic-ai/sdk/streaming.mjs +2 -0
  703. package/scripts/huddle/node_modules/@anthropic-ai/sdk/streaming.mjs.map +1 -0
  704. package/scripts/huddle/node_modules/@anthropic-ai/sdk/uploads.d.mts +2 -0
  705. package/scripts/huddle/node_modules/@anthropic-ai/sdk/uploads.d.mts.map +1 -0
  706. package/scripts/huddle/node_modules/@anthropic-ai/sdk/uploads.d.ts +2 -0
  707. package/scripts/huddle/node_modules/@anthropic-ai/sdk/uploads.d.ts.map +1 -0
  708. package/scripts/huddle/node_modules/@anthropic-ai/sdk/uploads.js +6 -0
  709. package/scripts/huddle/node_modules/@anthropic-ai/sdk/uploads.js.map +1 -0
  710. package/scripts/huddle/node_modules/@anthropic-ai/sdk/uploads.mjs +2 -0
  711. package/scripts/huddle/node_modules/@anthropic-ai/sdk/uploads.mjs.map +1 -0
  712. package/scripts/huddle/node_modules/@anthropic-ai/sdk/version.d.mts +2 -0
  713. package/scripts/huddle/node_modules/@anthropic-ai/sdk/version.d.mts.map +1 -0
  714. package/scripts/huddle/node_modules/@anthropic-ai/sdk/version.d.ts +2 -0
  715. package/scripts/huddle/node_modules/@anthropic-ai/sdk/version.d.ts.map +1 -0
  716. package/scripts/huddle/node_modules/@anthropic-ai/sdk/version.js +5 -0
  717. package/scripts/huddle/node_modules/@anthropic-ai/sdk/version.js.map +1 -0
  718. package/scripts/huddle/node_modules/@anthropic-ai/sdk/version.mjs +2 -0
  719. package/scripts/huddle/node_modules/@anthropic-ai/sdk/version.mjs.map +1 -0
  720. package/scripts/huddle/node_modules/dotenv/CHANGELOG.md +520 -0
  721. package/scripts/huddle/node_modules/dotenv/LICENSE +23 -0
  722. package/scripts/huddle/node_modules/dotenv/README-es.md +411 -0
  723. package/scripts/huddle/node_modules/dotenv/README.md +645 -0
  724. package/scripts/huddle/node_modules/dotenv/SECURITY.md +1 -0
  725. package/scripts/huddle/node_modules/dotenv/config.d.ts +1 -0
  726. package/scripts/huddle/node_modules/dotenv/config.js +9 -0
  727. package/scripts/huddle/node_modules/dotenv/lib/cli-options.js +17 -0
  728. package/scripts/huddle/node_modules/dotenv/lib/env-options.js +28 -0
  729. package/scripts/huddle/node_modules/dotenv/lib/main.d.ts +162 -0
  730. package/scripts/huddle/node_modules/dotenv/lib/main.js +386 -0
  731. package/scripts/huddle/node_modules/dotenv/package.json +62 -0
  732. package/scripts/huddle/node_modules/ws/LICENSE +20 -0
  733. package/scripts/huddle/node_modules/ws/README.md +548 -0
  734. package/scripts/huddle/node_modules/ws/browser.js +8 -0
  735. package/scripts/huddle/node_modules/ws/index.js +22 -0
  736. package/scripts/huddle/node_modules/ws/lib/buffer-util.js +131 -0
  737. package/scripts/huddle/node_modules/ws/lib/constants.js +19 -0
  738. package/scripts/huddle/node_modules/ws/lib/event-target.js +292 -0
  739. package/scripts/huddle/node_modules/ws/lib/extension.js +203 -0
  740. package/scripts/huddle/node_modules/ws/lib/limiter.js +55 -0
  741. package/scripts/huddle/node_modules/ws/lib/permessage-deflate.js +528 -0
  742. package/scripts/huddle/node_modules/ws/lib/receiver.js +706 -0
  743. package/scripts/huddle/node_modules/ws/lib/sender.js +602 -0
  744. package/scripts/huddle/node_modules/ws/lib/stream.js +161 -0
  745. package/scripts/huddle/node_modules/ws/lib/subprotocol.js +62 -0
  746. package/scripts/huddle/node_modules/ws/lib/validation.js +152 -0
  747. package/scripts/huddle/node_modules/ws/lib/websocket-server.js +554 -0
  748. package/scripts/huddle/node_modules/ws/lib/websocket.js +1393 -0
  749. package/scripts/huddle/node_modules/ws/package.json +70 -0
  750. package/scripts/huddle/node_modules/ws/wrapper.mjs +21 -0
  751. package/scripts/huddle/package-lock.json +62 -0
  752. package/scripts/huddle/package.json +22 -0
  753. package/scripts/huddle/setup-audio.sh +239 -0
  754. package/scripts/huddle/start-call.mjs +318 -0
  755. package/scripts/huddle/test-pipeline.mjs +263 -0
  756. package/scripts/llm_email_dedup.py +434 -0
  757. package/scripts/local-triggers/install-all.sh +43 -0
  758. package/scripts/local-triggers/plists/ai.adaptic.slack-events-server.plist +45 -0
  759. package/scripts/local-triggers/plists/ai.adaptic.sophie-backlog-executor.plist +21 -0
  760. package/scripts/local-triggers/plists/ai.adaptic.sophie-daemon.plist +32 -0
  761. package/scripts/local-triggers/plists/ai.adaptic.sophie-inbox-processor.plist +21 -0
  762. package/scripts/local-triggers/plists/ai.adaptic.sophie-meeting-action-capture.plist +21 -0
  763. package/scripts/local-triggers/plists/ai.adaptic.sophie-meeting-prep.plist +21 -0
  764. package/scripts/local-triggers/plists/ai.adaptic.sophie-midday-sweep.plist +26 -0
  765. package/scripts/local-triggers/plists/ai.adaptic.sophie-quarterly-self-assessment.plist +62 -0
  766. package/scripts/local-triggers/plists/ai.adaptic.sophie-weekly-engineering-health.plist +28 -0
  767. package/scripts/local-triggers/plists/ai.adaptic.sophie-weekly-execution.plist +28 -0
  768. package/scripts/local-triggers/plists/ai.adaptic.sophie-weekly-hiring.plist +28 -0
  769. package/scripts/local-triggers/plists/ai.adaptic.sophie-weekly-priorities.plist +28 -0
  770. package/scripts/local-triggers/plists/ai.adaptic.sophie-weekly-strategic-memo.plist +28 -0
  771. package/scripts/local-triggers/run-trigger.sh +44 -0
  772. package/scripts/media-generation/README.md +103 -0
  773. package/scripts/media-generation/gemini-image-client.mjs +173 -0
  774. package/scripts/media-generation/generate-assets.mjs +289 -0
  775. package/scripts/media-generation/veo-video-client.mjs +219 -0
  776. package/scripts/mehran-inbox-poller.py +437 -0
  777. package/scripts/outbound-dedup-cleanup.sh +43 -0
  778. package/scripts/outbound-dedup.sh +476 -0
  779. package/scripts/outbound_dedup.py +115 -0
  780. package/scripts/parse-voice-transcript.mjs +486 -0
  781. package/scripts/pdf-generation/README.md +61 -0
  782. package/scripts/pdf-generation/build-document.mjs +230 -0
  783. package/scripts/pdf-generation/templates/board-pack.latex +136 -0
  784. package/scripts/pdf-generation/templates/corporate-letter.latex +126 -0
  785. package/scripts/pdf-generation/templates/memo.latex +114 -0
  786. package/scripts/poll-slack-events.sh +33 -0
  787. package/scripts/poller/calendar-poller.mjs +12 -0
  788. package/scripts/poller/gmail-poller.mjs +156 -0
  789. package/scripts/poller/imap-client.mjs +286 -0
  790. package/scripts/poller/index.mjs +73 -0
  791. package/scripts/poller/intra-session-check.mjs +267 -0
  792. package/scripts/poller/mehran-gmail-poller.mjs +98 -0
  793. package/scripts/poller/slack-poller.mjs +716 -0
  794. package/scripts/poller/trigger.mjs +47 -0
  795. package/scripts/poller/utils.mjs +253 -0
  796. package/scripts/poller-launchd/ai.adaptic.sophie-poller.plist +40 -0
  797. package/scripts/poller-launchd/ai.adaptic.sophie-whatsapp-handler.plist +39 -0
  798. package/scripts/poller-launchd/install.sh +38 -0
  799. package/scripts/post-interaction-indexer.py +1598 -0
  800. package/scripts/pre-draft-context.py +994 -0
  801. package/scripts/pre_draft_lookup.py +258 -0
  802. package/scripts/rag-indexer.py +629 -0
  803. package/scripts/resume-operations.sh +40 -0
  804. package/scripts/search-mehran-inbox.py +181 -0
  805. package/scripts/self-optimization/compute-metrics.py +377 -0
  806. package/scripts/send-difc-rfp.sh +98 -0
  807. package/scripts/send-email-as-mehran.py +369 -0
  808. package/scripts/send-email-threaded.py +336 -0
  809. package/scripts/send-email-with-attachment.py +360 -0
  810. package/scripts/send-email.sh +93 -0
  811. package/scripts/send-sms.sh +151 -0
  812. package/scripts/send-whatsapp.sh +261 -0
  813. package/scripts/session-start.sh +106 -0
  814. package/scripts/setup/configure-macos.sh +508 -0
  815. package/scripts/setup/init-agent.sh +450 -0
  816. package/scripts/slack-events-ctl.sh +177 -0
  817. package/scripts/slack-events-server.mjs +989 -0
  818. package/scripts/slack-react.mjs +89 -0
  819. package/scripts/slack-responded.sh +185 -0
  820. package/scripts/slack-send.sh +190 -0
  821. package/scripts/slack-typing.mjs +196 -0
  822. package/scripts/slack-upload-v2.py +95 -0
  823. package/scripts/sms-handler.mjs +436 -0
  824. package/scripts/sophie-inbox-poller.py +406 -0
  825. package/scripts/spawn-session.sh +85 -0
  826. package/scripts/system-verify.sh +171 -0
  827. package/scripts/test-email-thread-dedup.py +239 -0
  828. package/scripts/test-information-barriers.py +484 -0
  829. package/scripts/test-llm-email-dedup.py +251 -0
  830. package/scripts/test-pre-draft-integration.py +203 -0
  831. package/scripts/test-rag-phase2.sh +442 -0
  832. package/scripts/test-rag-search.sh +251 -0
  833. package/scripts/test-voice-parser.mjs +316 -0
  834. package/scripts/user-context-search.py +660 -0
  835. package/scripts/validate-outbound.py +1493 -0
  836. package/scripts/whatsapp-handler.mjs +525 -0
  837. package/teams/desktop-operations.yaml +34 -0
  838. package/teams/executive-office.yaml +27 -0
  839. package/teams/legal-and-regulatory.yaml +24 -0
  840. package/teams/platform-and-engineering.yaml +23 -0
  841. package/teams/strategy-and-growth.yaml +29 -0
  842. package/workflows/continuous/inbound-monitor.yaml +168 -0
  843. package/workflows/daily/applicant-triage.yaml +197 -0
  844. package/workflows/daily/comms-triage.yaml +80 -0
  845. package/workflows/daily/evening-wrap.yaml +65 -0
  846. package/workflows/daily/morning-brief.yaml +147 -0
  847. package/workflows/daily/slack-followup-sweep.yaml +87 -0
  848. package/workflows/event-driven/README.md +50 -0
  849. package/workflows/monthly/board-readiness.yaml +76 -0
  850. package/workflows/quarterly/strategic-scenario-analysis.yaml +85 -0
  851. package/workflows/session-protocol.md +171 -0
  852. package/workflows/weekly/hiring-review.yaml +169 -0
  853. package/workflows/weekly/rollup-pipeline-review.yaml +76 -0
  854. package/workflows/weekly/strategic-memo.yaml +79 -0
@@ -0,0 +1,1994 @@
1
+ # Session Context & Dedup Overhaul — Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Eliminate duplicate responses and inject pre-compiled conversational context into every daemon-spawned session, with information barrier filtering.
6
+
7
+ **Architecture:** Two new modules (`session-lock.mjs` for file-based dedup/locking, `context-compiler.mjs` for pre-compiling context), plus integration changes to the four existing daemon modules. Feature-flagged via `DAEMON_CONTEXT_COMPILER=1`.
8
+
9
+ **Tech Stack:** Node.js ESM, `node:test` + `node:assert` for tests, file-based persistence (JSON/JSONL), YAML parsing via string matching (no new deps).
10
+
11
+ **Spec:** `docs/superpowers/specs/2026-04-06-session-context-dedup-design.md`
12
+
13
+ ---
14
+
15
+ ## File Structure
16
+
17
+ ### New Files
18
+
19
+ | File | Responsibility |
20
+ |------|---------------|
21
+ | `scripts/daemon/session-lock.mjs` | File-based item locks + sent-message registry. Exports: `acquireLock`, `releaseLock`, `updateLock`, `readLock`, `scanStaleLocks`, `checkRecentlySent`, `registerSent`, `sanitiseItemId` |
22
+ | `scripts/daemon/context-compiler.mjs` | Pre-compiles sender profile, disclosure boundaries, conversation history, active sessions, and sent-messages into a single context block. Exports: `compileContext` |
23
+ | `scripts/daemon/test-session-lock.mjs` | Unit tests for session-lock module |
24
+ | `scripts/daemon/test-context-compiler.mjs` | Unit tests for context-compiler module |
25
+ | `scripts/daemon/test-integration.mjs` | Integration tests for the full pipeline |
26
+ | `state/sessions/active.json` | Live manifest of currently running sessions |
27
+ | `state/sessions/locks/` | Directory for per-item lock files |
28
+ | `state/sessions/sent-registry.jsonl` | Append-only log of all sent messages |
29
+
30
+ ### Modified Files
31
+
32
+ | File | Change Summary |
33
+ |------|---------------|
34
+ | `scripts/daemon/sophie-daemon.mjs` | Remove in-memory dedup, use `acquireLock`/`releaseLock`, call `scanStaleLocks` on startup |
35
+ | `scripts/daemon/dispatcher.mjs` | Write/remove `active.json` entries, call `releaseLock` on session close, stabilise `backlogKey` |
36
+ | `scripts/daemon/prompt-builder.mjs` | Replace `loadConversationHistory` and profile-read instruction with `compileContext` output, feature flag |
37
+ | `scripts/daemon/responder.mjs` | Call `checkRecentlySent` before sending, `registerSent` after sending |
38
+
39
+ ---
40
+
41
+ ## Task 1: Create State Directory Structure
42
+
43
+ **Files:**
44
+ - Create: `state/sessions/active.json`
45
+ - Create: `state/sessions/locks/.gitkeep`
46
+ - Create: `state/sessions/sent-registry.jsonl`
47
+
48
+ - [ ] **Step 1: Create the directories and seed files**
49
+
50
+ ```bash
51
+ mkdir -p state/sessions/locks
52
+ echo '{}' > state/sessions/active.json
53
+ touch state/sessions/sent-registry.jsonl
54
+ touch state/sessions/locks/.gitkeep
55
+ ```
56
+
57
+ - [ ] **Step 2: Add to .gitignore (lock files and registry are ephemeral)**
58
+
59
+ Add to the project `.gitignore`:
60
+
61
+ ```
62
+ # Session state (ephemeral — regenerated at runtime)
63
+ state/sessions/locks/*.lock
64
+ state/sessions/sent-registry.jsonl
65
+ ```
66
+
67
+ Keep `active.json` tracked (seed file) and `locks/.gitkeep` tracked (preserves directory).
68
+
69
+ - [ ] **Step 3: Commit**
70
+
71
+ ```bash
72
+ git add state/sessions/active.json state/sessions/locks/.gitkeep .gitignore
73
+ git commit -m "chore: create state/sessions directory structure for session locks and context"
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Task 2: Implement Session Lock Module
79
+
80
+ **Files:**
81
+ - Create: `scripts/daemon/session-lock.mjs`
82
+ - Test: `scripts/daemon/test-session-lock.mjs`
83
+
84
+ - [ ] **Step 1: Write failing tests for `sanitiseItemId`**
85
+
86
+ Create `scripts/daemon/test-session-lock.mjs`:
87
+
88
+ ```javascript
89
+ import { describe, it, beforeEach, afterEach } from "node:test";
90
+ import assert from "node:assert/strict";
91
+ import { mkdirSync, rmSync, existsSync, readFileSync, writeFileSync } from "fs";
92
+ import { join } from "path";
93
+
94
+ // Tests use a temporary directory to avoid polluting real state
95
+ const TEST_DIR = join(import.meta.dirname, "../../.test-session-locks");
96
+ const LOCKS_DIR = join(TEST_DIR, "locks");
97
+ const REGISTRY_PATH = join(TEST_DIR, "sent-registry.jsonl");
98
+
99
+ // We'll set these env vars so the module uses our test paths
100
+ process.env.__TEST_SESSION_DIR = TEST_DIR;
101
+
102
+ const {
103
+ sanitiseItemId,
104
+ acquireLock,
105
+ releaseLock,
106
+ updateLock,
107
+ readLock,
108
+ scanStaleLocks,
109
+ checkRecentlySent,
110
+ registerSent,
111
+ } = await import("./session-lock.mjs");
112
+
113
+ describe("sanitiseItemId", () => {
114
+ it("replaces colons and dots with hyphens", () => {
115
+ assert.equal(
116
+ sanitiseItemId("slack:D099N1JGKRQ:1775331885.690669"),
117
+ "slack-D099N1JGKRQ-1775331885-690669"
118
+ );
119
+ });
120
+
121
+ it("handles simple alphanumeric IDs unchanged", () => {
122
+ assert.equal(sanitiseItemId("abc123"), "abc123");
123
+ });
124
+
125
+ it("replaces spaces and special chars", () => {
126
+ assert.equal(sanitiseItemId("foo bar/baz@qux"), "foo-bar-baz-qux");
127
+ });
128
+ });
129
+ ```
130
+
131
+ - [ ] **Step 2: Run test to verify it fails**
132
+
133
+ ```bash
134
+ node --test scripts/daemon/test-session-lock.mjs
135
+ ```
136
+
137
+ Expected: FAIL — `session-lock.mjs` does not exist.
138
+
139
+ - [ ] **Step 3: Write the session-lock module skeleton with `sanitiseItemId`**
140
+
141
+ Create `scripts/daemon/session-lock.mjs`:
142
+
143
+ ```javascript
144
+ #!/usr/bin/env node
145
+ /**
146
+ * session-lock.mjs — File-based session locks and sent-message registry
147
+ *
148
+ * Replaces the in-memory recentlyProcessed Map in sophie-daemon.mjs with
149
+ * persistent, atomic file locks that survive daemon restarts and prevent
150
+ * race conditions between concurrent polls.
151
+ *
152
+ * Also provides a sent-message registry to prevent duplicate sends even
153
+ * when locking fails (belt + suspenders).
154
+ */
155
+
156
+ import {
157
+ writeFileSync,
158
+ readFileSync,
159
+ unlinkSync,
160
+ readdirSync,
161
+ appendFileSync,
162
+ mkdirSync,
163
+ existsSync,
164
+ statSync,
165
+ } from "fs";
166
+ import { join } from "path";
167
+
168
+ // Allow test override of state directory
169
+ const SOPHIE_AI_DIR = join(new URL(".", import.meta.url).pathname, "../..");
170
+ const SESSION_DIR = process.env.__TEST_SESSION_DIR || join(SOPHIE_AI_DIR, "state", "sessions");
171
+ const LOCKS_DIR = join(SESSION_DIR, "locks");
172
+ const REGISTRY_PATH = join(SESSION_DIR, "sent-registry.jsonl");
173
+
174
+ const STALE_LOCK_MS = 15 * 60 * 1000; // 15 min — matches OPUS_TIMEOUT
175
+ const SENT_DEDUP_WINDOW_MS = 5 * 60 * 1000; // 5 min
176
+ const SENT_SUBSTANTIVE_WINDOW_MS = 2 * 60 * 1000; // 2 min for same-person substantive
177
+ const MAX_REGISTRY_ENTRIES = 500; // Keep registry file manageable
178
+
179
+ // Ensure directories exist
180
+ mkdirSync(LOCKS_DIR, { recursive: true });
181
+
182
+ /**
183
+ * Sanitise an item ID for use as a filename.
184
+ * Replaces all non-alphanumeric characters with hyphens.
185
+ */
186
+ export function sanitiseItemId(itemId) {
187
+ return itemId.replace(/[^a-zA-Z0-9]/g, "-");
188
+ }
189
+ ```
190
+
191
+ - [ ] **Step 4: Run test to verify `sanitiseItemId` passes**
192
+
193
+ ```bash
194
+ node --test scripts/daemon/test-session-lock.mjs
195
+ ```
196
+
197
+ Expected: 3 tests pass.
198
+
199
+ - [ ] **Step 5: Write failing tests for `acquireLock` / `releaseLock` / `readLock`**
200
+
201
+ Append to `scripts/daemon/test-session-lock.mjs`:
202
+
203
+ ```javascript
204
+ describe("acquireLock / releaseLock / readLock", () => {
205
+ beforeEach(() => {
206
+ mkdirSync(LOCKS_DIR, { recursive: true });
207
+ });
208
+
209
+ afterEach(() => {
210
+ rmSync(TEST_DIR, { recursive: true, force: true });
211
+ });
212
+
213
+ it("acquires a lock on first call", () => {
214
+ const result = acquireLock("test-item-1", { sender: "Alice", channel: "C001" });
215
+ assert.equal(result.acquired, true);
216
+ assert.equal(existsSync(join(LOCKS_DIR, "test-item-1.lock")), true);
217
+ });
218
+
219
+ it("rejects second acquire for same item", () => {
220
+ acquireLock("test-item-2", { sender: "Alice", channel: "C001" });
221
+ const result = acquireLock("test-item-2", { sender: "Bob", channel: "C002" });
222
+ assert.equal(result.acquired, false);
223
+ assert.ok(result.existing);
224
+ assert.equal(result.existing.sender, "Alice");
225
+ });
226
+
227
+ it("allows acquire after release", () => {
228
+ acquireLock("test-item-3", { sender: "Alice", channel: "C001" });
229
+ releaseLock("test-item-3");
230
+ const result = acquireLock("test-item-3", { sender: "Bob", channel: "C002" });
231
+ assert.equal(result.acquired, true);
232
+ });
233
+
234
+ it("readLock returns lock data when locked", () => {
235
+ acquireLock("test-item-4", { sender: "Alice", channel: "C001" });
236
+ const data = readLock("test-item-4");
237
+ assert.equal(data.sender, "Alice");
238
+ assert.equal(data.channel, "C001");
239
+ assert.equal(data.holdingSent, false);
240
+ assert.equal(data.substantiveSent, false);
241
+ });
242
+
243
+ it("readLock returns null when no lock", () => {
244
+ const data = readLock("nonexistent-item");
245
+ assert.equal(data, null);
246
+ });
247
+
248
+ it("overwrites stale lock (>15 min old)", () => {
249
+ // Write a lock with an old timestamp
250
+ const lockPath = join(LOCKS_DIR, "test-item-5.lock");
251
+ mkdirSync(LOCKS_DIR, { recursive: true });
252
+ const staleLock = {
253
+ sessionId: "old-session",
254
+ itemId: "test-item-5",
255
+ sender: "Alice",
256
+ channel: "C001",
257
+ acquiredAt: new Date(Date.now() - 20 * 60 * 1000).toISOString(),
258
+ holdingSent: false,
259
+ substantiveSent: false,
260
+ };
261
+ writeFileSync(lockPath, JSON.stringify(staleLock));
262
+
263
+ const result = acquireLock("test-item-5", { sender: "Bob", channel: "C002" });
264
+ assert.equal(result.acquired, true);
265
+ const data = readLock("test-item-5");
266
+ assert.equal(data.sender, "Bob");
267
+ });
268
+ });
269
+ ```
270
+
271
+ - [ ] **Step 6: Run test to verify new tests fail**
272
+
273
+ ```bash
274
+ node --test scripts/daemon/test-session-lock.mjs
275
+ ```
276
+
277
+ Expected: `sanitiseItemId` tests pass, lock tests fail (`acquireLock` not exported).
278
+
279
+ - [ ] **Step 7: Implement `acquireLock`, `releaseLock`, `readLock`**
280
+
281
+ Append to `scripts/daemon/session-lock.mjs`:
282
+
283
+ ```javascript
284
+ /**
285
+ * Attempt to acquire a lock for an item.
286
+ * Uses exclusive file creation (wx flag) for atomicity.
287
+ *
288
+ * @param {string} itemId - Raw item ID (will be sanitised)
289
+ * @param {object} metadata - { sender, channel, sessionId? }
290
+ * @returns {{ acquired: boolean, existing?: object }}
291
+ */
292
+ export function acquireLock(itemId, metadata = {}) {
293
+ const safeId = sanitiseItemId(itemId);
294
+ const lockPath = join(LOCKS_DIR, `${safeId}.lock`);
295
+
296
+ // Check for existing lock
297
+ if (existsSync(lockPath)) {
298
+ try {
299
+ const existing = JSON.parse(readFileSync(lockPath, "utf-8"));
300
+ const age = Date.now() - new Date(existing.acquiredAt).getTime();
301
+
302
+ // If lock is fresh (< STALE_LOCK_MS), reject
303
+ if (age < STALE_LOCK_MS) {
304
+ return { acquired: false, existing };
305
+ }
306
+ // Stale lock — fall through to overwrite
307
+ } catch {
308
+ // Corrupted lock file — overwrite
309
+ }
310
+ }
311
+
312
+ const lockData = {
313
+ sessionId: metadata.sessionId || `pending-${Date.now()}`,
314
+ itemId,
315
+ channel: metadata.channel || null,
316
+ sender: metadata.sender || null,
317
+ acquiredAt: new Date().toISOString(),
318
+ holdingSent: false,
319
+ substantiveSent: false,
320
+ };
321
+
322
+ try {
323
+ // Try exclusive create first (atomic)
324
+ writeFileSync(lockPath, JSON.stringify(lockData, null, 2), { flag: "wx" });
325
+ return { acquired: true };
326
+ } catch (err) {
327
+ if (err.code === "EEXIST") {
328
+ // Another process beat us — check if it's stale
329
+ try {
330
+ const existing = JSON.parse(readFileSync(lockPath, "utf-8"));
331
+ const age = Date.now() - new Date(existing.acquiredAt).getTime();
332
+ if (age >= STALE_LOCK_MS) {
333
+ // Stale — overwrite (non-atomic but acceptable for stale locks)
334
+ writeFileSync(lockPath, JSON.stringify(lockData, null, 2));
335
+ return { acquired: true };
336
+ }
337
+ return { acquired: false, existing };
338
+ } catch {
339
+ return { acquired: false };
340
+ }
341
+ }
342
+ throw err;
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Release a lock for an item.
348
+ * @param {string} itemId - Raw item ID (will be sanitised)
349
+ */
350
+ export function releaseLock(itemId) {
351
+ const safeId = sanitiseItemId(itemId);
352
+ const lockPath = join(LOCKS_DIR, `${safeId}.lock`);
353
+ try {
354
+ unlinkSync(lockPath);
355
+ } catch {
356
+ // Lock already released or never existed — fine
357
+ }
358
+ }
359
+
360
+ /**
361
+ * Read the lock data for an item without modifying it.
362
+ * @param {string} itemId - Raw item ID (will be sanitised)
363
+ * @returns {object|null} Lock data or null if no lock
364
+ */
365
+ export function readLock(itemId) {
366
+ const safeId = sanitiseItemId(itemId);
367
+ const lockPath = join(LOCKS_DIR, `${safeId}.lock`);
368
+ try {
369
+ return JSON.parse(readFileSync(lockPath, "utf-8"));
370
+ } catch {
371
+ return null;
372
+ }
373
+ }
374
+ ```
375
+
376
+ - [ ] **Step 8: Run tests to verify lock tests pass**
377
+
378
+ ```bash
379
+ node --test scripts/daemon/test-session-lock.mjs
380
+ ```
381
+
382
+ Expected: All tests pass.
383
+
384
+ - [ ] **Step 9: Write failing tests for `updateLock` and `scanStaleLocks`**
385
+
386
+ Append to `scripts/daemon/test-session-lock.mjs`:
387
+
388
+ ```javascript
389
+ describe("updateLock", () => {
390
+ beforeEach(() => {
391
+ mkdirSync(LOCKS_DIR, { recursive: true });
392
+ });
393
+
394
+ afterEach(() => {
395
+ rmSync(TEST_DIR, { recursive: true, force: true });
396
+ });
397
+
398
+ it("updates holdingSent flag on existing lock", () => {
399
+ acquireLock("test-item-6", { sender: "Alice", channel: "C001" });
400
+ updateLock("test-item-6", { holdingSent: true });
401
+ const data = readLock("test-item-6");
402
+ assert.equal(data.holdingSent, true);
403
+ assert.equal(data.substantiveSent, false);
404
+ });
405
+
406
+ it("no-ops when lock does not exist", () => {
407
+ // Should not throw
408
+ updateLock("nonexistent", { holdingSent: true });
409
+ });
410
+ });
411
+
412
+ describe("scanStaleLocks", () => {
413
+ beforeEach(() => {
414
+ mkdirSync(LOCKS_DIR, { recursive: true });
415
+ });
416
+
417
+ afterEach(() => {
418
+ rmSync(TEST_DIR, { recursive: true, force: true });
419
+ });
420
+
421
+ it("clears stale locks and leaves fresh ones", () => {
422
+ // Fresh lock
423
+ acquireLock("fresh-item", { sender: "Alice", channel: "C001" });
424
+
425
+ // Stale lock (manually written with old timestamp)
426
+ const stalePath = join(LOCKS_DIR, "stale-item.lock");
427
+ writeFileSync(stalePath, JSON.stringify({
428
+ sessionId: "old", itemId: "stale-item", sender: "Bob", channel: "C002",
429
+ acquiredAt: new Date(Date.now() - 20 * 60 * 1000).toISOString(),
430
+ holdingSent: false, substantiveSent: false,
431
+ }));
432
+
433
+ const cleared = scanStaleLocks();
434
+ assert.equal(cleared, 1);
435
+ assert.equal(existsSync(join(LOCKS_DIR, "fresh-item.lock")), true);
436
+ assert.equal(existsSync(stalePath), false);
437
+ });
438
+ });
439
+ ```
440
+
441
+ - [ ] **Step 10: Implement `updateLock` and `scanStaleLocks`**
442
+
443
+ Append to `scripts/daemon/session-lock.mjs`:
444
+
445
+ ```javascript
446
+ /**
447
+ * Update fields on an existing lock.
448
+ * @param {string} itemId - Raw item ID
449
+ * @param {object} updates - Fields to merge (e.g., { holdingSent: true })
450
+ */
451
+ export function updateLock(itemId, updates) {
452
+ const safeId = sanitiseItemId(itemId);
453
+ const lockPath = join(LOCKS_DIR, `${safeId}.lock`);
454
+ try {
455
+ const existing = JSON.parse(readFileSync(lockPath, "utf-8"));
456
+ const updated = { ...existing, ...updates };
457
+ writeFileSync(lockPath, JSON.stringify(updated, null, 2));
458
+ } catch {
459
+ // Lock doesn't exist — no-op
460
+ }
461
+ }
462
+
463
+ /**
464
+ * Scan and remove stale locks (older than STALE_LOCK_MS).
465
+ * Call on daemon startup to clean up after crashes.
466
+ * @returns {number} Number of stale locks cleared
467
+ */
468
+ export function scanStaleLocks() {
469
+ let cleared = 0;
470
+ try {
471
+ const files = readdirSync(LOCKS_DIR).filter((f) => f.endsWith(".lock"));
472
+ for (const file of files) {
473
+ const lockPath = join(LOCKS_DIR, file);
474
+ try {
475
+ const data = JSON.parse(readFileSync(lockPath, "utf-8"));
476
+ const age = Date.now() - new Date(data.acquiredAt).getTime();
477
+ if (age >= STALE_LOCK_MS) {
478
+ unlinkSync(lockPath);
479
+ cleared++;
480
+ }
481
+ } catch {
482
+ // Corrupted lock — remove it
483
+ try { unlinkSync(lockPath); cleared++; } catch {}
484
+ }
485
+ }
486
+ } catch {
487
+ // LOCKS_DIR doesn't exist yet — nothing to scan
488
+ }
489
+ return cleared;
490
+ }
491
+ ```
492
+
493
+ - [ ] **Step 11: Run tests to verify all pass**
494
+
495
+ ```bash
496
+ node --test scripts/daemon/test-session-lock.mjs
497
+ ```
498
+
499
+ Expected: All tests pass.
500
+
501
+ - [ ] **Step 12: Write failing tests for sent-message registry**
502
+
503
+ Append to `scripts/daemon/test-session-lock.mjs`:
504
+
505
+ ```javascript
506
+ describe("sent-message registry", () => {
507
+ beforeEach(() => {
508
+ mkdirSync(TEST_DIR, { recursive: true });
509
+ // Ensure clean registry
510
+ try { unlinkSync(REGISTRY_PATH); } catch {}
511
+ });
512
+
513
+ afterEach(() => {
514
+ rmSync(TEST_DIR, { recursive: true, force: true });
515
+ });
516
+
517
+ it("allows first send to a channel", () => {
518
+ const result = checkRecentlySent("C001", "1234.5678", "holding");
519
+ assert.equal(result.allowed, true);
520
+ });
521
+
522
+ it("blocks duplicate send to same channel+thread+type within window", () => {
523
+ registerSent("C001", "1234.5678", "holding", "s-1", "Got it");
524
+ const result = checkRecentlySent("C001", "1234.5678", "holding");
525
+ assert.equal(result.allowed, false);
526
+ assert.ok(result.reason.includes("duplicate"));
527
+ });
528
+
529
+ it("allows different type to same channel+thread", () => {
530
+ registerSent("C001", "1234.5678", "holding", "s-1", "Got it");
531
+ const result = checkRecentlySent("C001", "1234.5678", "substantive");
532
+ assert.equal(result.allowed, true);
533
+ });
534
+
535
+ it("allows same type to different channel", () => {
536
+ registerSent("C001", "1234.5678", "holding", "s-1", "Got it");
537
+ const result = checkRecentlySent("C002", "1234.5678", "holding");
538
+ assert.equal(result.allowed, true);
539
+ });
540
+
541
+ it("blocks rapid substantive sends to same channel even with different threads", () => {
542
+ registerSent("C001", "1111.0000", "substantive", "s-1", "Here is the answer");
543
+ const result = checkRecentlySent("C001", "2222.0000", "substantive");
544
+ assert.equal(result.allowed, false);
545
+ assert.ok(result.reason.includes("rapid"));
546
+ });
547
+ });
548
+ ```
549
+
550
+ - [ ] **Step 13: Implement `registerSent` and `checkRecentlySent`**
551
+
552
+ Append to `scripts/daemon/session-lock.mjs`:
553
+
554
+ ```javascript
555
+ /**
556
+ * Register that a message was sent.
557
+ * @param {string} channel - Slack channel ID or email address
558
+ * @param {string|null} threadTs - Thread timestamp (null for non-threaded)
559
+ * @param {string} type - "holding" | "quick_reply" | "substantive"
560
+ * @param {string} sessionId - Session that sent it
561
+ * @param {string} textPreview - First ~100 chars of the message
562
+ */
563
+ export function registerSent(channel, threadTs, type, sessionId, textPreview) {
564
+ const entry = {
565
+ timestamp: new Date().toISOString(),
566
+ channel,
567
+ threadTs: threadTs || null,
568
+ type,
569
+ sessionId,
570
+ textPreview: (textPreview || "").substring(0, 100),
571
+ };
572
+ try {
573
+ appendFileSync(REGISTRY_PATH, JSON.stringify(entry) + "\n");
574
+ } catch {
575
+ // Registry dir might not exist yet in tests
576
+ mkdirSync(SESSION_DIR, { recursive: true });
577
+ appendFileSync(REGISTRY_PATH, JSON.stringify(entry) + "\n");
578
+ }
579
+ }
580
+
581
+ /**
582
+ * Check if a similar message was recently sent to the same channel/thread.
583
+ * @param {string} channel
584
+ * @param {string|null} threadTs
585
+ * @param {string} type - "holding" | "quick_reply" | "substantive"
586
+ * @returns {{ allowed: boolean, reason?: string }}
587
+ */
588
+ export function checkRecentlySent(channel, threadTs, type) {
589
+ let entries = [];
590
+ try {
591
+ const raw = readFileSync(REGISTRY_PATH, "utf-8").trim();
592
+ if (!raw) return { allowed: true };
593
+ const lines = raw.split("\n").filter(Boolean);
594
+ // Only check recent entries (last MAX_REGISTRY_ENTRIES lines)
595
+ const recent = lines.slice(-MAX_REGISTRY_ENTRIES);
596
+ entries = recent.map((line) => {
597
+ try { return JSON.parse(line); } catch { return null; }
598
+ }).filter(Boolean);
599
+ } catch {
600
+ // No registry file yet — nothing sent
601
+ return { allowed: true };
602
+ }
603
+
604
+ const now = Date.now();
605
+
606
+ // Rule 1: Same channel + thread + type within 5 min → BLOCK
607
+ const exactMatch = entries.find((e) => {
608
+ const age = now - new Date(e.timestamp).getTime();
609
+ return e.channel === channel
610
+ && e.threadTs === (threadTs || null)
611
+ && e.type === type
612
+ && age < SENT_DEDUP_WINDOW_MS;
613
+ });
614
+ if (exactMatch) {
615
+ return { allowed: false, reason: `duplicate: same channel+thread+type sent ${Math.round((now - new Date(exactMatch.timestamp).getTime()) / 1000)}s ago` };
616
+ }
617
+
618
+ // Rule 2: Same channel + substantive within 2 min (any thread) → BLOCK
619
+ if (type === "substantive") {
620
+ const rapidMatch = entries.find((e) => {
621
+ const age = now - new Date(e.timestamp).getTime();
622
+ return e.channel === channel
623
+ && e.type === "substantive"
624
+ && age < SENT_SUBSTANTIVE_WINDOW_MS;
625
+ });
626
+ if (rapidMatch) {
627
+ return { allowed: false, reason: `rapid: substantive message to same channel sent ${Math.round((now - new Date(rapidMatch.timestamp).getTime()) / 1000)}s ago` };
628
+ }
629
+ }
630
+
631
+ return { allowed: true };
632
+ }
633
+ ```
634
+
635
+ - [ ] **Step 14: Run all session-lock tests**
636
+
637
+ ```bash
638
+ node --test scripts/daemon/test-session-lock.mjs
639
+ ```
640
+
641
+ Expected: All tests pass (sanitiseItemId + lock lifecycle + registry).
642
+
643
+ - [ ] **Step 15: Commit**
644
+
645
+ ```bash
646
+ git add scripts/daemon/session-lock.mjs scripts/daemon/test-session-lock.mjs
647
+ git commit -m "feat: file-based session locks and sent-message registry
648
+
649
+ Replaces in-memory dedup Map with persistent file locks using atomic
650
+ exclusive-create (wx flag). Adds sent-message registry to prevent
651
+ duplicate sends even when locking fails. All tests passing."
652
+ ```
653
+
654
+ ---
655
+
656
+ ## Task 3: Implement Context Compiler
657
+
658
+ **Files:**
659
+ - Create: `scripts/daemon/context-compiler.mjs`
660
+ - Test: `scripts/daemon/test-context-compiler.mjs`
661
+
662
+ - [ ] **Step 1: Write failing tests for token budget determination**
663
+
664
+ Create `scripts/daemon/test-context-compiler.mjs`:
665
+
666
+ ```javascript
667
+ import { describe, it, beforeEach, afterEach } from "node:test";
668
+ import assert from "node:assert/strict";
669
+ import { mkdirSync, rmSync, writeFileSync } from "fs";
670
+ import { join } from "path";
671
+
672
+ const TEST_DIR = join(import.meta.dirname, "../../.test-context-compiler");
673
+ process.env.__TEST_SOPHIE_DIR = TEST_DIR;
674
+
675
+ // Create minimal directory structure needed
676
+ function setupTestDir() {
677
+ mkdirSync(join(TEST_DIR, "memory", "profiles", "users"), { recursive: true });
678
+ mkdirSync(join(TEST_DIR, "memory", "interactions", "slack", "dm-alice-smith"), { recursive: true });
679
+ mkdirSync(join(TEST_DIR, "memory", "interactions", "slack", "D001TESTCH"), { recursive: true });
680
+ mkdirSync(join(TEST_DIR, "policies"), { recursive: true });
681
+ mkdirSync(join(TEST_DIR, "state", "sessions"), { recursive: true });
682
+ writeFileSync(join(TEST_DIR, "state", "sessions", "active.json"), "{}");
683
+ }
684
+
685
+ function cleanTestDir() {
686
+ rmSync(TEST_DIR, { recursive: true, force: true });
687
+ }
688
+
689
+ // Write a minimal information-barriers.yaml for tests
690
+ function writeBarriersPolicy() {
691
+ writeFileSync(join(TEST_DIR, "policies", "information-barriers.yaml"), `domains:
692
+ adaptic-internal-legal:
693
+ description: "Adaptic's own legal strategy, GC advice, privilege-protected"
694
+ default_sensitivity: high
695
+ rollup-strategy:
696
+ description: "Project Aether M&A targets, deal terms, pipeline"
697
+ default_sensitivity: critical
698
+ ftlab-operations:
699
+ description: "FTLab JV operational matters"
700
+ default_sensitivity: low
701
+ public:
702
+ description: "Published information, press, website — no restrictions"
703
+ default_sensitivity: none
704
+ `);
705
+ }
706
+
707
+ describe("compileContext", () => {
708
+ beforeEach(() => {
709
+ setupTestDir();
710
+ writeBarriersPolicy();
711
+ });
712
+
713
+ afterEach(() => {
714
+ cleanTestDir();
715
+ });
716
+
717
+ it("returns adaptive token budget based on priority", async () => {
718
+ const { compileContext } = await import("./context-compiler.mjs");
719
+
720
+ const criticalResult = await compileContext(
721
+ { sender: "Unknown", channel: "C001" },
722
+ { priority: "critical", model: "opus", action: "respond" },
723
+ {}
724
+ );
725
+ assert.ok(criticalResult.tokenBudget >= 12000);
726
+
727
+ const normalResult = await compileContext(
728
+ { sender: "Unknown", channel: "C001" },
729
+ { priority: "normal", model: "sonnet", action: "respond" },
730
+ {}
731
+ );
732
+ assert.ok(normalResult.tokenBudget >= 8000 && normalResult.tokenBudget < 12000);
733
+
734
+ const lowResult = await compileContext(
735
+ { sender: "Unknown", channel: "C001" },
736
+ { priority: "low", model: "sonnet", action: "respond" },
737
+ {}
738
+ );
739
+ assert.ok(lowResult.tokenBudget >= 4000 && lowResult.tokenBudget < 8000);
740
+ });
741
+
742
+ it("includes sender profile when available", async () => {
743
+ writeFileSync(join(TEST_DIR, "memory", "profiles", "users", "alice-smith.yaml"),
744
+ `name: Alice Smith\nrole: Partner\nprivilege_level: leadership\ncommunication_preferences:\n voice: formal\n`
745
+ );
746
+
747
+ const { compileContext } = await import("./context-compiler.mjs");
748
+ const result = await compileContext(
749
+ { sender: "Alice Smith", channel: "C001" },
750
+ { priority: "normal", model: "sonnet", action: "respond" },
751
+ {}
752
+ );
753
+ assert.ok(result.contextBlock.includes("Alice Smith"));
754
+ assert.ok(result.contextBlock.includes("Sender Profile"));
755
+ });
756
+
757
+ it("produces minimal block when no profile exists", async () => {
758
+ const { compileContext } = await import("./context-compiler.mjs");
759
+ const result = await compileContext(
760
+ { sender: "Nobody Special", channel: "C001" },
761
+ { priority: "normal", model: "sonnet", action: "respond" },
762
+ {}
763
+ );
764
+ assert.ok(result.contextBlock.includes("COMPILED CONTEXT"));
765
+ // Should not crash, should still have section headers
766
+ assert.ok(result.contextBlock.includes("END CONTEXT"));
767
+ });
768
+
769
+ it("filters history by disclosure boundaries", async () => {
770
+ // Profile with excluded domains
771
+ writeFileSync(join(TEST_DIR, "memory", "profiles", "users", "alice-smith.yaml"),
772
+ `name: Alice Smith\nrole: Partner\ninformation_boundaries:\n excluded_domains:\n - rollup-strategy\n - adaptic-internal-legal\n`
773
+ );
774
+
775
+ // History with mixed domain content
776
+ const historyDir = join(TEST_DIR, "memory", "interactions", "slack", "dm-alice-smith");
777
+ const today = new Date().toISOString().split("T")[0];
778
+ writeFileSync(join(historyDir, `${today}.jsonl`), [
779
+ JSON.stringify({ ts: "1000", from: "alice-smith", content: "How is the FTLab project going?", received_at: "2026-04-06T10:00:00Z" }),
780
+ JSON.stringify({ ts: "1001", from: "sophie", content: "Project Aether acquisition target list has 9 candidates", received_at: "2026-04-06T10:01:00Z" }),
781
+ JSON.stringify({ ts: "1002", from: "sophie", content: "The FTLab operations are on track", received_at: "2026-04-06T10:02:00Z" }),
782
+ JSON.stringify({ ts: "1003", from: "sophie", content: "GC advice on the legal strategy is privileged", received_at: "2026-04-06T10:03:00Z" }),
783
+ ].join("\n") + "\n");
784
+
785
+ const { compileContext } = await import("./context-compiler.mjs");
786
+ const result = await compileContext(
787
+ { sender: "Alice Smith", channel: "D001TESTCH", channel_id: "D001TESTCH" },
788
+ { priority: "normal", model: "sonnet", action: "respond" },
789
+ {}
790
+ );
791
+
792
+ // FTLab operations message should be included
793
+ assert.ok(result.contextBlock.includes("FTLab operations are on track"));
794
+ assert.ok(result.contextBlock.includes("FTLab project going"));
795
+ // Rollup strategy and legal messages should be filtered out
796
+ assert.ok(!result.contextBlock.includes("Aether acquisition target"));
797
+ assert.ok(!result.contextBlock.includes("GC advice"));
798
+ });
799
+
800
+ it("includes disclosure boundaries block with restrictions", async () => {
801
+ writeFileSync(join(TEST_DIR, "memory", "profiles", "users", "alice-smith.yaml"),
802
+ `name: Alice Smith\ninformation_boundaries:\n excluded_domains:\n - rollup-strategy\n - adaptic-internal-legal\n`
803
+ );
804
+
805
+ const { compileContext } = await import("./context-compiler.mjs");
806
+ const result = await compileContext(
807
+ { sender: "Alice Smith", channel: "C001" },
808
+ { priority: "normal", model: "sonnet", action: "respond" },
809
+ {}
810
+ );
811
+ assert.ok(result.contextBlock.includes("Disclosure Boundaries"));
812
+ assert.ok(result.contextBlock.includes("DO NOT mention"));
813
+ });
814
+
815
+ it("includes active sessions when present", async () => {
816
+ writeFileSync(join(TEST_DIR, "state", "sessions", "active.json"), JSON.stringify({
817
+ "s-100": { sender: "Bob", channel: "C002", summary: "Draft investor deck", model: "opus", startTime: Date.now() - 60000 }
818
+ }));
819
+
820
+ const { compileContext } = await import("./context-compiler.mjs");
821
+ const result = await compileContext(
822
+ { sender: "Unknown", channel: "C001" },
823
+ { priority: "normal", model: "sonnet", action: "respond" },
824
+ {}
825
+ );
826
+ assert.ok(result.contextBlock.includes("Active Sessions"));
827
+ assert.ok(result.contextBlock.includes("Draft investor deck"));
828
+ });
829
+
830
+ it("includes sent-registry entries for same sender", async () => {
831
+ writeFileSync(join(TEST_DIR, "state", "sessions", "sent-registry.jsonl"),
832
+ JSON.stringify({
833
+ timestamp: new Date().toISOString(),
834
+ channel: "D001TESTCH",
835
+ threadTs: null,
836
+ type: "holding",
837
+ sessionId: "s-1",
838
+ textPreview: "Got it, looking into this now."
839
+ }) + "\n"
840
+ );
841
+
842
+ const { compileContext } = await import("./context-compiler.mjs");
843
+ const result = await compileContext(
844
+ { sender: "Unknown", channel: "D001TESTCH", channel_id: "D001TESTCH" },
845
+ { priority: "normal", model: "sonnet", action: "respond" },
846
+ {}
847
+ );
848
+ assert.ok(result.contextBlock.includes("Already Sent"));
849
+ assert.ok(result.contextBlock.includes("Got it, looking into this"));
850
+ });
851
+
852
+ it("CEO gets all domains — no filtering", async () => {
853
+ writeFileSync(join(TEST_DIR, "memory", "profiles", "users", "mehran-granfar.yaml"),
854
+ `name: Mehran Granfar\nprivilege_level: ceo\n`
855
+ );
856
+
857
+ const historyDir = join(TEST_DIR, "memory", "interactions", "slack", "dm-mehran-granfar");
858
+ mkdirSync(historyDir, { recursive: true });
859
+ const today = new Date().toISOString().split("T")[0];
860
+ writeFileSync(join(historyDir, `${today}.jsonl`), [
861
+ JSON.stringify({ ts: "1000", from: "sophie", content: "Project Aether target list updated", received_at: "2026-04-06T10:00:00Z" }),
862
+ JSON.stringify({ ts: "1001", from: "sophie", content: "GC legal strategy memo ready", received_at: "2026-04-06T10:01:00Z" }),
863
+ ].join("\n") + "\n");
864
+
865
+ const { compileContext } = await import("./context-compiler.mjs");
866
+ const result = await compileContext(
867
+ { sender: "Mehran Granfar", channel: "D099CEO", channel_id: "D099CEO" },
868
+ { priority: "critical", model: "opus", action: "respond" },
869
+ {}
870
+ );
871
+ // CEO should see everything
872
+ assert.ok(result.contextBlock.includes("Aether target list"));
873
+ assert.ok(result.contextBlock.includes("legal strategy memo"));
874
+ assert.ok(!result.contextBlock.includes("DO NOT mention"));
875
+ });
876
+ });
877
+ ```
878
+
879
+ - [ ] **Step 2: Run tests to verify they fail**
880
+
881
+ ```bash
882
+ node --test scripts/daemon/test-context-compiler.mjs
883
+ ```
884
+
885
+ Expected: FAIL — `context-compiler.mjs` does not exist.
886
+
887
+ - [ ] **Step 3: Implement the context compiler**
888
+
889
+ Create `scripts/daemon/context-compiler.mjs`:
890
+
891
+ ```javascript
892
+ #!/usr/bin/env node
893
+ /**
894
+ * context-compiler.mjs — Pre-compile session context
895
+ *
896
+ * Assembles sender profile, disclosure boundaries, conversation history,
897
+ * active session state, and sent-message registry into a single context
898
+ * block injected into every dispatched session's prompt.
899
+ *
900
+ * Key principle: sessions receive pre-filtered context as data, not
901
+ * instructions to navigate the filesystem.
902
+ */
903
+
904
+ import { readFileSync, readdirSync, existsSync } from "fs";
905
+ import { join } from "path";
906
+
907
+ const SOPHIE_AI_DIR = process.env.__TEST_SOPHIE_DIR
908
+ || join(new URL(".", import.meta.url).pathname, "../..");
909
+ const SESSION_DIR = process.env.__TEST_SESSION_DIR
910
+ || join(SOPHIE_AI_DIR, "state", "sessions");
911
+
912
+ // Approximate chars-per-token ratio for budget calculations
913
+ const CHARS_PER_TOKEN = 4;
914
+
915
+ // Token budgets by priority tier
916
+ const TOKEN_BUDGETS = {
917
+ critical: 12000,
918
+ high: 12000,
919
+ normal: 8000,
920
+ low: 4000,
921
+ };
922
+
923
+ // Fixed allocations (tokens)
924
+ const PROFILE_BUDGET = 500;
925
+ const BOUNDARIES_BUDGET = 300;
926
+ const ACTIVE_SESSIONS_BUDGET = 300;
927
+ const SENT_REGISTRY_BUDGET = 500;
928
+
929
+ /**
930
+ * Main entry point: compile all context for a session.
931
+ *
932
+ * @param {object} item - Inbox item (sender, channel, channel_id, raw_ref, etc.)
933
+ * @param {object} classResult - Classifier output (priority, model, action)
934
+ * @param {object} options - { type?: "inbox"|"backlog" }
935
+ * @returns {{ contextBlock: string, tokenBudget: number, tokenEstimate: number }}
936
+ */
937
+ export async function compileContext(item, classResult, options = {}) {
938
+ const totalBudget = TOKEN_BUDGETS[classResult.priority] || TOKEN_BUDGETS.normal;
939
+ const historyBudget = totalBudget - PROFILE_BUDGET - BOUNDARIES_BUDGET - ACTIVE_SESSIONS_BUDGET - SENT_REGISTRY_BUDGET;
940
+
941
+ const senderSlug = item.sender ? item.sender.replace(/\s+/g, "-").toLowerCase() : null;
942
+ const channelId = extractChannelId(item);
943
+
944
+ // Step 2: Load user profile
945
+ const profileSection = loadUserProfile(senderSlug, PROFILE_BUDGET);
946
+
947
+ // Step 3: Load disclosure boundaries
948
+ const { boundariesSection, forbiddenDomains } = loadDisclosureBoundaries(senderSlug, BOUNDARIES_BUDGET);
949
+
950
+ // Step 4: Load conversation history (filtered by disclosure boundaries)
951
+ const historySection = loadConversationHistory(senderSlug, channelId, historyBudget, forbiddenDomains);
952
+
953
+ // Step 5: Load active sessions
954
+ const activeSection = loadActiveSessions(ACTIVE_SESSIONS_BUDGET);
955
+
956
+ // Step 6: Load sent-message registry
957
+ const sentSection = loadSentRegistry(channelId, SENT_REGISTRY_BUDGET);
958
+
959
+ // Step 7: Assemble
960
+ const sections = [
961
+ "--- COMPILED CONTEXT (do not repeat verbatim) ---",
962
+ "",
963
+ ];
964
+
965
+ if (profileSection) {
966
+ sections.push("## Sender Profile", profileSection, "");
967
+ }
968
+
969
+ if (boundariesSection) {
970
+ sections.push("## Disclosure Boundaries", boundariesSection, "");
971
+ }
972
+
973
+ if (historySection) {
974
+ sections.push("## Conversation History (most recent last)", historySection, "");
975
+ } else {
976
+ sections.push("## Conversation History", "No prior conversation history found.", "");
977
+ }
978
+
979
+ if (activeSection) {
980
+ sections.push("## Active Sessions", activeSection, "");
981
+ }
982
+
983
+ if (sentSection) {
984
+ sections.push("## Already Sent", sentSection, "");
985
+ }
986
+
987
+ sections.push("--- END CONTEXT ---");
988
+
989
+ const contextBlock = sections.join("\n");
990
+ const tokenEstimate = Math.ceil(contextBlock.length / CHARS_PER_TOKEN);
991
+
992
+ return { contextBlock, tokenBudget: totalBudget, tokenEstimate };
993
+ }
994
+
995
+ // ── Helpers ─────────────────────────────────────────────────────────────────
996
+
997
+ function extractChannelId(item) {
998
+ if (!item) return null;
999
+ if (item.channel_id) return item.channel_id;
1000
+ if (item.raw_ref) {
1001
+ const match = item.raw_ref.match(/slack:([DC][A-Z0-9]+):/);
1002
+ if (match) return match[1];
1003
+ }
1004
+ if (item.channel && /^[DC][A-Z0-9]{8,}$/.test(item.channel)) return item.channel;
1005
+ return null;
1006
+ }
1007
+
1008
+ function loadUserProfile(senderSlug, budget) {
1009
+ if (!senderSlug) return null;
1010
+ const profilePath = join(SOPHIE_AI_DIR, "memory", "profiles", "users", `${senderSlug}.yaml`);
1011
+ try {
1012
+ const raw = readFileSync(profilePath, "utf-8");
1013
+ // Truncate to budget
1014
+ const maxChars = budget * CHARS_PER_TOKEN;
1015
+ return raw.substring(0, maxChars);
1016
+ } catch {
1017
+ return null;
1018
+ }
1019
+ }
1020
+
1021
+ /**
1022
+ * Load disclosure boundaries from the sender's profile.
1023
+ * Returns the boundaries section text and the list of forbidden domains.
1024
+ */
1025
+ function loadDisclosureBoundaries(senderSlug, budget) {
1026
+ if (!senderSlug) {
1027
+ return {
1028
+ boundariesSection: "ALLOWED domains: public only. DO NOT mention or reference any internal Adaptic information.",
1029
+ forbiddenDomains: getAllDomains(),
1030
+ };
1031
+ }
1032
+
1033
+ // Read sender profile to find excluded domains
1034
+ const profilePath = join(SOPHIE_AI_DIR, "memory", "profiles", "users", `${senderSlug}.yaml`);
1035
+ let excludedDomains = [];
1036
+ let privilegeLevel = "unknown";
1037
+
1038
+ try {
1039
+ const raw = readFileSync(profilePath, "utf-8");
1040
+
1041
+ // Parse privilege_level
1042
+ const privMatch = raw.match(/privilege_level:\s*(\S+)/);
1043
+ if (privMatch) privilegeLevel = privMatch[1];
1044
+
1045
+ // CEO sees everything — no restrictions
1046
+ if (privilegeLevel === "ceo") {
1047
+ return { boundariesSection: null, forbiddenDomains: [] };
1048
+ }
1049
+
1050
+ // Parse excluded_domains list
1051
+ const boundaryBlock = raw.match(/information_boundaries:[\s\S]*?(?=\n\S|\n$|$)/);
1052
+ if (boundaryBlock) {
1053
+ const excludedMatch = boundaryBlock[0].match(/excluded_domains:\s*\n((?:\s+-\s+.+\n?)*)/);
1054
+ if (excludedMatch) {
1055
+ excludedDomains = excludedMatch[1]
1056
+ .split("\n")
1057
+ .map((line) => line.replace(/^\s*-\s*/, "").trim())
1058
+ .filter(Boolean);
1059
+ }
1060
+ }
1061
+ } catch {
1062
+ // No profile — treat as unknown sender, restrict to public
1063
+ return {
1064
+ boundariesSection: "ALLOWED domains: public only. DO NOT mention or reference any internal Adaptic information.",
1065
+ forbiddenDomains: getAllDomains(),
1066
+ };
1067
+ }
1068
+
1069
+ if (excludedDomains.length === 0) {
1070
+ return { boundariesSection: null, forbiddenDomains: [] };
1071
+ }
1072
+
1073
+ // Build human-readable restrictions from domain descriptions
1074
+ const domainDescriptions = loadDomainDescriptions();
1075
+ const restrictions = excludedDomains
1076
+ .map((d) => `- ${domainDescriptions[d] || d}`)
1077
+ .join("\n");
1078
+
1079
+ const boundariesSection = `DO NOT mention or reference:\n${restrictions}`;
1080
+
1081
+ return { boundariesSection, forbiddenDomains: excludedDomains };
1082
+ }
1083
+
1084
+ /**
1085
+ * Load all domain names from information-barriers.yaml.
1086
+ * Used when sender is unknown — restrict everything except public.
1087
+ */
1088
+ function getAllDomains() {
1089
+ const descriptions = loadDomainDescriptions();
1090
+ return Object.keys(descriptions).filter((d) => d !== "public");
1091
+ }
1092
+
1093
+ /**
1094
+ * Load domain name → description map from information-barriers.yaml.
1095
+ */
1096
+ function loadDomainDescriptions() {
1097
+ const barriersPath = join(SOPHIE_AI_DIR, "policies", "information-barriers.yaml");
1098
+ try {
1099
+ const raw = readFileSync(barriersPath, "utf-8");
1100
+ const descriptions = {};
1101
+ const domainMatches = raw.matchAll(/^\s{2}([\w-]+):\s*\n\s+description:\s*"(.+?)"/gm);
1102
+ for (const m of domainMatches) {
1103
+ descriptions[m[1]] = m[2];
1104
+ }
1105
+ return descriptions;
1106
+ } catch {
1107
+ return {};
1108
+ }
1109
+ }
1110
+
1111
+ /**
1112
+ * Build keyword patterns for each domain, used to filter history entries.
1113
+ * Keywords are extracted from domain descriptions in information-barriers.yaml.
1114
+ */
1115
+ function buildDomainKeywords() {
1116
+ const descriptions = loadDomainDescriptions();
1117
+ const keywords = {};
1118
+ for (const [domain, desc] of Object.entries(descriptions)) {
1119
+ // Extract significant words from description (4+ chars, lowercased)
1120
+ keywords[domain] = desc
1121
+ .toLowerCase()
1122
+ .replace(/[^a-z0-9\s]/g, "")
1123
+ .split(/\s+/)
1124
+ .filter((w) => w.length >= 4 && !["with", "from", "that", "this", "than"].includes(w));
1125
+ }
1126
+ return keywords;
1127
+ }
1128
+
1129
+ /**
1130
+ * Check if a message text matches any of the forbidden domains.
1131
+ * Uses keyword heuristic — conservative (exclude if uncertain).
1132
+ */
1133
+ function matchesForbiddenDomain(text, forbiddenDomains, domainKeywords) {
1134
+ if (forbiddenDomains.length === 0) return false;
1135
+ const lower = text.toLowerCase();
1136
+ for (const domain of forbiddenDomains) {
1137
+ const keywords = domainKeywords[domain] || [];
1138
+ // If 2+ keywords from a domain match, consider it a match
1139
+ let hits = 0;
1140
+ for (const kw of keywords) {
1141
+ if (lower.includes(kw)) hits++;
1142
+ if (hits >= 2) return true;
1143
+ }
1144
+ }
1145
+ return false;
1146
+ }
1147
+
1148
+ /**
1149
+ * Load conversation history, filtered by disclosure boundaries.
1150
+ */
1151
+ function loadConversationHistory(senderSlug, channelId, budgetTokens, forbiddenDomains) {
1152
+ const maxChars = budgetTokens * CHARS_PER_TOKEN;
1153
+ const entries = [];
1154
+ const today = new Date().toISOString().split("T")[0];
1155
+
1156
+ // Candidate directories to scan
1157
+ const candidateDirs = [];
1158
+ if (channelId) candidateDirs.push(join(SOPHIE_AI_DIR, "memory", "interactions", "slack", channelId));
1159
+ if (senderSlug) {
1160
+ candidateDirs.push(join(SOPHIE_AI_DIR, "memory", "interactions", "slack", `dm-${senderSlug}`));
1161
+ candidateDirs.push(join(SOPHIE_AI_DIR, "memory", "interactions", "slack", senderSlug));
1162
+ }
1163
+
1164
+ for (const dir of candidateDirs) {
1165
+ try {
1166
+ const files = readdirSync(dir)
1167
+ .filter((f) => f.endsWith(".jsonl"))
1168
+ .sort()
1169
+ .reverse()
1170
+ .slice(0, 3); // Today + yesterday + day before
1171
+
1172
+ for (const file of files) {
1173
+ try {
1174
+ const content = readFileSync(join(dir, file), "utf-8").trim();
1175
+ if (!content) continue;
1176
+ for (const line of content.split("\n").filter(Boolean)) {
1177
+ try { entries.push(JSON.parse(line)); } catch {}
1178
+ }
1179
+ } catch {}
1180
+ }
1181
+ } catch {}
1182
+ }
1183
+
1184
+ if (entries.length === 0) return null;
1185
+
1186
+ // Deduplicate by timestamp
1187
+ const seen = new Set();
1188
+ const unique = entries.filter((e) => {
1189
+ const key = `${e.ts || e.received_at || ""}:${e.from || ""}`;
1190
+ if (seen.has(key)) return false;
1191
+ seen.add(key);
1192
+ return true;
1193
+ });
1194
+
1195
+ // Sort chronologically
1196
+ unique.sort((a, b) => {
1197
+ const tsA = a.ts || a.received_at || "";
1198
+ const tsB = b.ts || b.received_at || "";
1199
+ return tsA.localeCompare(tsB);
1200
+ });
1201
+
1202
+ // Filter by disclosure boundaries
1203
+ const domainKeywords = buildDomainKeywords();
1204
+ const filtered = unique.filter((e) => {
1205
+ const text = e.content || e.text || "";
1206
+ return !matchesForbiddenDomain(text, forbiddenDomains, domainKeywords);
1207
+ });
1208
+
1209
+ // Format and truncate to budget (most recent first for truncation)
1210
+ const lines = [];
1211
+ let totalChars = 0;
1212
+
1213
+ // Take from end (most recent) to respect budget
1214
+ for (let i = filtered.length - 1; i >= 0; i--) {
1215
+ const e = filtered[i];
1216
+ const from = e.from || e.user_name || e.sender || "unknown";
1217
+ const text = (e.content || e.text || "").substring(0, 300);
1218
+ const ts = e.received_at || e.ts || "";
1219
+ const responded = e.response_sent ? " [Responded]" : "";
1220
+ const line = `[${ts}] ${from}: ${text}${responded}`;
1221
+
1222
+ if (totalChars + line.length > maxChars) break;
1223
+ lines.unshift(line); // Prepend to maintain chronological order
1224
+ totalChars += line.length;
1225
+ }
1226
+
1227
+ return lines.length > 0 ? lines.join("\n") : null;
1228
+ }
1229
+
1230
+ /**
1231
+ * Load active session state from active.json.
1232
+ */
1233
+ function loadActiveSessions(budgetTokens) {
1234
+ const activePath = join(SESSION_DIR, "active.json");
1235
+ try {
1236
+ const raw = readFileSync(activePath, "utf-8");
1237
+ const sessions = JSON.parse(raw);
1238
+ const entries = Object.entries(sessions);
1239
+ if (entries.length === 0) return null;
1240
+
1241
+ const lines = entries.map(([id, s]) => {
1242
+ const age = Math.round((Date.now() - (s.startTime || Date.now())) / 60000);
1243
+ return `- [${id}] ${s.summary || "unknown task"} (${s.model || "sonnet"}, ${s.sender || "system"}, started ${age}m ago)`;
1244
+ });
1245
+
1246
+ return lines.join("\n");
1247
+ } catch {
1248
+ return null;
1249
+ }
1250
+ }
1251
+
1252
+ /**
1253
+ * Load recent sent-message registry entries for a specific channel.
1254
+ */
1255
+ function loadSentRegistry(channelId, budgetTokens) {
1256
+ if (!channelId) return null;
1257
+ const registryPath = join(SESSION_DIR, "sent-registry.jsonl");
1258
+ try {
1259
+ const raw = readFileSync(registryPath, "utf-8").trim();
1260
+ if (!raw) return null;
1261
+ const lines = raw.split("\n").filter(Boolean);
1262
+ const entries = lines
1263
+ .map((line) => { try { return JSON.parse(line); } catch { return null; } })
1264
+ .filter(Boolean)
1265
+ .filter((e) => e.channel === channelId);
1266
+
1267
+ if (entries.length === 0) return null;
1268
+
1269
+ // Take last 20 entries for this channel
1270
+ const recent = entries.slice(-20);
1271
+ const formatted = recent.map((e) =>
1272
+ `[${e.timestamp}] (${e.type}) ${e.textPreview || "(no preview)"}`
1273
+ );
1274
+ return formatted.join("\n");
1275
+ } catch {
1276
+ return null;
1277
+ }
1278
+ }
1279
+ ```
1280
+
1281
+ - [ ] **Step 4: Run all context compiler tests**
1282
+
1283
+ ```bash
1284
+ node --test scripts/daemon/test-context-compiler.mjs
1285
+ ```
1286
+
1287
+ Expected: All 7 tests pass.
1288
+
1289
+ - [ ] **Step 5: Commit**
1290
+
1291
+ ```bash
1292
+ git add scripts/daemon/context-compiler.mjs scripts/daemon/test-context-compiler.mjs
1293
+ git commit -m "feat: context compiler with disclosure boundary filtering
1294
+
1295
+ Pre-compiles sender profile, conversation history, active sessions,
1296
+ and sent-message registry into a single context block. Filters history
1297
+ entries by information barrier domains. Adaptive token budget: 12K for
1298
+ critical/high, 8K for normal, 4K for low priority."
1299
+ ```
1300
+
1301
+ ---
1302
+
1303
+ ## Task 4: Integrate Session Locks into Daemon
1304
+
1305
+ **Files:**
1306
+ - Modify: `scripts/daemon/sophie-daemon.mjs`
1307
+
1308
+ - [ ] **Step 1: Read the current file**
1309
+
1310
+ Read `scripts/daemon/sophie-daemon.mjs` to confirm current line numbers.
1311
+
1312
+ - [ ] **Step 2: Add session-lock imports, replace in-memory dedup**
1313
+
1314
+ Replace the dedup-related code. Make these changes to `sophie-daemon.mjs`:
1315
+
1316
+ **Add import** (after the existing imports at line 35):
1317
+
1318
+ ```javascript
1319
+ import { acquireLock, updateLock, scanStaleLocks } from "./session-lock.mjs";
1320
+ ```
1321
+
1322
+ **Remove** these lines entirely:
1323
+ - Line 44: `const DEDUP_WINDOW = 5 * 60 * 1000;`
1324
+ - Line 46: `const recentlyProcessed = new Map();`
1325
+ - Lines 71-83: The entire `isDuplicate` function and `cleanDedup` function
1326
+
1327
+ **Replace the poll loop filter** (line 101):
1328
+
1329
+ Old:
1330
+ ```javascript
1331
+ const newItems = result.items.filter((item) => !isDuplicate(item.id || item.raw_ref));
1332
+ ```
1333
+
1334
+ New:
1335
+ ```javascript
1336
+ const newItems = result.items.filter((item) => {
1337
+ const itemId = item.id || item.raw_ref || `${svc.name}-${Date.now()}`;
1338
+ const lock = acquireLock(itemId, {
1339
+ sender: item.sender || "unknown",
1340
+ channel: item.channel || item.channel_id || "unknown",
1341
+ });
1342
+ return lock.acquired;
1343
+ });
1344
+ ```
1345
+
1346
+ **Remove the `cleanDedup()` call** (line 121):
1347
+
1348
+ Old:
1349
+ ```javascript
1350
+ cleanDedup();
1351
+ ```
1352
+
1353
+ Remove this line entirely.
1354
+
1355
+ **Add stale lock scan on startup** in `main()` (after emergency stop check, around line 332):
1356
+
1357
+ ```javascript
1358
+ // Clear orphaned locks from prior crashes
1359
+ const staleCleared = scanStaleLocks();
1360
+ if (staleCleared > 0) {
1361
+ console.log(`[daemon] Cleared ${staleCleared} stale session locks`);
1362
+ }
1363
+ ```
1364
+
1365
+ - [ ] **Step 3: Update holding message to set lock flag**
1366
+
1367
+ In `processItem()`, after the holding message is sent (around line 180), add lock update:
1368
+
1369
+ Old:
1370
+ ```javascript
1371
+ const holdResult = await sendHoldingMessage(item, classResult);
1372
+ holdingText = holdResult.sent ? holdResult.holdingText : null;
1373
+ ```
1374
+
1375
+ New:
1376
+ ```javascript
1377
+ const holdResult = await sendHoldingMessage(item, classResult);
1378
+ holdingText = holdResult.sent ? holdResult.holdingText : null;
1379
+ if (holdResult.sent) {
1380
+ const lockItemId = item.id || item.raw_ref || `${service}-${Date.now()}`;
1381
+ updateLock(lockItemId, { holdingSent: true });
1382
+ }
1383
+ ```
1384
+
1385
+ - [ ] **Step 4: Verify daemon module loads without errors**
1386
+
1387
+ ```bash
1388
+ node -e "import('./scripts/daemon/sophie-daemon.mjs').catch(e => { console.error(e.message); process.exit(1); })"
1389
+ ```
1390
+
1391
+ Expected: May fail due to missing env vars, but should NOT fail on import errors. If it errors on missing poller modules or env vars, that's expected — the structural changes are correct.
1392
+
1393
+ - [ ] **Step 5: Commit**
1394
+
1395
+ ```bash
1396
+ git add scripts/daemon/sophie-daemon.mjs
1397
+ git commit -m "refactor: replace in-memory dedup with file-based session locks
1398
+
1399
+ Remove recentlyProcessed Map and isDuplicate(). Use acquireLock() for
1400
+ atomic item claim. Set holdingSent flag when holding message is sent.
1401
+ Scan and clear stale locks on daemon startup."
1402
+ ```
1403
+
1404
+ ---
1405
+
1406
+ ## Task 5: Integrate Session Locks into Dispatcher
1407
+
1408
+ **Files:**
1409
+ - Modify: `scripts/daemon/dispatcher.mjs`
1410
+
1411
+ - [ ] **Step 1: Read the current file**
1412
+
1413
+ Read `scripts/daemon/dispatcher.mjs` to confirm current line numbers.
1414
+
1415
+ - [ ] **Step 2: Add imports and fix backlogKey**
1416
+
1417
+ Add import at top (after existing imports):
1418
+
1419
+ ```javascript
1420
+ import { releaseLock, readLock } from "./session-lock.mjs";
1421
+ import { readFileSync as _readFileSync, writeFileSync as _writeFileSync, renameSync } from "fs";
1422
+ ```
1423
+
1424
+ **Stabilise `backlogKey()`** (replace lines 94-96):
1425
+
1426
+ Old:
1427
+ ```javascript
1428
+ function backlogKey(item) {
1429
+ return item.title || item.id || item.summary || JSON.stringify(item).substring(0, 100);
1430
+ }
1431
+ ```
1432
+
1433
+ New:
1434
+ ```javascript
1435
+ function backlogKey(item) {
1436
+ return item.id || item.title || item.summary || JSON.stringify(item).substring(0, 100);
1437
+ }
1438
+ ```
1439
+
1440
+ - [ ] **Step 3: Write active.json on session spawn/close**
1441
+
1442
+ Add helper functions after the existing `logSession` function:
1443
+
1444
+ ```javascript
1445
+ const ACTIVE_PATH = join(SOPHIE_AI_DIR, "state", "sessions", "active.json");
1446
+
1447
+ function writeActiveSession(sessionId, entry) {
1448
+ try {
1449
+ let active = {};
1450
+ try { active = JSON.parse(_readFileSync(ACTIVE_PATH, "utf-8")); } catch {}
1451
+ active[sessionId] = {
1452
+ sender: entry.item?.sender || null,
1453
+ channel: entry.item?.channel || entry.item?.channel_id || null,
1454
+ summary: entry.classResult?.summary || "unknown",
1455
+ model: entry.classResult?.model || "sonnet",
1456
+ startTime: Date.now(),
1457
+ source: entry.source,
1458
+ };
1459
+ const tmpPath = ACTIVE_PATH + ".tmp";
1460
+ _writeFileSync(tmpPath, JSON.stringify(active, null, 2));
1461
+ renameSync(tmpPath, ACTIVE_PATH);
1462
+ } catch (err) {
1463
+ console.warn(`[dispatcher] Failed to write active.json: ${err.message}`);
1464
+ }
1465
+ }
1466
+
1467
+ function removeActiveSession(sessionId) {
1468
+ try {
1469
+ let active = {};
1470
+ try { active = JSON.parse(_readFileSync(ACTIVE_PATH, "utf-8")); } catch {}
1471
+ delete active[sessionId];
1472
+ const tmpPath = ACTIVE_PATH + ".tmp";
1473
+ _writeFileSync(tmpPath, JSON.stringify(active, null, 2));
1474
+ renameSync(tmpPath, ACTIVE_PATH);
1475
+ } catch (err) {
1476
+ console.warn(`[dispatcher] Failed to update active.json: ${err.message}`);
1477
+ }
1478
+ }
1479
+ ```
1480
+
1481
+ **In `spawnSession()`**, after `activeSessions.set(sessionId, ...)` (around line 208), add:
1482
+
1483
+ ```javascript
1484
+ writeActiveSession(sessionId, entry);
1485
+ ```
1486
+
1487
+ **In the `proc.on("close")` handler** (around line 224), after `activeSessions.delete(sessionId)`, add:
1488
+
1489
+ ```javascript
1490
+ removeActiveSession(sessionId);
1491
+
1492
+ // Release item lock
1493
+ const itemId = item.id || item.raw_ref || item.title;
1494
+ if (itemId) releaseLock(itemId);
1495
+ ```
1496
+
1497
+ - [ ] **Step 4: Verify module loads**
1498
+
1499
+ ```bash
1500
+ node -e "import('./scripts/daemon/dispatcher.mjs').catch(e => { console.error(e.message); process.exit(1); })"
1501
+ ```
1502
+
1503
+ Expected: No import errors.
1504
+
1505
+ - [ ] **Step 5: Commit**
1506
+
1507
+ ```bash
1508
+ git add scripts/daemon/dispatcher.mjs
1509
+ git commit -m "refactor: write active.json and release locks on session lifecycle
1510
+
1511
+ Dispatcher now writes/removes entries from state/sessions/active.json
1512
+ on session spawn/close (atomic via tmp+rename). Releases item locks
1513
+ on session completion. Stabilise backlogKey to prefer item.id."
1514
+ ```
1515
+
1516
+ ---
1517
+
1518
+ ## Task 6: Integrate Context Compiler into Prompt Builder
1519
+
1520
+ **Files:**
1521
+ - Modify: `scripts/daemon/prompt-builder.mjs`
1522
+
1523
+ - [ ] **Step 1: Read the current file**
1524
+
1525
+ Read `scripts/daemon/prompt-builder.mjs` to confirm current state.
1526
+
1527
+ - [ ] **Step 2: Add context compiler import and feature flag**
1528
+
1529
+ Add import at top (after existing imports):
1530
+
1531
+ ```javascript
1532
+ import { compileContext } from "./context-compiler.mjs";
1533
+ ```
1534
+
1535
+ - [ ] **Step 3: Replace conversation history and profile instruction with compiled context**
1536
+
1537
+ In `buildPrompt()`, replace sections 4 and 6 with the compiled context.
1538
+
1539
+ **Remove the MAX_HISTORY_LINES constant** (line 12):
1540
+
1541
+ ```javascript
1542
+ // Delete: const MAX_HISTORY_LINES = 30;
1543
+ ```
1544
+
1545
+ **Remove the `loadConversationHistory()` function** (lines 148-216 approximately) entirely — this logic now lives in context-compiler.mjs.
1546
+
1547
+ **Remove the `extractChannelId()` helper** (lines 221-233) — also moved to context-compiler.
1548
+
1549
+ **Replace section 4 (conversation history) and section 6 (profile instruction)** in `buildPrompt()`.
1550
+
1551
+ Old section 4 (around lines 369-376):
1552
+ ```javascript
1553
+ // 4. Conversation history (so the session has prior context)
1554
+ if (type === "inbox" && item) {
1555
+ const history = loadConversationHistory(item);
1556
+ if (history) {
1557
+ parts.push(history);
1558
+ parts.push("");
1559
+ }
1560
+ }
1561
+ ```
1562
+
1563
+ Old section 6 (around lines 382-387):
1564
+ ```javascript
1565
+ // 6. User profile lookup (for inbox items with a sender)
1566
+ if (type === "inbox" && item.sender) {
1567
+ const profileName = item.sender.replace(/\s+/g, "-").toLowerCase();
1568
+ 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.`);
1569
+ parts.push("");
1570
+ }
1571
+ ```
1572
+
1573
+ Replace BOTH with a single compiled context block (insert after section 3, before section 5):
1574
+
1575
+ ```javascript
1576
+ // 4. Compiled context (profile + history + disclosure boundaries + active sessions + sent messages)
1577
+ if (process.env.DAEMON_CONTEXT_COMPILER === "1" && (type === "inbox" ? item : true)) {
1578
+ try {
1579
+ const { contextBlock } = await compileContext(
1580
+ item || {},
1581
+ classResult,
1582
+ { type }
1583
+ );
1584
+ parts.push(contextBlock);
1585
+ parts.push("");
1586
+ } catch (err) {
1587
+ console.error(`[prompt-builder] Context compilation failed: ${err.message}`);
1588
+ // Fall through to legacy behaviour
1589
+ if (type === "inbox" && item) {
1590
+ const history = loadConversationHistoryLegacy(item);
1591
+ if (history) { parts.push(history); parts.push(""); }
1592
+ }
1593
+ }
1594
+ } else if (type === "inbox" && item) {
1595
+ // Legacy path (feature flag off)
1596
+ const history = loadConversationHistoryLegacy(item);
1597
+ if (history) { parts.push(history); parts.push(""); }
1598
+ }
1599
+ ```
1600
+
1601
+ **Keep the old `loadConversationHistory` as `loadConversationHistoryLegacy`** — rename the function (don't delete) so the feature flag fallback works:
1602
+
1603
+ ```javascript
1604
+ // Legacy — kept for feature flag fallback, will be removed after validation
1605
+ function loadConversationHistoryLegacy(item) {
1606
+ // ... existing loadConversationHistory code unchanged ...
1607
+ }
1608
+ ```
1609
+
1610
+ Also keep `extractChannelId` renamed as `extractChannelIdLegacy` used by the legacy function. Or since context-compiler has its own, just keep the legacy version local.
1611
+
1612
+ **Remove the old section 6 (profile instruction)** — it's now inside the compiled context block. In the legacy path, add it back:
1613
+
1614
+ After the legacy history loading block, add:
1615
+
1616
+ ```javascript
1617
+ // Legacy profile instruction (only when feature flag is off)
1618
+ if (process.env.DAEMON_CONTEXT_COMPILER !== "1" && type === "inbox" && item && item.sender) {
1619
+ const profileName = item.sender.replace(/\s+/g, "-").toLowerCase();
1620
+ 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.`);
1621
+ parts.push("");
1622
+ }
1623
+ ```
1624
+
1625
+ - [ ] **Step 4: Verify module loads**
1626
+
1627
+ ```bash
1628
+ node -e "import('./scripts/daemon/prompt-builder.mjs').catch(e => { console.error(e.message); process.exit(1); })"
1629
+ ```
1630
+
1631
+ Expected: No import errors.
1632
+
1633
+ - [ ] **Step 5: Commit**
1634
+
1635
+ ```bash
1636
+ git add scripts/daemon/prompt-builder.mjs
1637
+ git commit -m "feat: integrate context compiler into prompt builder
1638
+
1639
+ Replace self-discovery instructions with pre-compiled context block.
1640
+ Feature-flagged via DAEMON_CONTEXT_COMPILER=1 — legacy path preserved
1641
+ as fallback. Sessions now receive rich pre-filtered context including
1642
+ profile, disclosure boundaries, history, active sessions, and sent
1643
+ messages directly in the prompt."
1644
+ ```
1645
+
1646
+ ---
1647
+
1648
+ ## Task 7: Integrate Sent-Registry into Responder
1649
+
1650
+ **Files:**
1651
+ - Modify: `scripts/daemon/responder.mjs`
1652
+
1653
+ - [ ] **Step 1: Read the current file**
1654
+
1655
+ Read `scripts/daemon/responder.mjs` to confirm current state.
1656
+
1657
+ - [ ] **Step 2: Add session-lock imports**
1658
+
1659
+ Add after existing imports:
1660
+
1661
+ ```javascript
1662
+ import { checkRecentlySent, registerSent } from "./session-lock.mjs";
1663
+ ```
1664
+
1665
+ - [ ] **Step 3: Add dedup check and registration to `sendQuickResponse`**
1666
+
1667
+ In `sendQuickResponse()`, after the validation check and before `sendSlackMessage()`:
1668
+
1669
+ Find the section (around line 396):
1670
+ ```javascript
1671
+ if (item.service === "slack") {
1672
+ const channel = resolveSlackChannel(item);
1673
+ if (channel) {
1674
+ ```
1675
+
1676
+ Replace with:
1677
+ ```javascript
1678
+ if (item.service === "slack") {
1679
+ const channel = resolveSlackChannel(item);
1680
+ if (channel) {
1681
+ // Check sent-message registry for duplicates
1682
+ const dupCheck = checkRecentlySent(channel, item.thread_id || null, "quick_reply");
1683
+ if (!dupCheck.allowed) {
1684
+ console.log(`[responder] Quick reply blocked by sent-registry: ${dupCheck.reason}`);
1685
+ logResponse({ type: "quick_response_dedup_blocked", sender: item.sender, reason: dupCheck.reason });
1686
+ return { sent: false, text: null, blocked: true, reason: dupCheck.reason };
1687
+ }
1688
+ ```
1689
+
1690
+ After the successful `sendSlackMessage()` call and before `sendResult = { sent: true, ... }`, add:
1691
+
1692
+ ```javascript
1693
+ registerSent(channel, item.thread_id || null, "quick_reply", "quick-responder", text.substring(0, 100));
1694
+ ```
1695
+
1696
+ - [ ] **Step 4: Add dedup check and registration to `sendHoldingMessage`**
1697
+
1698
+ In `sendHoldingMessage()`, same pattern. After `const channel = resolveSlackChannel(item);`:
1699
+
1700
+ ```javascript
1701
+ if (channel) {
1702
+ // Check sent-message registry for duplicates
1703
+ const dupCheck = checkRecentlySent(channel, item.thread_id || null, "holding");
1704
+ if (!dupCheck.allowed) {
1705
+ console.log(`[responder] Holding message blocked by sent-registry: ${dupCheck.reason}`);
1706
+ logResponse({ type: "holding_dedup_blocked", sender: item.sender, reason: dupCheck.reason });
1707
+ return { sent: false, holdingText: null, blocked: true, reason: dupCheck.reason };
1708
+ }
1709
+ ```
1710
+
1711
+ After successful `sendSlackMessage()`:
1712
+
1713
+ ```javascript
1714
+ registerSent(channel, item.thread_id || null, "holding", "quick-responder", text.substring(0, 100));
1715
+ ```
1716
+
1717
+ - [ ] **Step 5: Verify module loads**
1718
+
1719
+ ```bash
1720
+ node -e "import('./scripts/daemon/responder.mjs').catch(e => { console.error(e.message); process.exit(1); })"
1721
+ ```
1722
+
1723
+ Expected: No import errors.
1724
+
1725
+ - [ ] **Step 6: Commit**
1726
+
1727
+ ```bash
1728
+ git add scripts/daemon/responder.mjs
1729
+ git commit -m "feat: add sent-message registry checks to responder
1730
+
1731
+ Quick replies and holding messages now check the sent-registry before
1732
+ sending and register after successful send. Prevents duplicate messages
1733
+ even when upstream dedup fails."
1734
+ ```
1735
+
1736
+ ---
1737
+
1738
+ ## Task 8: Integration Test
1739
+
1740
+ **Files:**
1741
+ - Create: `scripts/daemon/test-integration.mjs`
1742
+
1743
+ - [ ] **Step 1: Write integration tests**
1744
+
1745
+ Create `scripts/daemon/test-integration.mjs`:
1746
+
1747
+ ```javascript
1748
+ import { describe, it, beforeEach, afterEach } from "node:test";
1749
+ import assert from "node:assert/strict";
1750
+ import { mkdirSync, rmSync, writeFileSync, readFileSync, existsSync } from "fs";
1751
+ import { join } from "path";
1752
+
1753
+ const TEST_DIR = join(import.meta.dirname, "../../.test-integration");
1754
+ process.env.__TEST_SESSION_DIR = join(TEST_DIR, "state", "sessions");
1755
+ process.env.__TEST_SOPHIE_DIR = TEST_DIR;
1756
+
1757
+ function setupTestEnv() {
1758
+ mkdirSync(join(TEST_DIR, "state", "sessions", "locks"), { recursive: true });
1759
+ mkdirSync(join(TEST_DIR, "memory", "profiles", "users"), { recursive: true });
1760
+ mkdirSync(join(TEST_DIR, "memory", "interactions", "slack", "dm-test-user"), { recursive: true });
1761
+ mkdirSync(join(TEST_DIR, "policies"), { recursive: true });
1762
+ writeFileSync(join(TEST_DIR, "state", "sessions", "active.json"), "{}");
1763
+
1764
+ writeFileSync(join(TEST_DIR, "policies", "information-barriers.yaml"), `domains:
1765
+ adaptic-internal-legal:
1766
+ description: "Adaptic legal strategy, GC advice, privilege-protected"
1767
+ default_sensitivity: high
1768
+ rollup-strategy:
1769
+ description: "Project Aether M&A targets, deal terms, pipeline"
1770
+ default_sensitivity: critical
1771
+ public:
1772
+ description: "Published information, press, website"
1773
+ default_sensitivity: none
1774
+ `);
1775
+ }
1776
+
1777
+ function cleanTestEnv() {
1778
+ rmSync(TEST_DIR, { recursive: true, force: true });
1779
+ }
1780
+
1781
+ describe("Integration: lock → compile → prompt pipeline", () => {
1782
+ beforeEach(setupTestEnv);
1783
+ afterEach(cleanTestEnv);
1784
+
1785
+ it("acquireLock prevents duplicate processing of same item", async () => {
1786
+ const { acquireLock, releaseLock } = await import("./session-lock.mjs");
1787
+
1788
+ const first = acquireLock("slack-C001-12345", { sender: "Test", channel: "C001" });
1789
+ assert.equal(first.acquired, true);
1790
+
1791
+ const second = acquireLock("slack-C001-12345", { sender: "Test", channel: "C001" });
1792
+ assert.equal(second.acquired, false);
1793
+
1794
+ releaseLock("slack-C001-12345");
1795
+ });
1796
+
1797
+ it("sent-registry prevents duplicate sends across quick-reply and holding paths", async () => {
1798
+ const { registerSent, checkRecentlySent } = await import("./session-lock.mjs");
1799
+
1800
+ // Holding message sent
1801
+ registerSent("C001", "12345.6789", "holding", "s-1", "Looking into this now");
1802
+
1803
+ // Quick reply to same channel+thread should still be allowed (different type)
1804
+ const quickCheck = checkRecentlySent("C001", "12345.6789", "quick_reply");
1805
+ assert.equal(quickCheck.allowed, true);
1806
+
1807
+ // Another holding to same channel+thread should be blocked
1808
+ const holdCheck = checkRecentlySent("C001", "12345.6789", "holding");
1809
+ assert.equal(holdCheck.allowed, false);
1810
+ });
1811
+
1812
+ it("context compiler respects disclosure boundaries end-to-end", async () => {
1813
+ // Create a profile with restrictions
1814
+ writeFileSync(
1815
+ join(TEST_DIR, "memory", "profiles", "users", "test-user.yaml"),
1816
+ `name: Test User\ninformation_boundaries:\n excluded_domains:\n - rollup-strategy\n`
1817
+ );
1818
+
1819
+ // Create history with mixed content
1820
+ const today = new Date().toISOString().split("T")[0];
1821
+ writeFileSync(
1822
+ join(TEST_DIR, "memory", "interactions", "slack", "dm-test-user", `${today}.jsonl`),
1823
+ [
1824
+ JSON.stringify({ ts: "100", from: "test-user", content: "What is the status?", received_at: "2026-04-06T10:00:00Z" }),
1825
+ JSON.stringify({ ts: "101", from: "sophie", content: "Project Aether deal terms are looking good", received_at: "2026-04-06T10:01:00Z" }),
1826
+ JSON.stringify({ ts: "102", from: "sophie", content: "The public website update is live", received_at: "2026-04-06T10:02:00Z" }),
1827
+ ].join("\n") + "\n"
1828
+ );
1829
+
1830
+ const { compileContext } = await import("./context-compiler.mjs");
1831
+ const { contextBlock } = await compileContext(
1832
+ { sender: "Test User", channel: "D001", channel_id: "D001" },
1833
+ { priority: "normal", model: "sonnet", action: "respond" },
1834
+ {}
1835
+ );
1836
+
1837
+ // Public content should be included
1838
+ assert.ok(contextBlock.includes("website update is live"));
1839
+ // Rollup content should be filtered
1840
+ assert.ok(!contextBlock.includes("Aether deal terms"));
1841
+ // Should have disclosure boundaries
1842
+ assert.ok(contextBlock.includes("DO NOT mention"));
1843
+ });
1844
+
1845
+ it("active sessions are visible in compiled context", async () => {
1846
+ writeFileSync(
1847
+ join(TEST_DIR, "state", "sessions", "active.json"),
1848
+ JSON.stringify({
1849
+ "s-999": {
1850
+ sender: "Alice",
1851
+ channel: "C002",
1852
+ summary: "Preparing board pack",
1853
+ model: "opus",
1854
+ startTime: Date.now() - 120000,
1855
+ },
1856
+ })
1857
+ );
1858
+
1859
+ const { compileContext } = await import("./context-compiler.mjs");
1860
+ const { contextBlock } = await compileContext(
1861
+ { sender: "Test", channel: "C001" },
1862
+ { priority: "normal", model: "sonnet", action: "respond" },
1863
+ {}
1864
+ );
1865
+
1866
+ assert.ok(contextBlock.includes("Preparing board pack"));
1867
+ assert.ok(contextBlock.includes("Active Sessions"));
1868
+ });
1869
+ });
1870
+ ```
1871
+
1872
+ - [ ] **Step 2: Run integration tests**
1873
+
1874
+ ```bash
1875
+ node --test scripts/daemon/test-integration.mjs
1876
+ ```
1877
+
1878
+ Expected: All 4 tests pass.
1879
+
1880
+ - [ ] **Step 3: Run all test files together**
1881
+
1882
+ ```bash
1883
+ node --test scripts/daemon/test-session-lock.mjs scripts/daemon/test-context-compiler.mjs scripts/daemon/test-integration.mjs
1884
+ ```
1885
+
1886
+ Expected: All tests pass.
1887
+
1888
+ - [ ] **Step 4: Commit**
1889
+
1890
+ ```bash
1891
+ git add scripts/daemon/test-integration.mjs
1892
+ git commit -m "test: integration tests for session lock + context compiler pipeline
1893
+
1894
+ Covers: duplicate lock prevention, sent-registry cross-path dedup,
1895
+ disclosure boundary filtering end-to-end, active session visibility."
1896
+ ```
1897
+
1898
+ ---
1899
+
1900
+ ## Task 9: Feature Flag and .env Update
1901
+
1902
+ **Files:**
1903
+ - Modify: `.env` (or `.env.example` if it exists)
1904
+ - Modify: `config/environment.yaml` (if feature flags live there)
1905
+
1906
+ - [ ] **Step 1: Add feature flag to .env**
1907
+
1908
+ Add the following line to `.env`:
1909
+
1910
+ ```
1911
+ DAEMON_CONTEXT_COMPILER=1
1912
+ ```
1913
+
1914
+ - [ ] **Step 2: Add npm test script**
1915
+
1916
+ Add to `package.json` scripts:
1917
+
1918
+ ```json
1919
+ "test:daemon": "node --test scripts/daemon/test-session-lock.mjs scripts/daemon/test-context-compiler.mjs scripts/daemon/test-integration.mjs"
1920
+ ```
1921
+
1922
+ - [ ] **Step 3: Verify all tests pass with feature flag**
1923
+
1924
+ ```bash
1925
+ DAEMON_CONTEXT_COMPILER=1 npm run test:daemon
1926
+ ```
1927
+
1928
+ Expected: All tests pass.
1929
+
1930
+ - [ ] **Step 4: Commit**
1931
+
1932
+ ```bash
1933
+ git add package.json .env
1934
+ git commit -m "feat: enable context compiler feature flag and add test script
1935
+
1936
+ Set DAEMON_CONTEXT_COMPILER=1 to activate the new context compilation
1937
+ pipeline. Add npm run test:daemon for running all daemon tests."
1938
+ ```
1939
+
1940
+ ---
1941
+
1942
+ ## Task 10: Add .gitignore Entries and Final Cleanup
1943
+
1944
+ **Files:**
1945
+ - Modify: `.gitignore`
1946
+ - Create: `state/sessions/locks/.gitkeep`
1947
+
1948
+ - [ ] **Step 1: Verify .gitignore has the right entries**
1949
+
1950
+ Ensure `.gitignore` contains:
1951
+
1952
+ ```
1953
+ # Session state (ephemeral)
1954
+ state/sessions/locks/*.lock
1955
+ state/sessions/sent-registry.jsonl
1956
+ state/sessions/active.json.tmp
1957
+
1958
+ # Test directories
1959
+ .test-session-locks/
1960
+ .test-context-compiler/
1961
+ .test-integration/
1962
+ ```
1963
+
1964
+ - [ ] **Step 2: Run full test suite one final time**
1965
+
1966
+ ```bash
1967
+ npm run test:daemon
1968
+ ```
1969
+
1970
+ Expected: All tests pass.
1971
+
1972
+ - [ ] **Step 3: Commit**
1973
+
1974
+ ```bash
1975
+ git add .gitignore state/sessions/locks/.gitkeep
1976
+ git commit -m "chore: gitignore ephemeral session state and test directories"
1977
+ ```
1978
+
1979
+ ---
1980
+
1981
+ ## Summary
1982
+
1983
+ | Task | What it builds | Key files |
1984
+ |------|---------------|-----------|
1985
+ | 1 | Directory structure | `state/sessions/` |
1986
+ | 2 | Session lock module + tests | `session-lock.mjs`, `test-session-lock.mjs` |
1987
+ | 3 | Context compiler + tests | `context-compiler.mjs`, `test-context-compiler.mjs` |
1988
+ | 4 | Daemon lock integration | `sophie-daemon.mjs` |
1989
+ | 5 | Dispatcher active.json + lock release | `dispatcher.mjs` |
1990
+ | 6 | Prompt builder context injection | `prompt-builder.mjs` |
1991
+ | 7 | Responder sent-registry integration | `responder.mjs` |
1992
+ | 8 | Integration tests | `test-integration.mjs` |
1993
+ | 9 | Feature flag + test script | `.env`, `package.json` |
1994
+ | 10 | Gitignore + cleanup | `.gitignore` |