crewly 1.20.193 → 1.20.195

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 (407) hide show
  1. package/config/roles/auditor/prompt.md +2 -2
  2. package/config/roles/orchestrator/fragments/communication.md +1 -1
  3. package/config/roles/orchestrator/prompt.md +15 -505
  4. package/config/skills/agent/computer-use/SKILL.md +6 -0
  5. package/config/skills/agent/core/experiment-card/SKILL.md +101 -0
  6. package/config/skills/agent/core/experiment-card/execute.sh +149 -0
  7. package/config/skills/agent/core/experiment-card/execute.test.sh +97 -0
  8. package/config/skills/agent/desktop-app-control/execute.sh +5 -2
  9. package/config/skills/orchestrator/send-message/execute.sh +19 -2
  10. package/config/skills/orchestrator/send-message/execute.test.sh +80 -0
  11. package/config/skills/registry.json +7 -5
  12. package/config/skills/team-leader/signal-digest/SKILL.md +110 -0
  13. package/config/skills/team-leader/signal-digest/execute.sh +173 -0
  14. package/config/skills/team-leader/signal-digest/execute.test.sh +123 -0
  15. package/config/skills/team-leader/signal-digest/signal-digest.config.example.json +23 -0
  16. package/config/skills/team-leader/signal-digest/signal_digest.py +572 -0
  17. package/config/skills/team-leader/signal-digest/signal_digest.test.py +319 -0
  18. package/dist/backend/backend/src/constants.d.ts +191 -1
  19. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  20. package/dist/backend/backend/src/constants.js +194 -1
  21. package/dist/backend/backend/src/constants.js.map +1 -1
  22. package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
  23. package/dist/backend/backend/src/controllers/chat/chat.controller.js +6 -0
  24. package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
  25. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts.map +1 -1
  26. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js +3 -1
  27. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js.map +1 -1
  28. package/dist/backend/backend/src/controllers/experiments/experiments.routes.d.ts +27 -0
  29. package/dist/backend/backend/src/controllers/experiments/experiments.routes.d.ts.map +1 -0
  30. package/dist/backend/backend/src/controllers/experiments/experiments.routes.js +96 -0
  31. package/dist/backend/backend/src/controllers/experiments/experiments.routes.js.map +1 -0
  32. package/dist/backend/backend/src/controllers/marketplace/marketplace.routes.d.ts +1 -0
  33. package/dist/backend/backend/src/controllers/marketplace/marketplace.routes.d.ts.map +1 -1
  34. package/dist/backend/backend/src/controllers/marketplace/marketplace.routes.js +1 -0
  35. package/dist/backend/backend/src/controllers/marketplace/marketplace.routes.js.map +1 -1
  36. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts.map +1 -1
  37. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js +5 -2
  38. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js.map +1 -1
  39. package/dist/backend/backend/src/controllers/request/request.controller.d.ts.map +1 -1
  40. package/dist/backend/backend/src/controllers/request/request.controller.js +5 -1
  41. package/dist/backend/backend/src/controllers/request/request.controller.js.map +1 -1
  42. package/dist/backend/backend/src/controllers/settings/settings.controller.d.ts.map +1 -1
  43. package/dist/backend/backend/src/controllers/settings/settings.controller.js +18 -2
  44. package/dist/backend/backend/src/controllers/settings/settings.controller.js.map +1 -1
  45. package/dist/backend/backend/src/controllers/signal-digest/signal-digest.controller.d.ts +26 -0
  46. package/dist/backend/backend/src/controllers/signal-digest/signal-digest.controller.d.ts.map +1 -0
  47. package/dist/backend/backend/src/controllers/signal-digest/signal-digest.controller.js +100 -0
  48. package/dist/backend/backend/src/controllers/signal-digest/signal-digest.controller.js.map +1 -0
  49. package/dist/backend/backend/src/controllers/trace/trace.controller.d.ts +47 -0
  50. package/dist/backend/backend/src/controllers/trace/trace.controller.d.ts.map +1 -0
  51. package/dist/backend/backend/src/controllers/trace/trace.controller.js +169 -0
  52. package/dist/backend/backend/src/controllers/trace/trace.controller.js.map +1 -0
  53. package/dist/backend/backend/src/index.d.ts.map +1 -1
  54. package/dist/backend/backend/src/index.js +66 -20
  55. package/dist/backend/backend/src/index.js.map +1 -1
  56. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  57. package/dist/backend/backend/src/routes/api.routes.js +14 -0
  58. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  59. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  60. package/dist/backend/backend/src/services/agent/agent-registration.service.js +6 -1
  61. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  62. package/dist/backend/backend/src/services/agent/auditor-scheduler.service.d.ts.map +1 -1
  63. package/dist/backend/backend/src/services/agent/auditor-scheduler.service.js +3 -0
  64. package/dist/backend/backend/src/services/agent/auditor-scheduler.service.js.map +1 -1
  65. package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.d.ts +1 -1
  66. package/dist/backend/backend/src/services/agent/oauth-relogin-monitor.service.d.ts.map +1 -1
  67. package/dist/backend/backend/src/services/agent/runtime-session-recovery.d.ts +4 -2
  68. package/dist/backend/backend/src/services/agent/runtime-session-recovery.d.ts.map +1 -1
  69. package/dist/backend/backend/src/services/agent/runtime-session-recovery.js +22 -6
  70. package/dist/backend/backend/src/services/agent/runtime-session-recovery.js.map +1 -1
  71. package/dist/backend/backend/src/services/ai/prompt-modules/communication.module.js +2 -2
  72. package/dist/backend/backend/src/services/ai/prompt-modules/communication.module.js.map +1 -1
  73. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  74. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -0
  75. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  76. package/dist/backend/backend/src/services/backup/backup-archive.service.d.ts +6 -0
  77. package/dist/backend/backend/src/services/backup/backup-archive.service.d.ts.map +1 -1
  78. package/dist/backend/backend/src/services/backup/backup-archive.service.js +7 -1
  79. package/dist/backend/backend/src/services/backup/backup-archive.service.js.map +1 -1
  80. package/dist/backend/backend/src/services/boot/boot-announce.service.d.ts +1 -1
  81. package/dist/backend/backend/src/services/boot/boot-announce.service.d.ts.map +1 -1
  82. package/dist/backend/backend/src/services/canva/canva-token.service.d.ts.map +1 -1
  83. package/dist/backend/backend/src/services/canva/canva-token.service.js +5 -0
  84. package/dist/backend/backend/src/services/canva/canva-token.service.js.map +1 -1
  85. package/dist/backend/backend/src/services/chat/chat.service.d.ts +78 -2
  86. package/dist/backend/backend/src/services/chat/chat.service.d.ts.map +1 -1
  87. package/dist/backend/backend/src/services/chat/chat.service.js +210 -26
  88. package/dist/backend/backend/src/services/chat/chat.service.js.map +1 -1
  89. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +14 -3
  90. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
  91. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +28 -4
  92. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
  93. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts +4 -3
  94. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
  95. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js +7 -4
  96. package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
  97. package/dist/backend/backend/src/services/decisions/decision-store.d.ts.map +1 -1
  98. package/dist/backend/backend/src/services/decisions/decision-store.js +5 -1
  99. package/dist/backend/backend/src/services/decisions/decision-store.js.map +1 -1
  100. package/dist/backend/backend/src/services/decisions/decision.wiring.d.ts +10 -1
  101. package/dist/backend/backend/src/services/decisions/decision.wiring.d.ts.map +1 -1
  102. package/dist/backend/backend/src/services/decisions/decision.wiring.js +20 -9
  103. package/dist/backend/backend/src/services/decisions/decision.wiring.js.map +1 -1
  104. package/dist/backend/backend/src/services/experiments/experiment-verdict.d.ts +88 -0
  105. package/dist/backend/backend/src/services/experiments/experiment-verdict.d.ts.map +1 -0
  106. package/dist/backend/backend/src/services/experiments/experiment-verdict.js +226 -0
  107. package/dist/backend/backend/src/services/experiments/experiment-verdict.js.map +1 -0
  108. package/dist/backend/backend/src/services/experiments/experiment.service.d.ts +350 -0
  109. package/dist/backend/backend/src/services/experiments/experiment.service.d.ts.map +1 -0
  110. package/dist/backend/backend/src/services/experiments/experiment.service.js +908 -0
  111. package/dist/backend/backend/src/services/experiments/experiment.service.js.map +1 -0
  112. package/dist/backend/backend/src/services/experiments/experiment.wiring.d.ts +88 -0
  113. package/dist/backend/backend/src/services/experiments/experiment.wiring.d.ts.map +1 -0
  114. package/dist/backend/backend/src/services/experiments/experiment.wiring.js +162 -0
  115. package/dist/backend/backend/src/services/experiments/experiment.wiring.js.map +1 -0
  116. package/dist/backend/backend/src/services/experiments/seo-ops-metric.fetcher.d.ts +67 -0
  117. package/dist/backend/backend/src/services/experiments/seo-ops-metric.fetcher.d.ts.map +1 -0
  118. package/dist/backend/backend/src/services/experiments/seo-ops-metric.fetcher.js +133 -0
  119. package/dist/backend/backend/src/services/experiments/seo-ops-metric.fetcher.js.map +1 -0
  120. package/dist/backend/backend/src/services/google/google-workspace-token.service.d.ts.map +1 -1
  121. package/dist/backend/backend/src/services/google/google-workspace-token.service.js +5 -0
  122. package/dist/backend/backend/src/services/google/google-workspace-token.service.js.map +1 -1
  123. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.service.d.ts.map +1 -1
  124. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.service.js +7 -0
  125. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.service.js.map +1 -1
  126. package/dist/backend/backend/src/services/messaging/queue-processor.service.d.ts +11 -3
  127. package/dist/backend/backend/src/services/messaging/queue-processor.service.d.ts.map +1 -1
  128. package/dist/backend/backend/src/services/messaging/queue-processor.service.js +24 -7
  129. package/dist/backend/backend/src/services/messaging/queue-processor.service.js.map +1 -1
  130. package/dist/backend/backend/src/services/microsoft/microsoft-token.service.d.ts.map +1 -1
  131. package/dist/backend/backend/src/services/microsoft/microsoft-token.service.js +5 -0
  132. package/dist/backend/backend/src/services/microsoft/microsoft-token.service.js.map +1 -1
  133. package/dist/backend/backend/src/services/monitoring/token-usage.service.d.ts +7 -0
  134. package/dist/backend/backend/src/services/monitoring/token-usage.service.d.ts.map +1 -1
  135. package/dist/backend/backend/src/services/monitoring/token-usage.service.js +4 -0
  136. package/dist/backend/backend/src/services/monitoring/token-usage.service.js.map +1 -1
  137. package/dist/backend/backend/src/services/orc/orc-status-router.service.d.ts.map +1 -1
  138. package/dist/backend/backend/src/services/orc/orc-status-router.service.js +12 -3
  139. package/dist/backend/backend/src/services/orc/orc-status-router.service.js.map +1 -1
  140. package/dist/backend/backend/src/services/orc/reply-destination.wiring.d.ts.map +1 -1
  141. package/dist/backend/backend/src/services/orc/reply-destination.wiring.js +16 -0
  142. package/dist/backend/backend/src/services/orc/reply-destination.wiring.js.map +1 -1
  143. package/dist/backend/backend/src/services/people/acting-for.service.d.ts +42 -5
  144. package/dist/backend/backend/src/services/people/acting-for.service.d.ts.map +1 -1
  145. package/dist/backend/backend/src/services/people/acting-for.service.js +80 -5
  146. package/dist/backend/backend/src/services/people/acting-for.service.js.map +1 -1
  147. package/dist/backend/backend/src/services/people/grant-sharing.d.ts +5 -4
  148. package/dist/backend/backend/src/services/people/grant-sharing.d.ts.map +1 -1
  149. package/dist/backend/backend/src/services/people/grant-sharing.js +12 -4
  150. package/dist/backend/backend/src/services/people/grant-sharing.js.map +1 -1
  151. package/dist/backend/backend/src/services/people/people-directory.service.d.ts +71 -13
  152. package/dist/backend/backend/src/services/people/people-directory.service.d.ts.map +1 -1
  153. package/dist/backend/backend/src/services/people/people-directory.service.js +164 -27
  154. package/dist/backend/backend/src/services/people/people-directory.service.js.map +1 -1
  155. package/dist/backend/backend/src/services/project-tickets/project-ticket.service.d.ts.map +1 -1
  156. package/dist/backend/backend/src/services/project-tickets/project-ticket.service.js +2 -0
  157. package/dist/backend/backend/src/services/project-tickets/project-ticket.service.js.map +1 -1
  158. package/dist/backend/backend/src/services/reconciler/reconciler.service.d.ts.map +1 -1
  159. package/dist/backend/backend/src/services/reconciler/reconciler.service.js +15 -0
  160. package/dist/backend/backend/src/services/reconciler/reconciler.service.js.map +1 -1
  161. package/dist/backend/backend/src/services/session/session-handoff.service.d.ts.map +1 -1
  162. package/dist/backend/backend/src/services/session/session-handoff.service.js +8 -5
  163. package/dist/backend/backend/src/services/session/session-handoff.service.js.map +1 -1
  164. package/dist/backend/backend/src/services/signal-digest/signal-digest-card.d.ts +75 -0
  165. package/dist/backend/backend/src/services/signal-digest/signal-digest-card.d.ts.map +1 -0
  166. package/dist/backend/backend/src/services/signal-digest/signal-digest-card.js +187 -0
  167. package/dist/backend/backend/src/services/signal-digest/signal-digest-card.js.map +1 -0
  168. package/dist/backend/backend/src/services/signal-digest/signal-digest-contract.d.ts +86 -0
  169. package/dist/backend/backend/src/services/signal-digest/signal-digest-contract.d.ts.map +1 -0
  170. package/dist/backend/backend/src/services/signal-digest/signal-digest-contract.js +307 -0
  171. package/dist/backend/backend/src/services/signal-digest/signal-digest-contract.js.map +1 -0
  172. package/dist/backend/backend/src/services/signal-digest/signal-digest-store.d.ts +85 -0
  173. package/dist/backend/backend/src/services/signal-digest/signal-digest-store.d.ts.map +1 -0
  174. package/dist/backend/backend/src/services/signal-digest/signal-digest-store.js +185 -0
  175. package/dist/backend/backend/src/services/signal-digest/signal-digest-store.js.map +1 -0
  176. package/dist/backend/backend/src/services/signal-digest/signal-digest.service.d.ts +272 -0
  177. package/dist/backend/backend/src/services/signal-digest/signal-digest.service.d.ts.map +1 -0
  178. package/dist/backend/backend/src/services/signal-digest/signal-digest.service.js +570 -0
  179. package/dist/backend/backend/src/services/signal-digest/signal-digest.service.js.map +1 -0
  180. package/dist/backend/backend/src/services/signal-digest/signal-digest.wiring.d.ts +51 -0
  181. package/dist/backend/backend/src/services/signal-digest/signal-digest.wiring.d.ts.map +1 -0
  182. package/dist/backend/backend/src/services/signal-digest/signal-digest.wiring.js +116 -0
  183. package/dist/backend/backend/src/services/signal-digest/signal-digest.wiring.js.map +1 -0
  184. package/dist/backend/backend/src/services/slack/notify-reconciliation.service.d.ts +5 -1
  185. package/dist/backend/backend/src/services/slack/notify-reconciliation.service.d.ts.map +1 -1
  186. package/dist/backend/backend/src/services/slack/notify-reconciliation.service.js +6 -2
  187. package/dist/backend/backend/src/services/slack/notify-reconciliation.service.js.map +1 -1
  188. package/dist/backend/backend/src/services/slack/slack-bot-ids.d.ts +33 -0
  189. package/dist/backend/backend/src/services/slack/slack-bot-ids.d.ts.map +1 -0
  190. package/dist/backend/backend/src/services/slack/slack-bot-ids.js +56 -0
  191. package/dist/backend/backend/src/services/slack/slack-bot-ids.js.map +1 -0
  192. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +14 -1
  193. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  194. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +30 -10
  195. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  196. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.d.ts +1 -1
  197. package/dist/backend/backend/src/services/slack/slack-relogin-dm.service.d.ts.map +1 -1
  198. package/dist/backend/backend/src/services/slack/slack.service.d.ts +31 -1
  199. package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
  200. package/dist/backend/backend/src/services/slack/slack.service.js +52 -7
  201. package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
  202. package/dist/backend/backend/src/services/system/auto-update.service.d.ts +2 -2
  203. package/dist/backend/backend/src/services/system/auto-update.service.d.ts.map +1 -1
  204. package/dist/backend/backend/src/services/task-pool/pool-storage.d.ts.map +1 -1
  205. package/dist/backend/backend/src/services/task-pool/pool-storage.js +4 -0
  206. package/dist/backend/backend/src/services/task-pool/pool-storage.js.map +1 -1
  207. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  208. package/dist/backend/backend/src/services/task-pool/task-pool.service.js +2 -0
  209. package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  210. package/dist/backend/backend/src/services/trace/trace-context.service.d.ts +195 -0
  211. package/dist/backend/backend/src/services/trace/trace-context.service.d.ts.map +1 -0
  212. package/dist/backend/backend/src/services/trace/trace-context.service.js +432 -0
  213. package/dist/backend/backend/src/services/trace/trace-context.service.js.map +1 -0
  214. package/dist/backend/backend/src/services/trace/trace-http.middleware.d.ts +42 -0
  215. package/dist/backend/backend/src/services/trace/trace-http.middleware.d.ts.map +1 -0
  216. package/dist/backend/backend/src/services/trace/trace-http.middleware.js +146 -0
  217. package/dist/backend/backend/src/services/trace/trace-http.middleware.js.map +1 -0
  218. package/dist/backend/backend/src/services/trace/trace-markers.d.ts +93 -0
  219. package/dist/backend/backend/src/services/trace/trace-markers.d.ts.map +1 -0
  220. package/dist/backend/backend/src/services/trace/trace-markers.js +162 -0
  221. package/dist/backend/backend/src/services/trace/trace-markers.js.map +1 -0
  222. package/dist/backend/backend/src/services/trace/trace-recorder.d.ts +290 -0
  223. package/dist/backend/backend/src/services/trace/trace-recorder.d.ts.map +1 -0
  224. package/dist/backend/backend/src/services/trace/trace-recorder.js +669 -0
  225. package/dist/backend/backend/src/services/trace/trace-recorder.js.map +1 -0
  226. package/dist/backend/backend/src/services/trace/trace-store.d.ts +269 -0
  227. package/dist/backend/backend/src/services/trace/trace-store.d.ts.map +1 -0
  228. package/dist/backend/backend/src/services/trace/trace-store.js +602 -0
  229. package/dist/backend/backend/src/services/trace/trace-store.js.map +1 -0
  230. package/dist/backend/backend/src/services/trace/trace.types.d.ts +118 -0
  231. package/dist/backend/backend/src/services/trace/trace.types.d.ts.map +1 -0
  232. package/dist/backend/backend/src/services/trace/trace.types.js +85 -0
  233. package/dist/backend/backend/src/services/trace/trace.types.js.map +1 -0
  234. package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.d.ts +1 -1
  235. package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.boot.d.ts.map +1 -1
  236. package/dist/backend/backend/src/services/v3/request.service.d.ts.map +1 -1
  237. package/dist/backend/backend/src/services/v3/request.service.js +4 -0
  238. package/dist/backend/backend/src/services/v3/request.service.js.map +1 -1
  239. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.d.ts.map +1 -1
  240. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js +22 -2
  241. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js.map +1 -1
  242. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.d.ts +3 -1
  243. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.d.ts.map +1 -1
  244. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.js.map +1 -1
  245. package/dist/backend/backend/src/services/workflow/message-scheduler.service.d.ts.map +1 -1
  246. package/dist/backend/backend/src/services/workflow/message-scheduler.service.js +3 -0
  247. package/dist/backend/backend/src/services/workflow/message-scheduler.service.js.map +1 -1
  248. package/dist/backend/backend/src/services/workflow/scheduler.service.d.ts.map +1 -1
  249. package/dist/backend/backend/src/services/workflow/scheduler.service.js +3 -0
  250. package/dist/backend/backend/src/services/workflow/scheduler.service.js.map +1 -1
  251. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts +3 -0
  252. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts.map +1 -1
  253. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js +7 -0
  254. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js.map +1 -1
  255. package/dist/backend/backend/src/types/experiment.types.d.ts +155 -0
  256. package/dist/backend/backend/src/types/experiment.types.d.ts.map +1 -0
  257. package/dist/backend/backend/src/types/experiment.types.js +32 -0
  258. package/dist/backend/backend/src/types/experiment.types.js.map +1 -0
  259. package/dist/backend/backend/src/types/messaging.types.d.ts +6 -0
  260. package/dist/backend/backend/src/types/messaging.types.d.ts.map +1 -1
  261. package/dist/backend/backend/src/types/messaging.types.js.map +1 -1
  262. package/dist/backend/backend/src/types/signal-digest.types.d.ts +185 -0
  263. package/dist/backend/backend/src/types/signal-digest.types.d.ts.map +1 -0
  264. package/dist/backend/backend/src/types/signal-digest.types.js +21 -0
  265. package/dist/backend/backend/src/types/signal-digest.types.js.map +1 -0
  266. package/dist/backend/backend/src/types/v2/request.types.d.ts +4 -0
  267. package/dist/backend/backend/src/types/v2/request.types.d.ts.map +1 -1
  268. package/dist/backend/backend/src/types/v2/request.types.js.map +1 -1
  269. package/dist/backend/backend/src/types/v2/work-item.types.d.ts +4 -0
  270. package/dist/backend/backend/src/types/v2/work-item.types.d.ts.map +1 -1
  271. package/dist/backend/backend/src/types/v2/work-item.types.js +1 -0
  272. package/dist/backend/backend/src/types/v2/work-item.types.js.map +1 -1
  273. package/dist/backend/backend/src/utils/security.d.ts +2 -0
  274. package/dist/backend/backend/src/utils/security.d.ts.map +1 -1
  275. package/dist/backend/backend/src/utils/security.js +3 -1
  276. package/dist/backend/backend/src/utils/security.js.map +1 -1
  277. package/dist/backend/backend/src/websocket/chat.gateway.d.ts +9 -0
  278. package/dist/backend/backend/src/websocket/chat.gateway.d.ts.map +1 -1
  279. package/dist/backend/backend/src/websocket/chat.gateway.js +26 -0
  280. package/dist/backend/backend/src/websocket/chat.gateway.js.map +1 -1
  281. package/dist/backend/build-info.json +2 -2
  282. package/dist/cli/backend/src/constants.d.ts +191 -1
  283. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  284. package/dist/cli/backend/src/constants.js +194 -1
  285. package/dist/cli/backend/src/constants.js.map +1 -1
  286. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  287. package/dist/cli/backend/src/services/agent/agent-registration.service.js +6 -1
  288. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  289. package/dist/cli/backend/src/services/agent/auditor-scheduler.service.d.ts.map +1 -1
  290. package/dist/cli/backend/src/services/agent/auditor-scheduler.service.js +3 -0
  291. package/dist/cli/backend/src/services/agent/auditor-scheduler.service.js.map +1 -1
  292. package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.d.ts +1 -1
  293. package/dist/cli/backend/src/services/agent/oauth-relogin-monitor.service.d.ts.map +1 -1
  294. package/dist/cli/backend/src/services/agent/runtime-session-recovery.d.ts +4 -2
  295. package/dist/cli/backend/src/services/agent/runtime-session-recovery.d.ts.map +1 -1
  296. package/dist/cli/backend/src/services/agent/runtime-session-recovery.js +22 -6
  297. package/dist/cli/backend/src/services/agent/runtime-session-recovery.js.map +1 -1
  298. package/dist/cli/backend/src/services/ai/prompt-modules/communication.module.js +2 -2
  299. package/dist/cli/backend/src/services/ai/prompt-modules/communication.module.js.map +1 -1
  300. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  301. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -0
  302. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  303. package/dist/cli/backend/src/services/backup/backup-archive.service.d.ts +6 -0
  304. package/dist/cli/backend/src/services/backup/backup-archive.service.d.ts.map +1 -1
  305. package/dist/cli/backend/src/services/backup/backup-archive.service.js +7 -1
  306. package/dist/cli/backend/src/services/backup/backup-archive.service.js.map +1 -1
  307. package/dist/cli/backend/src/services/chat/chat.service.d.ts +185 -0
  308. package/dist/cli/backend/src/services/chat/chat.service.d.ts.map +1 -0
  309. package/dist/cli/backend/src/services/chat/chat.service.js +558 -0
  310. package/dist/cli/backend/src/services/chat/chat.service.js.map +1 -0
  311. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +14 -3
  312. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
  313. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +28 -4
  314. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
  315. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts +4 -3
  316. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
  317. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js +7 -4
  318. package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
  319. package/dist/cli/backend/src/services/decisions/decision-store.d.ts.map +1 -1
  320. package/dist/cli/backend/src/services/decisions/decision-store.js +5 -1
  321. package/dist/cli/backend/src/services/decisions/decision-store.js.map +1 -1
  322. package/dist/cli/backend/src/services/messaging/owner-message-watchdog.service.d.ts.map +1 -1
  323. package/dist/cli/backend/src/services/messaging/owner-message-watchdog.service.js +7 -0
  324. package/dist/cli/backend/src/services/messaging/owner-message-watchdog.service.js.map +1 -1
  325. package/dist/cli/backend/src/services/messaging/queue-processor.service.d.ts +11 -3
  326. package/dist/cli/backend/src/services/messaging/queue-processor.service.d.ts.map +1 -1
  327. package/dist/cli/backend/src/services/messaging/queue-processor.service.js +24 -7
  328. package/dist/cli/backend/src/services/messaging/queue-processor.service.js.map +1 -1
  329. package/dist/cli/backend/src/services/monitoring/token-usage.service.d.ts +7 -0
  330. package/dist/cli/backend/src/services/monitoring/token-usage.service.d.ts.map +1 -1
  331. package/dist/cli/backend/src/services/monitoring/token-usage.service.js +4 -0
  332. package/dist/cli/backend/src/services/monitoring/token-usage.service.js.map +1 -1
  333. package/dist/cli/backend/src/services/people/acting-for.service.d.ts +42 -5
  334. package/dist/cli/backend/src/services/people/acting-for.service.d.ts.map +1 -1
  335. package/dist/cli/backend/src/services/people/acting-for.service.js +80 -5
  336. package/dist/cli/backend/src/services/people/acting-for.service.js.map +1 -1
  337. package/dist/cli/backend/src/services/people/people-directory.service.d.ts +71 -13
  338. package/dist/cli/backend/src/services/people/people-directory.service.d.ts.map +1 -1
  339. package/dist/cli/backend/src/services/people/people-directory.service.js +164 -27
  340. package/dist/cli/backend/src/services/people/people-directory.service.js.map +1 -1
  341. package/dist/cli/backend/src/services/project-tickets/project-ticket.service.d.ts.map +1 -1
  342. package/dist/cli/backend/src/services/project-tickets/project-ticket.service.js +2 -0
  343. package/dist/cli/backend/src/services/project-tickets/project-ticket.service.js.map +1 -1
  344. package/dist/cli/backend/src/services/session/session-handoff.service.d.ts.map +1 -1
  345. package/dist/cli/backend/src/services/session/session-handoff.service.js +8 -5
  346. package/dist/cli/backend/src/services/session/session-handoff.service.js.map +1 -1
  347. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts +14 -1
  348. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  349. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +30 -10
  350. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  351. package/dist/cli/backend/src/services/slack/slack.service.d.ts +31 -1
  352. package/dist/cli/backend/src/services/slack/slack.service.d.ts.map +1 -1
  353. package/dist/cli/backend/src/services/slack/slack.service.js +52 -7
  354. package/dist/cli/backend/src/services/slack/slack.service.js.map +1 -1
  355. package/dist/cli/backend/src/services/task-pool/pool-storage.d.ts.map +1 -1
  356. package/dist/cli/backend/src/services/task-pool/pool-storage.js +4 -0
  357. package/dist/cli/backend/src/services/task-pool/pool-storage.js.map +1 -1
  358. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  359. package/dist/cli/backend/src/services/task-pool/task-pool.service.js +2 -0
  360. package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  361. package/dist/cli/backend/src/services/trace/trace-context.service.d.ts +195 -0
  362. package/dist/cli/backend/src/services/trace/trace-context.service.d.ts.map +1 -0
  363. package/dist/cli/backend/src/services/trace/trace-context.service.js +432 -0
  364. package/dist/cli/backend/src/services/trace/trace-context.service.js.map +1 -0
  365. package/dist/cli/backend/src/services/trace/trace-markers.d.ts +93 -0
  366. package/dist/cli/backend/src/services/trace/trace-markers.d.ts.map +1 -0
  367. package/dist/cli/backend/src/services/trace/trace-markers.js +162 -0
  368. package/dist/cli/backend/src/services/trace/trace-markers.js.map +1 -0
  369. package/dist/cli/backend/src/services/trace/trace-recorder.d.ts +290 -0
  370. package/dist/cli/backend/src/services/trace/trace-recorder.d.ts.map +1 -0
  371. package/dist/cli/backend/src/services/trace/trace-recorder.js +669 -0
  372. package/dist/cli/backend/src/services/trace/trace-recorder.js.map +1 -0
  373. package/dist/cli/backend/src/services/trace/trace-store.d.ts +269 -0
  374. package/dist/cli/backend/src/services/trace/trace-store.d.ts.map +1 -0
  375. package/dist/cli/backend/src/services/trace/trace-store.js +602 -0
  376. package/dist/cli/backend/src/services/trace/trace-store.js.map +1 -0
  377. package/dist/cli/backend/src/services/trace/trace.types.d.ts +118 -0
  378. package/dist/cli/backend/src/services/trace/trace.types.d.ts.map +1 -0
  379. package/dist/cli/backend/src/services/trace/trace.types.js +85 -0
  380. package/dist/cli/backend/src/services/trace/trace.types.js.map +1 -0
  381. package/dist/cli/backend/src/services/v3/request.service.d.ts.map +1 -1
  382. package/dist/cli/backend/src/services/v3/request.service.js +4 -0
  383. package/dist/cli/backend/src/services/v3/request.service.js.map +1 -1
  384. package/dist/cli/backend/src/services/wiki/wiki-ingest.service.d.ts +3 -1
  385. package/dist/cli/backend/src/services/wiki/wiki-ingest.service.d.ts.map +1 -1
  386. package/dist/cli/backend/src/services/wiki/wiki-ingest.service.js.map +1 -1
  387. package/dist/cli/backend/src/types/messaging.types.d.ts +6 -0
  388. package/dist/cli/backend/src/types/messaging.types.d.ts.map +1 -1
  389. package/dist/cli/backend/src/types/messaging.types.js.map +1 -1
  390. package/dist/cli/backend/src/types/v2/request.types.d.ts +4 -0
  391. package/dist/cli/backend/src/types/v2/request.types.d.ts.map +1 -1
  392. package/dist/cli/backend/src/types/v2/request.types.js.map +1 -1
  393. package/dist/cli/backend/src/types/v2/work-item.types.d.ts +4 -0
  394. package/dist/cli/backend/src/types/v2/work-item.types.d.ts.map +1 -1
  395. package/dist/cli/backend/src/types/v2/work-item.types.js +1 -0
  396. package/dist/cli/backend/src/types/v2/work-item.types.js.map +1 -1
  397. package/dist/cli/backend/src/utils/security.d.ts +196 -0
  398. package/dist/cli/backend/src/utils/security.d.ts.map +1 -0
  399. package/dist/cli/backend/src/utils/security.js +538 -0
  400. package/dist/cli/backend/src/utils/security.js.map +1 -0
  401. package/dist/cli/backend/src/websocket/chat.gateway.d.ts +9 -0
  402. package/dist/cli/backend/src/websocket/chat.gateway.d.ts.map +1 -1
  403. package/dist/cli/backend/src/websocket/chat.gateway.js +26 -0
  404. package/dist/cli/backend/src/websocket/chat.gateway.js.map +1 -1
  405. package/frontend/dist/assets/{index-465d1f8f.js → index-800118ca.js} +274 -274
  406. package/frontend/dist/index.html +1 -1
  407. package/package.json +1 -1
@@ -0,0 +1,319 @@
1
+ #!/usr/bin/env python3
2
+ """Offline tests for signal_digest.py (no network). Run: python3 signal_digest.test.py"""
3
+ import contextlib
4
+ import datetime as dt
5
+ import io
6
+ import json
7
+ import os
8
+ import sys
9
+ import tempfile
10
+ import unittest
11
+
12
+ HERE = os.path.dirname(os.path.abspath(__file__))
13
+ sys.path.insert(0, HERE)
14
+ import signal_digest as D # noqa: E402
15
+
16
+ SO = D.load_seo_ops()
17
+ TODAY = dt.date(2026, 10, 3)
18
+ SITE = "https://visa.careerengine.us"
19
+
20
+
21
+ class FakeNet:
22
+ """Canned GA4 reports (by first dimension), GSC rows (by dimensions, now/prev) and GET pages."""
23
+
24
+ def __init__(self, ga4=None, gsc=None, pages=None, fail_gsc=False, crash_ga4=None):
25
+ self.ga4, self.gsc, self.pages, self.fail_gsc = ga4 or {}, gsc or {}, pages or {}, fail_gsc
26
+ self.crash_ga4 = crash_ga4
27
+ self.gets = []
28
+ self.ga4_bodies = []
29
+
30
+ def get(self, url, timeout=30):
31
+ self.gets.append(url)
32
+ v = self.pages.get(url)
33
+ if v is None:
34
+ return 404, ""
35
+ if isinstance(v, int):
36
+ return v, ""
37
+ return 200, v
38
+
39
+ def post_json(self, url, body, scope, what, email_hint=None):
40
+ if "analyticsdata" in url:
41
+ if self.crash_ga4:
42
+ raise self.crash_ga4
43
+ self.ga4_bodies.append(body)
44
+ return self.ga4.get(body["dimensions"][0]["name"], {"rows": []})
45
+ if self.fail_gsc:
46
+ raise SO.SeoOpsError("sa@x is not authorized for Search Console property (HTTP 403).", 3)
47
+ start, end, _, _ = SO.windows(7, TODAY)
48
+ rng = "now" if body["startDate"] == str(start) else "prev"
49
+ return {"rows": self.gsc.get((rng,) + tuple(body["dimensions"]), [])}
50
+
51
+ def service_account_email(self):
52
+ return "sa@example.iam.gserviceaccount.com"
53
+
54
+
55
+ def gsc_row(keys, imp, clk, pos):
56
+ return {"keys": keys, "impressions": imp, "clicks": clk, "ctr": clk / imp if imp else 0, "position": pos}
57
+
58
+
59
+ def ga4_rows(rows):
60
+ return {"rows": [{"dimensionValues": [{"value": d} for d in dims], "metricValues": [{"value": str(m)} for m in mets]} for dims, mets in rows]}
61
+
62
+
63
+ GA4 = {
64
+ "sessionDefaultChannelGroup": ga4_rows([
65
+ (["Organic Search", "now"], [300, 6]), (["Direct", "now"], [100, 2]),
66
+ (["Organic Search", "prev"], [320, 12]), (["Direct", "prev"], [110, 3]),
67
+ ]),
68
+ "eventName": ga4_rows([(["generate_lead", "now"], [6]), (["generate_lead", "prev"], [13]), (["scroll", "now"], [50])]),
69
+ }
70
+
71
+ GSC = {
72
+ ("now", "query"): [
73
+ gsc_row(["h1b visa fee"], 900, 36, 2.1), # low CTR in the top 3
74
+ gsc_row(["opt extension"], 400, 8, 7.5), # near miss
75
+ gsc_row(["eb2 niw timeline"], 220, 11, 5.2), # near miss + rising
76
+ gsc_row(["careerengine login"], 999, 400, 1.0), # excluded (brand)
77
+ gsc_row(["i140 premium processing"], 30, 0, 25.0), # new, no page ranks for it yet
78
+ ],
79
+ ("prev", "query"): [gsc_row(["h1b visa fee"], 880, 35, 2.2), gsc_row(["opt extension"], 380, 8, 7.9)],
80
+ ("now", "query", "page"): [
81
+ gsc_row(["h1b visa fee", SITE + "/h1b-fee"], 850, 34, 2.0),
82
+ gsc_row(["opt extension", SITE + "/opt"], 300, 6, 7.0),
83
+ gsc_row(["opt extension", SITE + "/opt-stem"], 100, 2, 9.0),
84
+ gsc_row(["eb2 niw timeline", SITE + "/eb2-niw"], 220, 11, 5.2),
85
+ ],
86
+ }
87
+
88
+ SITEMAP = "<urlset><url><loc>%s/h1b-fee</loc></url><url><loc>%s/old-page</loc></url><url><loc>%s/opt</loc></url></urlset>" % (SITE, SITE, SITE)
89
+
90
+
91
+ def config(extra=None, sd=None):
92
+ cfg = {
93
+ "siteUrl": SITE, "gscProperty": "sc-domain:visa.careerengine.us", "ga4PropertyId": "42",
94
+ "sitemapUrl": SITE + "/sitemap.xml", "exclusions": {"queries": ["careerengine"]},
95
+ "signalDigest": dict({"project": "CE site", "ga4": {"conversionEvents": ["generate_lead"]}}, **(sd or {})),
96
+ }
97
+ cfg.update(extra or {})
98
+ return SO.deep_merge(SO.DEFAULTS, cfg)
99
+
100
+
101
+ def net(**kw):
102
+ pages = {SITE + "/sitemap.xml": SITEMAP, SITE + "/h1b-fee": "<html/>", SITE + "/opt": "<html/>"}
103
+ return FakeNet(ga4=GA4, gsc=GSC, pages=pages, **kw)
104
+
105
+
106
+ class Collect(unittest.TestCase):
107
+ def test_signals_and_ranked_candidates(self):
108
+ out = D.collect(SO, config(), net(), TODAY)
109
+ self.assertEqual(out["site"], "visa.careerengine.us")
110
+ self.assertEqual(out["project"], "CE site")
111
+ self.assertEqual(out["sources"], {"ga4": "ok", "gsc": "ok", "inbox": "not configured", "errors": "ok"})
112
+ self.assertEqual(out["ga4"]["keyEvents"], {"now": 6, "prev": 13, "changePct": -53.8})
113
+ self.assertEqual(out["ga4"]["organicSessions"], {"now": 300, "prev": 320, "changePct": -6.2})
114
+ self.assertEqual([e["event"] for e in out["ga4"]["byEvent"]], ["generate_lead"])
115
+ self.assertEqual([r["query"] for r in out["gsc"]["lowCtrTop3"]], ["h1b visa fee"])
116
+ self.assertEqual(out["gsc"]["lowCtrTop3"][0]["page"], SITE + "/h1b-fee")
117
+ self.assertNotIn("careerengine login", json.dumps(out["gsc"]))
118
+ self.assertEqual(out["errors"]["brokenPages"], [{"url": SITE + "/old-page", "status": 404}])
119
+ keys = [c["key"] for c in out["candidates"]]
120
+ # The conversion drop outranks everything; every candidate is complete.
121
+ self.assertEqual(keys[0], "ga4:key-events-drop")
122
+ self.assertIn("gsc:low-ctr:h1b visa fee", keys)
123
+ self.assertIn("gsc:near-miss:opt extension", keys)
124
+ self.assertIn("errors:broken:/old-page", keys)
125
+ self.assertIn("gsc:cannibal:opt extension", keys)
126
+ # Rising queries the site already ranks for (or that another action covers) are not topics.
127
+ self.assertNotIn("gsc:rising:h1b visa fee", keys)
128
+ self.assertNotIn("gsc:rising:opt extension", keys)
129
+ self.assertNotIn("gsc:rising:eb2 niw timeline", keys) # its near-miss action covers it
130
+ self.assertIn("gsc:rising:i140 premium processing", keys)
131
+ for c in out["candidates"]:
132
+ for f in ("key", "source", "signal", "proposal", "expectedEffect", "effort"):
133
+ self.assertTrue(c[f], (c["key"], f))
134
+ self.assertNotIn("subject", c)
135
+ low = next(c for c in out["candidates"] if c["key"] == "gsc:low-ctr:h1b visa fee")
136
+ self.assertIn("/h1b-fee", low["proposal"])
137
+ self.assertRegex(low["expectedEffect"], r"CTR 4\.0% → ~16%: about \+\d+ clicks a week")
138
+
139
+ def test_history_and_experiment_log_drop_drafts(self):
140
+ history = {"success": True, "data": {"site": "visa.careerengine.us", "entries": [
141
+ {"key": "GSC:Low-CTR:h1b visa fee", "status": "do", "at": "2026-09-30T12:00:00Z", "digestId": "SD-2"},
142
+ {"key": "errors:broken:/old-page", "status": "skip", "at": "2026-10-01T12:00:00Z", "digestId": "SD-3"},
143
+ {"key": "gsc:near-miss:eb2 niw timeline", "status": "open", "at": "2026-10-02T12:00:00Z", "digestId": "SD-4"},
144
+ ]}}
145
+ with tempfile.NamedTemporaryFile("w", suffix=".md", delete=False) as f:
146
+ f.write("## 2026-09-20 OPT Extension page rewrite\nResult: inconclusive\n")
147
+ try:
148
+ out = D.collect(SO, config(sd={"experimentLog": f.name}), net(), TODAY, history=history)
149
+ finally:
150
+ os.unlink(f.name)
151
+ keys = [c["key"] for c in out["candidates"]]
152
+ self.assertNotIn("gsc:low-ctr:h1b visa fee", keys)
153
+ self.assertNotIn("errors:broken:/old-page", keys)
154
+ self.assertNotIn("gsc:near-miss:opt extension", keys)
155
+ self.assertIn("gsc:near-miss:eb2 niw timeline", keys)
156
+ self.assertNotIn("gsc:rising:eb2 niw timeline", keys) # the near-miss action covers it
157
+ whys = {t["key"]: t["why"] for t in out["alreadyTried"]}
158
+ self.assertEqual(whys["gsc:low-ctr:h1b visa fee"], "owner chose Do on 2026-09-30 (SD-2)")
159
+ self.assertEqual(whys["errors:broken:/old-page"], "owner chose Skip on 2026-10-01 (SD-3)")
160
+ self.assertEqual(whys["gsc:near-miss:opt extension"], "mentioned in the experiment log")
161
+ # Open = still on the owner's card: reported, not blocked (the next digest replaces that card).
162
+ self.assertEqual(out["pendingOnOwner"], [{"key": "gsc:near-miss:eb2 niw timeline", "digestId": "SD-4"}])
163
+
164
+ def test_a_failing_source_is_reported_and_the_rest_still_runs(self):
165
+ out = D.collect(SO, config(), net(fail_gsc=True), TODAY)
166
+ self.assertTrue(out["sources"]["gsc"].startswith("error: sa@x is not authorized"))
167
+ self.assertEqual(out["sources"]["ga4"], "ok")
168
+ self.assertNotIn("gsc", out)
169
+ self.assertEqual(out["candidates"][0]["key"], "ga4:key-events-drop")
170
+
171
+ def test_an_unexpected_exception_in_one_source_does_not_kill_collect(self):
172
+ out = D.collect(SO, config(), net(crash_ga4=KeyError("rows")), TODAY)
173
+ self.assertEqual(out["sources"]["ga4"], "error: 'rows'")
174
+ self.assertEqual(out["sources"]["gsc"], "ok")
175
+ self.assertTrue(out["candidates"])
176
+ out = D.collect(SO, config(), net(crash_ga4=RuntimeError()), TODAY)
177
+ self.assertEqual(out["sources"]["ga4"], "error: RuntimeError")
178
+
179
+ def test_sitemap_is_checked_in_rotating_slices(self):
180
+ urls = ["%s/p%02d" % (SITE, i) for i in range(10)]
181
+ self.assertEqual(D.sitemap_slice(urls, 20, TODAY), (urls, 0))
182
+ seen, offsets = set(), []
183
+ for d in range(4):
184
+ got, offset = D.sitemap_slice(list(reversed(urls)), 3, TODAY + dt.timedelta(days=d))
185
+ self.assertEqual(len(got), 3)
186
+ seen.update(got)
187
+ offsets.append(offset)
188
+ self.assertEqual(len(set(offsets)), 4) # a different slice each day
189
+ self.assertEqual(seen, set(urls)) # the whole sitemap within ceil(10/3) = 4 days
190
+ sm = "<urlset>%s</urlset>" % "".join("<url><loc>%s</loc></url>" % u for u in urls)
191
+ n = net()
192
+ n.pages[SITE + "/sitemap.xml"] = sm
193
+ a = D.collect(SO, config(sd={"errors": {"maxUrls": 3}}), n, TODAY)["errors"]
194
+ b = D.collect(SO, config(sd={"errors": {"maxUrls": 3}}), n, TODAY + dt.timedelta(days=1))["errors"]
195
+ self.assertEqual((a["checked"], a["sitemapUrls"]), (3, 10))
196
+ self.assertNotEqual(a["sitemapOffset"], b["sitemapOffset"])
197
+
198
+ def test_inbox_summary_and_failure(self):
199
+ inbox = {"query": "to:visa@", "count": 1, "messages": [{"from": "a@b", "subject": "H1B question", "date": "d", "snippet": "hi", "id": "x"}]}
200
+ out = D.collect(SO, config(), net(), TODAY, inbox=inbox)
201
+ self.assertEqual(out["inbox"], {"query": "to:visa@", "count": 1, "messages": [{"from": "a@b", "subject": "H1B question", "date": "d", "snippet": "hi"}]})
202
+ bad = D.collect(SO, config(), net(), TODAY, inbox={"success": False, "reason": "not_connected", "message": "connect Google"})
203
+ self.assertEqual(bad["sources"]["inbox"], "error: inbox search failed: not_connected connect Google")
204
+
205
+ def test_js_errors_from_a_url(self):
206
+ pages = {SITE + "/errs": json.dumps({"errors": [{"message": "TypeError: x is undefined", "count": 12, "url": SITE + "/form"}, {"message": "rare", "count": 1}]})}
207
+ n = net()
208
+ n.pages.update(pages)
209
+ out = D.collect(SO, config(sd={"errors": {"checkSitemap": False, "url": SITE + "/errs"}}), n, TODAY)
210
+ self.assertEqual(out["errors"]["jsErrors"][0], {"message": "TypeError: x is undefined", "count": 12, "url": SITE + "/form"})
211
+ js = [c for c in out["candidates"] if c["key"].startswith("errors:js:")]
212
+ self.assertEqual(js[0]["signal"], "JS error 'TypeError: x is undefined' ×12 on /form")
213
+
214
+ def test_candidates_carry_the_experiment_a_do_creates(self):
215
+ out = D.collect(SO, config(), net(), TODAY)
216
+ by_key = {c["key"]: c for c in out["candidates"]}
217
+ self.assertEqual(by_key["gsc:low-ctr:h1b visa fee"]["experiment"],
218
+ {"source": "gsc", "measure": "ctr", "query": "h1b visa fee", "page": SITE + "/h1b-fee"})
219
+ self.assertEqual(by_key["gsc:near-miss:opt extension"]["experiment"]["measure"], "position")
220
+ self.assertEqual(by_key["gsc:rising:i140 premium processing"]["experiment"], {"source": "gsc", "measure": "clicks", "query": "i140 premium processing"})
221
+ # One configured conversion event → the drop is measured on it (as a key event), across all channels.
222
+ self.assertEqual(by_key["ga4:key-events-drop"]["experiment"], {"source": "ga4", "measure": "conversions", "event": "generate_lead", "channel": "all"})
223
+ self.assertNotIn("experiment", by_key["errors:broken:/old-page"])
224
+
225
+ def test_two_conversion_events_measure_all_key_events(self):
226
+ out = D.collect(SO, config(sd={"ga4": {"conversionEvents": ["generate_lead", "form_submit"]}}), net(), TODAY)
227
+ drop = next(c for c in out["candidates"] if c["key"] == "ga4:key-events-drop")
228
+ self.assertEqual(drop["experiment"], {"source": "ga4", "measure": "conversions", "channel": "all"})
229
+
230
+ def test_experiment_cards_cover_their_query_and_page(self):
231
+ experiments = {"success": True, "data": [
232
+ {"id": "EXP-1", "status": "running", "metric": {"query": "Opt Extension"}, "updatedAt": "2026-10-01T00:00:00Z"},
233
+ {"id": "EXP-2", "status": "done", "metric": {"page": SITE + "/h1b-fee"}, "updatedAt": "2026-09-20T00:00:00Z"},
234
+ {"id": "EXP-3", "status": "done", "metric": {"query": "i140 premium processing"}, "updatedAt": "2026-05-01T00:00:00Z"},
235
+ {"id": "EXP-4", "status": "cancelled", "metric": {"query": "eb2 niw timeline"}, "updatedAt": "2026-10-01T00:00:00Z"},
236
+ ]}
237
+ out = D.collect(SO, config(), net(), TODAY, experiments=experiments)
238
+ keys = [c["key"] for c in out["candidates"]]
239
+ whys = {t["key"]: t["why"] for t in out["alreadyTried"]}
240
+ self.assertEqual(whys["gsc:near-miss:opt extension"], "experiment EXP-1 (running)")
241
+ self.assertEqual(whys["gsc:cannibal:opt extension"], "experiment EXP-1 (running)")
242
+ self.assertEqual(whys["gsc:low-ctr:h1b visa fee"], "experiment EXP-2 (done)")
243
+ self.assertIn("gsc:rising:i140 premium processing", keys) # finished more than 90 days ago
244
+ self.assertIn("gsc:near-miss:eb2 niw timeline", keys) # cancelled
245
+
246
+ def test_ga4_host_filter(self):
247
+ n = net()
248
+ D.collect(SO, config(extra={"ga4HostName": "visa.careerengine.us"}), n, TODAY)
249
+ self.assertTrue(n.ga4_bodies)
250
+ for body in n.ga4_bodies:
251
+ self.assertEqual(body["dimensionFilter"]["filter"]["stringFilter"]["value"], "visa.careerengine.us")
252
+ n2 = net()
253
+ D.collect(SO, config(), n2, TODAY)
254
+ self.assertNotIn("dimensionFilter", n2.ga4_bodies[0])
255
+
256
+ def test_sitemap_check_without_a_sitemap_is_not_configured(self):
257
+ out = D.collect(SO, config(extra={"sitemapUrl": ""}), net(), TODAY)
258
+ self.assertEqual(out["sources"]["errors"], "not configured")
259
+
260
+ def test_nothing_configured(self):
261
+ out = D.collect(SO, config(extra={"gscProperty": "", "ga4PropertyId": "", "sitemapUrl": ""}, sd={"errors": {"checkSitemap": False}}), net(), TODAY)
262
+ self.assertEqual(set(out["sources"].values()), {"not configured"})
263
+ self.assertEqual(out["candidates"], [])
264
+
265
+
266
+ class Helpers(unittest.TestCase):
267
+ def test_site_from_site_url(self):
268
+ self.assertEqual(D.digest_settings({"siteUrl": "https://example.com/x"})["site"], "example.com")
269
+ with self.assertRaises(D.DigestError):
270
+ D.digest_settings({})
271
+
272
+ def test_parse_error_list(self):
273
+ self.assertEqual(D.parse_error_list('[{"message":"a","count":2},{"nope":1},{"message":"b"}]'),
274
+ [{"message": "a", "count": 2}, {"message": "b", "count": 1}])
275
+ with self.assertRaises(ValueError):
276
+ D.parse_error_list('{"errors": 3}')
277
+
278
+ def test_normalize_key_and_change(self):
279
+ self.assertEqual(D.normalize_key(" GSC:Low CTR "), "gsc:low ctr")
280
+ self.assertIsNone(D.change_pct(5, 0))
281
+ self.assertEqual(D.change_pct(50, 100), -50.0)
282
+
283
+ def test_load_seo_ops_missing(self):
284
+ with self.assertRaises(D.DigestError):
285
+ D.load_seo_ops(["/nope/seo_ops.py"])
286
+
287
+
288
+ class Main(unittest.TestCase):
289
+ def run_main(self, cfg, n):
290
+ with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False) as f:
291
+ json.dump(cfg, f)
292
+ out, err = io.StringIO(), io.StringIO()
293
+ try:
294
+ with contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
295
+ code = D.main(["collect", "--config", f.name], net=n, today=TODAY, so=SO)
296
+ finally:
297
+ os.unlink(f.name)
298
+ return code, out.getvalue(), err.getvalue()
299
+
300
+ def test_prints_json_and_exits_0(self):
301
+ raw = {"siteUrl": SITE, "gscProperty": "sc-domain:x", "exclusions": {"queries": ["careerengine"]}, "signalDigest": {"errors": {"checkSitemap": False}}}
302
+ code, out, _ = self.run_main(raw, net())
303
+ self.assertEqual(code, 0)
304
+ self.assertEqual(json.loads(out)["sources"]["gsc"], "ok")
305
+
306
+ def test_exit_1_when_nothing_could_be_examined(self):
307
+ code, out, err = self.run_main({"siteUrl": SITE, "signalDigest": {"errors": {"checkSitemap": False}}}, net())
308
+ self.assertEqual(code, 1)
309
+ self.assertIn("no source could be examined", err)
310
+ self.assertEqual(json.loads(out)["candidates"], [])
311
+
312
+ def test_exit_2_on_setup_problem(self):
313
+ code, _, err = self.run_main({"gscProperty": "x"}, net())
314
+ self.assertEqual(code, 2)
315
+ self.assertIn("signalDigest.site", err)
316
+
317
+
318
+ if __name__ == "__main__":
319
+ unittest.main()
@@ -1995,6 +1995,18 @@ export declare const SLACK_DEDUP_CONSTANTS: {
1995
1995
  /** Maximum number of message fingerprints to track in memory */
1996
1996
  readonly MAX_TRACKED_MESSAGES: 100;
1997
1997
  };
1998
+ /**
1999
+ * Constants for outgoing Slack message payloads.
2000
+ */
2001
+ export declare const SLACK_OUTGOING_MESSAGE_CONSTANTS: {
2002
+ /**
2003
+ * Top-level `text` sent when a message has no text of its own and no
2004
+ * block text to derive one from. Slack rejects an empty `text` without
2005
+ * blocks (`no_text`) and uses `text` as the notification/accessibility
2006
+ * fallback when blocks are present, so it must never be empty.
2007
+ */
2008
+ readonly EMPTY_TEXT_FALLBACK: "New message";
2009
+ };
1998
2010
  /**
1999
2011
  * Constants for NOTIFY Slack delivery reconciliation.
2000
2012
  * Used by NotifyReconciliationService to retry failed Slack deliveries
@@ -5415,6 +5427,66 @@ export declare const SPEND_CAP_CONSTANTS: {
5415
5427
  readonly UNLIMITED: "Unlimited today";
5416
5428
  };
5417
5429
  };
5430
+ /**
5431
+ * Run traces: one id from a request / goal / owner message through tickets,
5432
+ * work items, agent turns, skill calls, messages and usage
5433
+ * (specs/2026-10-03-run-traces.md, issue #983).
5434
+ */
5435
+ export declare const TRACE_CONSTANTS: {
5436
+ /** Folder under CREWLY_HOME holding `<traceId>.jsonl` and the index */
5437
+ readonly DIR_NAME: "traces";
5438
+ /** Index file name inside {@link TRACE_CONSTANTS.DIR_NAME} */
5439
+ readonly INDEX_FILE: "index.json";
5440
+ /** Index file format version */
5441
+ readonly INDEX_VERSION: 1;
5442
+ /** Trace id prefix (`tr-YYYYMMDD-xxxxxxxx`) */
5443
+ readonly ID_PREFIX: "tr-";
5444
+ /** Random hex chars at the end of a trace id */
5445
+ readonly ID_RANDOM_HEX: 8;
5446
+ /** Most events one trace keeps; the next one becomes a `trace.truncated` marker */
5447
+ readonly MAX_EVENTS_PER_TRACE: 5000;
5448
+ /** Most bytes one trace file takes (same marker) */
5449
+ readonly MAX_BYTES_PER_TRACE: number;
5450
+ /** Longest summary written (chars); message bodies are cut to this */
5451
+ readonly SUMMARY_MAX_CHARS: 200;
5452
+ /** Longest string value kept in an event's `data` map */
5453
+ readonly DATA_VALUE_MAX_CHARS: 120;
5454
+ /** Traces whose last event is older than this are deleted (90 days) */
5455
+ readonly RETENTION_MS: number;
5456
+ /** Minimum gap between two retention sweeps (one day) */
5457
+ readonly SWEEP_INTERVAL_MS: number;
5458
+ /** Delay before the in-memory index is written after a change */
5459
+ readonly INDEX_FLUSH_DELAY_MS: 1000;
5460
+ /** Per-session `(since, traceId)` spans kept for timestamp lookups */
5461
+ readonly SESSION_HISTORY_SPANS: 50;
5462
+ /**
5463
+ * A session with no activity (deliveries, API calls, hooks) for this long
5464
+ * stops being on its trace. Override with CREWLY_TRACE_IDLE_CLEAR_MINUTES.
5465
+ */
5466
+ readonly IDLE_CLEAR_MS: number;
5467
+ /**
5468
+ * Most bytes the whole traces folder may take; the oldest traces are
5469
+ * pruned past it (daily sweep and the disk janitor). Override with
5470
+ * CREWLY_TRACES_MAX_TOTAL_MB.
5471
+ */
5472
+ readonly MAX_TOTAL_BYTES: number;
5473
+ /** A pending owner-message root older than this is forgotten */
5474
+ readonly PENDING_ROOT_TTL_MS: number;
5475
+ /** Default and maximum page size of `GET /api/traces/:id` */
5476
+ readonly DEFAULT_PAGE_SIZE: 200;
5477
+ readonly MAX_PAGE_SIZE: 1000;
5478
+ /** Default and maximum rows of `GET /api/traces` */
5479
+ readonly DEFAULT_LIST_LIMIT: 100;
5480
+ readonly MAX_LIST_LIMIT: 1000;
5481
+ /**
5482
+ * API paths (relative to /api) whose agent calls are not recorded as skill
5483
+ * calls: Claude Code hooks fire on every tool use, heartbeats are polling,
5484
+ * and reading a trace must not write to it.
5485
+ */
5486
+ readonly SKIPPED_SKILL_PATH_PREFIXES: readonly ["/agent-hooks", "/traces", "/heartbeat", "/health"];
5487
+ /** HTTP statuses recorded as `guard.block` instead of `error` */
5488
+ readonly GUARD_BLOCK_STATUSES: readonly [403, 409, 423, 429];
5489
+ };
5418
5490
  /**
5419
5491
  * Token usage: the unit, the stats endpoint, and the Codex / Antigravity
5420
5492
  * ledgers (specs/2026-10-02-spend-cap.md).
@@ -5556,8 +5628,14 @@ export declare const PEOPLE_CONSTANTS: {
5556
5628
  readonly STORE_FILE: "people.json";
5557
5629
  /** Who each agent session acts for, under CREWLY_HOME */
5558
5630
  readonly ACTING_FOR_FILE: "acting-for.json";
5559
- /** Person id of the instance owner when their Slack user id is not known (dashboard, terminal) */
5631
+ /** Person id of the instance owner — always this, whatever their Slack user id (stored and sent to Cloud) */
5560
5632
  readonly OWNER_ID: "owner";
5633
+ /** Env var naming the owner's Slack user id(s), comma-separated — for Slack credentials from env (no installer known) */
5634
+ readonly OWNER_SLACK_USER_ID_ENV: "SLACK_OWNER_USER_ID";
5635
+ /** Error code when Crewly Cloud is too old for per-person access (sharing endpoint missing) */
5636
+ readonly CLOUD_UPDATE_REQUIRED_CODE: "cloud_update_required";
5637
+ /** What the dashboard shows for that */
5638
+ readonly CLOUD_UPDATE_REQUIRED_MESSAGE: "Requires a Cloud update";
5561
5639
  /** Roles a person can have */
5562
5640
  readonly ROLES: readonly string[];
5563
5641
  /** Role a newly seen Slack user gets */
@@ -5576,4 +5654,116 @@ export declare const PEOPLE_CONSTANTS: {
5576
5654
  /** Slack user ids (`U…` / `W…`) */
5577
5655
  readonly SLACK_USER_ID_PATTERN: RegExp;
5578
5656
  };
5657
+ /**
5658
+ * Daily signal digest (#987, specs/2026-10-03-signal-digest.md): a team lead
5659
+ * turns a site's signals (GA4, Search Console, inbound mail, errors) into 3–5
5660
+ * actions; the owner answers Do / Skip per action on one Slack card, and Do
5661
+ * opens an experiment ticket.
5662
+ */
5663
+ export declare const SIGNAL_DIGEST_CONSTANTS: {
5664
+ /** Persisted digests under CREWLY_HOME */
5665
+ readonly STORE_FILENAME: "signal-digests.json";
5666
+ /** Id prefix of a digest (`SD-<n>`) */
5667
+ readonly ID_PREFIX: "SD-";
5668
+ /** Actions per digest */
5669
+ readonly MIN_ITEMS: 3;
5670
+ readonly MAX_ITEMS: 5;
5671
+ /** Field length caps (characters) */
5672
+ readonly SITE_MAX_CHARS: 120;
5673
+ readonly KEY_MAX_CHARS: 160;
5674
+ readonly SIGNAL_MAX_CHARS: 400;
5675
+ readonly PROPOSAL_MAX_CHARS: 200;
5676
+ readonly EXPECTED_MAX_CHARS: 240;
5677
+ readonly EFFORT_MAX_CHARS: 60;
5678
+ readonly METRIC_MAX_CHARS: 200;
5679
+ /** Ticket titles are cut here */
5680
+ readonly TICKET_TITLE_MAX_CHARS: 120;
5681
+ /** Item sources an action can name */
5682
+ readonly SOURCES: readonly string[];
5683
+ /** An action the owner chose Do on is not proposed again for this long (ms) */
5684
+ readonly DO_BLOCK_MS: number;
5685
+ /** An action the owner skipped is not proposed again for this long (ms) */
5686
+ readonly SKIP_BLOCK_MS: number;
5687
+ /** Experiment observation window written on Do tickets (days, #986 default) */
5688
+ readonly EXPERIMENT_WINDOW_DAYS: 14;
5689
+ /** Labels on the ticket a Do creates */
5690
+ readonly TICKET_LABELS: readonly string[];
5691
+ /** Status of the ticket a Do creates */
5692
+ readonly TICKET_STATUS: "ready";
5693
+ /**
5694
+ * Block Kit action ids: `decision:signal:<n>:<do|skip>`. The `decision:`
5695
+ * prefix rides the existing Socket Mode / Cloud interactivity route; the
5696
+ * decision service ignores these (their value has no decision id).
5697
+ */
5698
+ readonly ACTION_PREFIX: "decision:signal:";
5699
+ /** Digests are kept this long after their last change, then pruned (ms) */
5700
+ readonly KEEP_MS: number;
5701
+ /**
5702
+ * A site's digest proposed within this window with actions still open
5703
+ * stands: proposing again returns it (same actions) or answers 409.
5704
+ */
5705
+ readonly DUPLICATE_WINDOW_MS: number;
5706
+ /** A source report older than this is not attached to a new digest (ms) */
5707
+ readonly SOURCE_REPORT_MAX_AGE_MS: number;
5708
+ /** Source names / failure details are cut at these lengths */
5709
+ readonly SOURCE_NAME_MAX_CHARS: 40;
5710
+ readonly SOURCE_DETAIL_MAX_CHARS: 200;
5711
+ /** Most sources one report may list */
5712
+ readonly MAX_SOURCES: 10;
5713
+ /** How the card names the known sources */
5714
+ readonly SOURCE_LABELS: Readonly<Record<string, string>>;
5715
+ };
5716
+ /**
5717
+ * Experiment cards (issue #986, epic #982): a hypothesis on an optimisation
5718
+ * ticket, measured automatically at the end of its window. specs/experiment-cards.md
5719
+ */
5720
+ export declare const EXPERIMENT_CONSTANTS: {
5721
+ /** Experiment store under CREWLY_HOME */
5722
+ readonly STORE_FILE: "experiments.json";
5723
+ /** Id prefix (EXP-1, EXP-2, …) */
5724
+ readonly ID_PREFIX: "EXP-";
5725
+ /** Default observation window; whole weeks keep weekdays balanced */
5726
+ readonly DEFAULT_WINDOW_DAYS: 14;
5727
+ /** Shortest / longest window accepted */
5728
+ readonly MIN_WINDOW_DAYS: 7;
5729
+ readonly MAX_WINDOW_DAYS: 90;
5730
+ /** Days a source's numbers take to settle (Search Console 2-3, GA4 1-2) */
5731
+ readonly SOURCE_LAG_DAYS: Readonly<Record<string, number>>;
5732
+ /** Measures each source offers (the seo-ops `metric` command) */
5733
+ readonly SOURCE_MEASURES: Readonly<Record<string, readonly string[]>>;
5734
+ /** Measures where lower is better (default direction "decrease") */
5735
+ readonly LOWER_IS_BETTER: readonly string[];
5736
+ /** Fewest events (baseline + result) before a count verdict is more than "inconclusive" */
5737
+ readonly MIN_COUNT_VOLUME: Readonly<Record<string, number>>;
5738
+ /** CTR / position: impressions needed in each window */
5739
+ readonly MIN_IMPRESSIONS_PER_WINDOW: 200;
5740
+ /** CTR: clicks needed across both windows */
5741
+ readonly MIN_CTR_CLICKS: 10;
5742
+ /** Position: days with impressions needed in each window */
5743
+ readonly MIN_POSITION_DAYS: 7;
5744
+ /** |z| (or Welch t) at or above this is a real change (~95% two-sided) */
5745
+ readonly SIGNIFICANCE_Z: 1.96;
5746
+ /** Confidence recorded on the agent's prediction when none is given */
5747
+ readonly DEFAULT_CONFIDENCE: 0.6;
5748
+ /** Scheduler tick */
5749
+ readonly TICK_INTERVAL_MS: number;
5750
+ /** Failed fetches before the owner is told a measurement is stuck */
5751
+ readonly MAX_FETCH_ATTEMPTS: 6;
5752
+ /** After MAX_FETCH_ATTEMPTS failures in a row, retry this often instead of every tick */
5753
+ readonly FETCH_BACKOFF_MS: number;
5754
+ /** seo-ops script, relative to the package root */
5755
+ readonly SEO_OPS_SCRIPT: "config/skills/agent/marketplace/seo-ops/seo_ops.py";
5756
+ /** Python used to run it */
5757
+ readonly PYTHON_BIN: "python3";
5758
+ /** seo-ops metric timeout */
5759
+ readonly FETCH_TIMEOUT_MS: 120000;
5760
+ /** Wiki page (append-only log) results are written to, inside the vault */
5761
+ readonly WIKI_LOG_PATH: "llm-curated/experiments/log.md";
5762
+ /** Timeline entries kept per experiment */
5763
+ readonly MAX_TIMELINE: 200;
5764
+ /** Longest hypothesis / title kept */
5765
+ readonly MAX_TEXT_LENGTH: 1000;
5766
+ /** Kill switch for the scheduler */
5767
+ readonly ENV_SWITCH: "CREWLY_EXPERIMENTS";
5768
+ };
5579
5769
  //# sourceMappingURL=constants.d.ts.map