agentix-cli 0.18.0 → 0.19.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 (171) hide show
  1. package/README.md +75 -27
  2. package/dist/{agent-RDVGL2TW.js → agent-57MG4LBM.js} +2 -2
  3. package/dist/{audit-Z47AQFFD.js → audit-JU3HWISB.js} +2 -2
  4. package/dist/{board-dashboard-KX2CNOSY.js → board-dashboard-HXDOYGIG.js} +2237 -69
  5. package/dist/board-dashboard-HXDOYGIG.js.map +1 -0
  6. package/dist/chunk-23AUU4S2.js +102 -0
  7. package/dist/chunk-23AUU4S2.js.map +1 -0
  8. package/dist/chunk-4MREHZP5.js +111 -0
  9. package/dist/chunk-4MREHZP5.js.map +1 -0
  10. package/dist/chunk-4YCH6IZV.js +2 -0
  11. package/dist/chunk-6B74TO2J.js +743 -0
  12. package/dist/chunk-6B74TO2J.js.map +1 -0
  13. package/dist/chunk-7P5QRX5W.js +108 -0
  14. package/dist/chunk-7P5QRX5W.js.map +1 -0
  15. package/dist/chunk-A6BKSZQA.js +3 -0
  16. package/dist/chunk-A6BKSZQA.js.map +1 -0
  17. package/dist/{chunk-6PPIFDOQ.js → chunk-CNNLREHD.js} +67 -22
  18. package/dist/{chunk-6PPIFDOQ.js.map → chunk-CNNLREHD.js.map} +1 -1
  19. package/dist/{chunk-J4WKMKTP.js → chunk-DLVOHCTA.js} +2 -2
  20. package/dist/chunk-E4ZZILAK.js +15 -0
  21. package/dist/chunk-E4ZZILAK.js.map +1 -0
  22. package/dist/{chunk-TRQSP67X.js → chunk-IKBHHTIT.js} +2 -2
  23. package/dist/chunk-JTJIPNXY.js +2 -0
  24. package/dist/chunk-JTJIPNXY.js.map +1 -0
  25. package/dist/chunk-MBOLXKA2.js +6 -0
  26. package/dist/chunk-MBOLXKA2.js.map +1 -0
  27. package/dist/{inbox-O5UB3YQN.js → chunk-MTRWXRFF.js} +69 -4
  28. package/dist/chunk-MTRWXRFF.js.map +1 -0
  29. package/dist/{processes-7W27G3J3.js → chunk-NOF4HSGK.js} +3 -3
  30. package/dist/{processes-7W27G3J3.js.map → chunk-NOF4HSGK.js.map} +1 -1
  31. package/dist/chunk-QW2OYR3N.js +7 -0
  32. package/dist/chunk-QW2OYR3N.js.map +1 -0
  33. package/dist/chunk-SV3I4CI7.js +440 -0
  34. package/dist/chunk-SV3I4CI7.js.map +1 -0
  35. package/dist/{chunk-JXEPJ3YD.js → chunk-TC3BSUP7.js} +5 -5
  36. package/dist/chunk-VWTBHLMX.js +1 -0
  37. package/dist/chunk-WFHIIVLL.js +719 -0
  38. package/dist/chunk-WFHIIVLL.js.map +1 -0
  39. package/dist/chunk-WPPUOTY6.js +6 -0
  40. package/dist/chunk-WPPUOTY6.js.map +1 -0
  41. package/dist/cli.js +1 -1
  42. package/dist/cli.js.map +1 -1
  43. package/dist/{compaction-S5H6DTOR.js → compaction-BTUJJ3LJ.js} +4 -4
  44. package/dist/config-QXWKEQON.js +2 -0
  45. package/dist/config-mutator-53XGR2NT.js +2 -0
  46. package/dist/conflict-detector-D7VIYPDI.js +2 -0
  47. package/dist/conflict-detector-D7VIYPDI.js.map +1 -0
  48. package/dist/{context-planner-2RGHJHSO.js → context-planner-I7ID7XF3.js} +3 -3
  49. package/dist/debug-N3B5NVJU.js +2 -0
  50. package/dist/{discover-OATU7SHI.js → discover-XJXQXXIO.js} +2 -2
  51. package/dist/doctor-SEOUFHSL.js +2 -0
  52. package/dist/{editor-chat-FUBQLMAQ.js → editor-chat-RVLCKKB7.js} +2 -2
  53. package/dist/{heal-YP7QI7D3.js → heal-YEQYWX2L.js} +2 -2
  54. package/dist/inbox-BIN2S5XN.js +2 -0
  55. package/dist/index.d.ts +830 -100
  56. package/dist/index.js +2 -2
  57. package/dist/index.js.map +1 -1
  58. package/dist/{ingest-whatsapp-C3O5UPBX.js → ingest-whatsapp-LZE62INA.js} +2 -2
  59. package/dist/loader-5CA3O5GM.js +2 -0
  60. package/dist/loader-6IHUCPVO.js +2 -0
  61. package/dist/memory-extract-KRCHD3OP.js +2 -0
  62. package/dist/migrate-v2-QO7SLKIM.js +4 -0
  63. package/dist/migrate-v2-QO7SLKIM.js.map +1 -0
  64. package/dist/processes-RJ6RDP32.js +2 -0
  65. package/dist/program-SASDC2GV.js +2 -0
  66. package/dist/{providers-WCQSKTNP.js → providers-WVCMIW67.js} +2 -2
  67. package/dist/query-HZYYRCLU.js +2 -0
  68. package/dist/recipes-EPLE2SNT.js +2 -0
  69. package/dist/registry-HM4ELSXS.js +2 -0
  70. package/dist/rotation-memo-GMTZPMNE.js +13 -0
  71. package/dist/rotation-memo-GMTZPMNE.js.map +1 -0
  72. package/dist/scheduler-XVFIK4XH.js +2 -0
  73. package/dist/{slack-D27BE37Q.js → slack-6P7ZJ32Y.js} +2 -2
  74. package/dist/store-BAPDZHXE.js +2 -0
  75. package/dist/{subagent-EPOT3QDM.js → subagent-WV7QKQ3L.js} +2 -2
  76. package/dist/{task-store-3NUWLYRY.js → task-store-RHISEU6V.js} +2 -2
  77. package/dist/{telegram-UWV67KWG.js → telegram-PIYDRM46.js} +2 -2
  78. package/dist/timers-5LPZN55J.js +2 -0
  79. package/dist/types-VECTMYTO.js +2 -0
  80. package/dist/types-VECTMYTO.js.map +1 -0
  81. package/dist/types-ZRLHHCGB.js +2 -0
  82. package/dist/types-ZRLHHCGB.js.map +1 -0
  83. package/dist/web/activity-graph.global.js +654 -0
  84. package/dist/web/activity-graph.global.js.map +1 -0
  85. package/dist/web/workflow-editor.global.js +1 -1
  86. package/dist/web/workflow-editor.global.js.map +1 -1
  87. package/dist/{whatsapp-KJ2NRKKQ.js → whatsapp-PFRDMUMT.js} +2 -2
  88. package/dist/{whatsapp-state-3ZVQ6MQD.js → whatsapp-state-XMTCSRXU.js} +2 -2
  89. package/dist/wiki-53W5NLZV.js +2 -0
  90. package/dist/wiki-53W5NLZV.js.map +1 -0
  91. package/dist/workspace-setup-FPGN5VNY.js +2 -0
  92. package/dist/workspace-setup-FPGN5VNY.js.map +1 -0
  93. package/package.json +3 -2
  94. package/dist/board-dashboard-KX2CNOSY.js.map +0 -1
  95. package/dist/chunk-34NG7UDX.js +0 -11
  96. package/dist/chunk-34NG7UDX.js.map +0 -1
  97. package/dist/chunk-ASSJ4Z6Z.js +0 -164
  98. package/dist/chunk-ASSJ4Z6Z.js.map +0 -1
  99. package/dist/chunk-BLUXL6HR.js +0 -854
  100. package/dist/chunk-BLUXL6HR.js.map +0 -1
  101. package/dist/chunk-BTAEYL3P.js +0 -72
  102. package/dist/chunk-BTAEYL3P.js.map +0 -1
  103. package/dist/chunk-DSCMCVTV.js +0 -7
  104. package/dist/chunk-DSCMCVTV.js.map +0 -1
  105. package/dist/chunk-HPDHNIA3.js +0 -2
  106. package/dist/chunk-IM376SD6.js +0 -13
  107. package/dist/chunk-IM376SD6.js.map +0 -1
  108. package/dist/chunk-LEIYC3BF.js +0 -104
  109. package/dist/chunk-LEIYC3BF.js.map +0 -1
  110. package/dist/chunk-LY7JGDV7.js +0 -6
  111. package/dist/chunk-LY7JGDV7.js.map +0 -1
  112. package/dist/chunk-U364GNQX.js +0 -719
  113. package/dist/chunk-U364GNQX.js.map +0 -1
  114. package/dist/chunk-WZEYII2S.js +0 -6
  115. package/dist/chunk-WZEYII2S.js.map +0 -1
  116. package/dist/config-UOHEG4BW.js +0 -2
  117. package/dist/config-mutator-KSEBWKJH.js +0 -2
  118. package/dist/debug-TGAP7E7W.js +0 -2
  119. package/dist/inbox-O5UB3YQN.js.map +0 -1
  120. package/dist/loader-BGWA7IRY.js +0 -2
  121. package/dist/loader-LBHKSXBK.js +0 -2
  122. package/dist/memory-extract-KKPKAOPH.js +0 -2
  123. package/dist/program-2MDELQLX.js +0 -2
  124. package/dist/query-FZAL7AQD.js +0 -2
  125. package/dist/recipes-LZ56TA3X.js +0 -2
  126. package/dist/registry-ONJSILH4.js +0 -2
  127. package/dist/scheduler-XRM2RJIV.js +0 -2
  128. package/dist/store-4UCO56K2.js +0 -2
  129. package/dist/timers-QB4KYNAE.js +0 -2
  130. package/dist/types-ECMEC5BZ.js +0 -2
  131. package/dist/types-TTB6DJUE.js +0 -2
  132. package/dist/usage-dashboard-FQGPPABD.js +0 -803
  133. package/dist/usage-dashboard-FQGPPABD.js.map +0 -1
  134. package/dist/wiki-OUYKM7MB.js +0 -2
  135. package/dist/workspace-setup-6GDBEORT.js +0 -2
  136. /package/dist/{agent-RDVGL2TW.js.map → agent-57MG4LBM.js.map} +0 -0
  137. /package/dist/{audit-Z47AQFFD.js.map → audit-JU3HWISB.js.map} +0 -0
  138. /package/dist/{chunk-HPDHNIA3.js.map → chunk-4YCH6IZV.js.map} +0 -0
  139. /package/dist/{chunk-J4WKMKTP.js.map → chunk-DLVOHCTA.js.map} +0 -0
  140. /package/dist/{chunk-TRQSP67X.js.map → chunk-IKBHHTIT.js.map} +0 -0
  141. /package/dist/{chunk-JXEPJ3YD.js.map → chunk-TC3BSUP7.js.map} +0 -0
  142. /package/dist/{config-UOHEG4BW.js.map → chunk-VWTBHLMX.js.map} +0 -0
  143. /package/dist/{compaction-S5H6DTOR.js.map → compaction-BTUJJ3LJ.js.map} +0 -0
  144. /package/dist/{config-mutator-KSEBWKJH.js.map → config-QXWKEQON.js.map} +0 -0
  145. /package/dist/{debug-TGAP7E7W.js.map → config-mutator-53XGR2NT.js.map} +0 -0
  146. /package/dist/{context-planner-2RGHJHSO.js.map → context-planner-I7ID7XF3.js.map} +0 -0
  147. /package/dist/{heal-YP7QI7D3.js.map → debug-N3B5NVJU.js.map} +0 -0
  148. /package/dist/{discover-OATU7SHI.js.map → discover-XJXQXXIO.js.map} +0 -0
  149. /package/dist/{loader-BGWA7IRY.js.map → doctor-SEOUFHSL.js.map} +0 -0
  150. /package/dist/{editor-chat-FUBQLMAQ.js.map → editor-chat-RVLCKKB7.js.map} +0 -0
  151. /package/dist/{loader-LBHKSXBK.js.map → heal-YEQYWX2L.js.map} +0 -0
  152. /package/dist/{memory-extract-KKPKAOPH.js.map → inbox-BIN2S5XN.js.map} +0 -0
  153. /package/dist/{ingest-whatsapp-C3O5UPBX.js.map → ingest-whatsapp-LZE62INA.js.map} +0 -0
  154. /package/dist/{program-2MDELQLX.js.map → loader-5CA3O5GM.js.map} +0 -0
  155. /package/dist/{providers-WCQSKTNP.js.map → loader-6IHUCPVO.js.map} +0 -0
  156. /package/dist/{query-FZAL7AQD.js.map → memory-extract-KRCHD3OP.js.map} +0 -0
  157. /package/dist/{recipes-LZ56TA3X.js.map → processes-RJ6RDP32.js.map} +0 -0
  158. /package/dist/{registry-ONJSILH4.js.map → program-SASDC2GV.js.map} +0 -0
  159. /package/dist/{scheduler-XRM2RJIV.js.map → providers-WVCMIW67.js.map} +0 -0
  160. /package/dist/{store-4UCO56K2.js.map → query-HZYYRCLU.js.map} +0 -0
  161. /package/dist/{task-store-3NUWLYRY.js.map → recipes-EPLE2SNT.js.map} +0 -0
  162. /package/dist/{timers-QB4KYNAE.js.map → registry-HM4ELSXS.js.map} +0 -0
  163. /package/dist/{types-ECMEC5BZ.js.map → scheduler-XVFIK4XH.js.map} +0 -0
  164. /package/dist/{slack-D27BE37Q.js.map → slack-6P7ZJ32Y.js.map} +0 -0
  165. /package/dist/{types-TTB6DJUE.js.map → store-BAPDZHXE.js.map} +0 -0
  166. /package/dist/{subagent-EPOT3QDM.js.map → subagent-WV7QKQ3L.js.map} +0 -0
  167. /package/dist/{wiki-OUYKM7MB.js.map → task-store-RHISEU6V.js.map} +0 -0
  168. /package/dist/{telegram-UWV67KWG.js.map → telegram-PIYDRM46.js.map} +0 -0
  169. /package/dist/{workspace-setup-6GDBEORT.js.map → timers-5LPZN55J.js.map} +0 -0
  170. /package/dist/{whatsapp-KJ2NRKKQ.js.map → whatsapp-PFRDMUMT.js.map} +0 -0
  171. /package/dist/{whatsapp-state-3ZVQ6MQD.js.map → whatsapp-state-XMTCSRXU.js.map} +0 -0
package/dist/index.d.ts CHANGED
@@ -1314,6 +1314,25 @@ declare const agentConfigSchema: z.ZodObject<{
1314
1314
  model: z.ZodOptional<z.ZodString>;
1315
1315
  systemPrompt: z.ZodOptional<z.ZodString>;
1316
1316
  mentions: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
1317
+ /** Phase 5 — typed capabilities (drop-condition fallback). Free-form
1318
+ * list of intent strings this agent is allowed to handle. When set,
1319
+ * the org-chart `canHandle(agentId, project, intent)` check rejects
1320
+ * dispatches with intents not in this list. When empty/unset
1321
+ * (the default), the agent is permissive — handles any intent.
1322
+ * Intent matching is exact-string for now; a glob/prefix layer can
1323
+ * come later if it produces real rejections.
1324
+ * Example: ["issue.opened", "issue.commented", "merge_request.opened"]
1325
+ * Example: ["cron.fired", "message.received"] */
1326
+ intents: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
1327
+ /** Phase 8 — capability-bounded security. Max distinct upstream
1328
+ * agents in the delegation chain on the same (project, subject)
1329
+ * before a dispatch to THIS agent is refused. The ledger itself
1330
+ * provides the chain — `decideAndCommit` walks recent decisions on
1331
+ * the subject and counts distinct agents. Default 5; set to 0 to
1332
+ * disable for an agent that's always called as the bottom of a
1333
+ * chain. The check prevents cascade loops where A → B → A → B → ...
1334
+ * blows past sane chain depth. */
1335
+ maxDelegationDepth: z.ZodDefault<z.ZodNumber>;
1317
1336
  /** MCP servers this agent's Claude Code session should load. Synced
1318
1337
  * to <workspace>/.mcp.json at daemon boot via agent-mcp.ts. Operator
1319
1338
  * edits to .mcp.json are respected (see SyncResult.skipped-operator-owned). */
@@ -1374,6 +1393,8 @@ declare const agentConfigSchema: z.ZodObject<{
1374
1393
  workspace: string;
1375
1394
  tier: "claude-code" | "sdk" | "orchestrator";
1376
1395
  mentions: string[];
1396
+ intents: string[];
1397
+ maxDelegationDepth: number;
1377
1398
  contextReferences: boolean;
1378
1399
  maxConcurrent: number;
1379
1400
  maxExecutionMinutes: number;
@@ -1403,6 +1424,8 @@ declare const agentConfigSchema: z.ZodObject<{
1403
1424
  tier?: "claude-code" | "sdk" | "orchestrator" | undefined;
1404
1425
  systemPrompt?: string | undefined;
1405
1426
  mentions?: string[] | undefined;
1427
+ intents?: string[] | undefined;
1428
+ maxDelegationDepth?: number | undefined;
1406
1429
  mcp?: Record<string, {
1407
1430
  command: string;
1408
1431
  env?: Record<string, string> | undefined;
@@ -1531,6 +1554,25 @@ declare const daemonConfigSchema: z.ZodObject<{
1531
1554
  model: z.ZodOptional<z.ZodString>;
1532
1555
  systemPrompt: z.ZodOptional<z.ZodString>;
1533
1556
  mentions: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
1557
+ /** Phase 5 — typed capabilities (drop-condition fallback). Free-form
1558
+ * list of intent strings this agent is allowed to handle. When set,
1559
+ * the org-chart `canHandle(agentId, project, intent)` check rejects
1560
+ * dispatches with intents not in this list. When empty/unset
1561
+ * (the default), the agent is permissive — handles any intent.
1562
+ * Intent matching is exact-string for now; a glob/prefix layer can
1563
+ * come later if it produces real rejections.
1564
+ * Example: ["issue.opened", "issue.commented", "merge_request.opened"]
1565
+ * Example: ["cron.fired", "message.received"] */
1566
+ intents: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
1567
+ /** Phase 8 — capability-bounded security. Max distinct upstream
1568
+ * agents in the delegation chain on the same (project, subject)
1569
+ * before a dispatch to THIS agent is refused. The ledger itself
1570
+ * provides the chain — `decideAndCommit` walks recent decisions on
1571
+ * the subject and counts distinct agents. Default 5; set to 0 to
1572
+ * disable for an agent that's always called as the bottom of a
1573
+ * chain. The check prevents cascade loops where A → B → A → B → ...
1574
+ * blows past sane chain depth. */
1575
+ maxDelegationDepth: z.ZodDefault<z.ZodNumber>;
1534
1576
  /** MCP servers this agent's Claude Code session should load. Synced
1535
1577
  * to <workspace>/.mcp.json at daemon boot via agent-mcp.ts. Operator
1536
1578
  * edits to .mcp.json are respected (see SyncResult.skipped-operator-owned). */
@@ -1591,6 +1633,8 @@ declare const daemonConfigSchema: z.ZodObject<{
1591
1633
  workspace: string;
1592
1634
  tier: "claude-code" | "sdk" | "orchestrator";
1593
1635
  mentions: string[];
1636
+ intents: string[];
1637
+ maxDelegationDepth: number;
1594
1638
  contextReferences: boolean;
1595
1639
  maxConcurrent: number;
1596
1640
  maxExecutionMinutes: number;
@@ -1620,6 +1664,8 @@ declare const daemonConfigSchema: z.ZodObject<{
1620
1664
  tier?: "claude-code" | "sdk" | "orchestrator" | undefined;
1621
1665
  systemPrompt?: string | undefined;
1622
1666
  mentions?: string[] | undefined;
1667
+ intents?: string[] | undefined;
1668
+ maxDelegationDepth?: number | undefined;
1623
1669
  mcp?: Record<string, {
1624
1670
  command: string;
1625
1671
  env?: Record<string, string> | undefined;
@@ -2802,10 +2848,53 @@ declare const daemonConfigSchema: z.ZodObject<{
2802
2848
  reportsTo?: string | undefined;
2803
2849
  utilizationTarget?: number | undefined;
2804
2850
  }>>>;
2851
+ projects: z.ZodDefault<z.ZodArray<z.ZodObject<{
2852
+ id: z.ZodString;
2853
+ pm: z.ZodOptional<z.ZodString>;
2854
+ client: z.ZodOptional<z.ZodString>;
2855
+ }, "strip", z.ZodTypeAny, {
2856
+ id: string;
2857
+ pm?: string | undefined;
2858
+ client?: string | undefined;
2859
+ }, {
2860
+ id: string;
2861
+ pm?: string | undefined;
2862
+ client?: string | undefined;
2863
+ }>, "many">>;
2864
+ contactMap: z.ZodDefault<z.ZodArray<z.ZodObject<{
2865
+ channel: z.ZodOptional<z.ZodString>;
2866
+ chatId: z.ZodOptional<z.ZodString>;
2867
+ username: z.ZodOptional<z.ZodString>;
2868
+ senderId: z.ZodOptional<z.ZodString>;
2869
+ client: z.ZodString;
2870
+ project: z.ZodOptional<z.ZodString>;
2871
+ displayName: z.ZodOptional<z.ZodString>;
2872
+ }, "strip", z.ZodTypeAny, {
2873
+ client: string;
2874
+ channel?: string | undefined;
2875
+ chatId?: string | undefined;
2876
+ username?: string | undefined;
2877
+ senderId?: string | undefined;
2878
+ project?: string | undefined;
2879
+ displayName?: string | undefined;
2880
+ }, {
2881
+ client: string;
2882
+ channel?: string | undefined;
2883
+ chatId?: string | undefined;
2884
+ username?: string | undefined;
2885
+ senderId?: string | undefined;
2886
+ project?: string | undefined;
2887
+ displayName?: string | undefined;
2888
+ }>, "many">>;
2805
2889
  workTickMinutes: z.ZodDefault<z.ZodNumber>;
2806
2890
  idleQueueThreshold: z.ZodDefault<z.ZodNumber>;
2807
2891
  }, "strip", z.ZodTypeAny, {
2808
2892
  enabled: boolean;
2893
+ projects: {
2894
+ id: string;
2895
+ pm?: string | undefined;
2896
+ client?: string | undefined;
2897
+ }[];
2809
2898
  timezone: string;
2810
2899
  mainChannel: {
2811
2900
  channel: string;
@@ -2843,6 +2932,15 @@ declare const daemonConfigSchema: z.ZodObject<{
2843
2932
  utilizationTarget: number;
2844
2933
  reportsTo?: string | undefined;
2845
2934
  }>;
2935
+ contactMap: {
2936
+ client: string;
2937
+ channel?: string | undefined;
2938
+ chatId?: string | undefined;
2939
+ username?: string | undefined;
2940
+ senderId?: string | undefined;
2941
+ project?: string | undefined;
2942
+ displayName?: string | undefined;
2943
+ }[];
2846
2944
  workTickMinutes: number;
2847
2945
  idleQueueThreshold: number;
2848
2946
  }, {
@@ -2863,6 +2961,11 @@ declare const daemonConfigSchema: z.ZodObject<{
2863
2961
  glob?: string | undefined;
2864
2962
  };
2865
2963
  enabled?: boolean | undefined;
2964
+ projects?: {
2965
+ id: string;
2966
+ pm?: string | undefined;
2967
+ client?: string | undefined;
2968
+ }[] | undefined;
2866
2969
  timezone?: string | undefined;
2867
2970
  roles?: Record<string, {
2868
2971
  title: string;
@@ -2884,6 +2987,15 @@ declare const daemonConfigSchema: z.ZodObject<{
2884
2987
  reportsTo?: string | undefined;
2885
2988
  utilizationTarget?: number | undefined;
2886
2989
  }> | undefined;
2990
+ contactMap?: {
2991
+ client: string;
2992
+ channel?: string | undefined;
2993
+ chatId?: string | undefined;
2994
+ username?: string | undefined;
2995
+ senderId?: string | undefined;
2996
+ project?: string | undefined;
2997
+ displayName?: string | undefined;
2998
+ }[] | undefined;
2887
2999
  workTickMinutes?: number | undefined;
2888
3000
  idleQueueThreshold?: number | undefined;
2889
3001
  }>>;
@@ -3107,6 +3219,11 @@ declare const daemonConfigSchema: z.ZodObject<{
3107
3219
  * is driven by `autoApproveStructure` alone. Lower it to e.g. 0.7 if you
3108
3220
  * also want high-confidence structural changes auto-approved. */
3109
3221
  autoApproveConfidence: z.ZodDefault<z.ZodNumber>;
3222
+ /** Anthropic model id used for the direct classifier call (Phase 2 of
3223
+ * classifier-retire). Default haiku — classification is metadata, not
3224
+ * work, so we use the cheapest fast model. Override only if Haiku is
3225
+ * rate-limited or you want to A/B test. */
3226
+ classifierModel: z.ZodDefault<z.ZodString>;
3110
3227
  /** Weights for the wiki hybrid retrieval score. Path-ancestry match vs
3111
3228
  * BM25 over article text. Sum need not be 1. */
3112
3229
  retrievalWeights: z.ZodDefault<z.ZodObject<{
@@ -3124,6 +3241,7 @@ declare const daemonConfigSchema: z.ZodObject<{
3124
3241
  baseDir: string;
3125
3242
  autoApproveStructure: "any" | "strict" | "extend-leaves";
3126
3243
  autoApproveConfidence: number;
3244
+ classifierModel: string;
3127
3245
  retrievalWeights: {
3128
3246
  graph: number;
3129
3247
  bm25: number;
@@ -3137,6 +3255,7 @@ declare const daemonConfigSchema: z.ZodObject<{
3137
3255
  reviewAgent?: string | undefined;
3138
3256
  autoApproveStructure?: "any" | "strict" | "extend-leaves" | undefined;
3139
3257
  autoApproveConfidence?: number | undefined;
3258
+ classifierModel?: string | undefined;
3140
3259
  retrievalWeights?: {
3141
3260
  graph?: number | undefined;
3142
3261
  bm25?: number | undefined;
@@ -3260,12 +3379,22 @@ declare const daemonConfigSchema: z.ZodObject<{
3260
3379
  maxClaudeCodeDispatchesPerHour?: number | undefined;
3261
3380
  maxClaudeCodeDispatchesPer5h?: number | undefined;
3262
3381
  }>>;
3382
+ /** Move B — JS/TS plugins. Each entry is an installed npm package name
3383
+ * (e.g. `agentx-plugin-mattermost` or `@noqta/plugin-mattermost`); the
3384
+ * loader does a dynamic `import(name)` at boot, validates the manifest,
3385
+ * and calls plugin.setup(ctx). Plugins can register channel adapters
3386
+ * via ctx.addChannel() and subscribe to bus events via ctx.on(). Empty
3387
+ * default — installs that don't list plugins are unaffected. Plugin
3388
+ * authoring guide: docs/architecture/plugins.md. */
3389
+ plugins: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
3263
3390
  }, "strip", z.ZodTypeAny, {
3264
3391
  agents: Record<string, {
3265
3392
  name: string;
3266
3393
  workspace: string;
3267
3394
  tier: "claude-code" | "sdk" | "orchestrator";
3268
3395
  mentions: string[];
3396
+ intents: string[];
3397
+ maxDelegationDepth: number;
3269
3398
  contextReferences: boolean;
3270
3399
  maxConcurrent: number;
3271
3400
  maxExecutionMinutes: number;
@@ -3312,6 +3441,7 @@ declare const daemonConfigSchema: z.ZodObject<{
3312
3441
  baseDir: string;
3313
3442
  autoApproveStructure: "any" | "strict" | "extend-leaves";
3314
3443
  autoApproveConfidence: number;
3444
+ classifierModel: string;
3315
3445
  retrievalWeights: {
3316
3446
  graph: number;
3317
3447
  bm25: number;
@@ -3564,8 +3694,14 @@ declare const daemonConfigSchema: z.ZodObject<{
3564
3694
  maxClaudeCodeDispatchesPerHour: number;
3565
3695
  maxClaudeCodeDispatchesPer5h: number;
3566
3696
  };
3697
+ plugins: string[];
3567
3698
  business?: {
3568
3699
  enabled: boolean;
3700
+ projects: {
3701
+ id: string;
3702
+ pm?: string | undefined;
3703
+ client?: string | undefined;
3704
+ }[];
3569
3705
  timezone: string;
3570
3706
  mainChannel: {
3571
3707
  channel: string;
@@ -3603,6 +3739,15 @@ declare const daemonConfigSchema: z.ZodObject<{
3603
3739
  utilizationTarget: number;
3604
3740
  reportsTo?: string | undefined;
3605
3741
  }>;
3742
+ contactMap: {
3743
+ client: string;
3744
+ channel?: string | undefined;
3745
+ chatId?: string | undefined;
3746
+ username?: string | undefined;
3747
+ senderId?: string | undefined;
3748
+ project?: string | undefined;
3749
+ displayName?: string | undefined;
3750
+ }[];
3606
3751
  workTickMinutes: number;
3607
3752
  idleQueueThreshold: number;
3608
3753
  } | undefined;
@@ -3621,6 +3766,8 @@ declare const daemonConfigSchema: z.ZodObject<{
3621
3766
  tier?: "claude-code" | "sdk" | "orchestrator" | undefined;
3622
3767
  systemPrompt?: string | undefined;
3623
3768
  mentions?: string[] | undefined;
3769
+ intents?: string[] | undefined;
3770
+ maxDelegationDepth?: number | undefined;
3624
3771
  mcp?: Record<string, {
3625
3772
  command: string;
3626
3773
  env?: Record<string, string> | undefined;
@@ -3660,6 +3807,7 @@ declare const daemonConfigSchema: z.ZodObject<{
3660
3807
  reviewAgent?: string | undefined;
3661
3808
  autoApproveStructure?: "any" | "strict" | "extend-leaves" | undefined;
3662
3809
  autoApproveConfidence?: number | undefined;
3810
+ classifierModel?: string | undefined;
3663
3811
  retrievalWeights?: {
3664
3812
  graph?: number | undefined;
3665
3813
  bm25?: number | undefined;
@@ -3857,6 +4005,11 @@ declare const daemonConfigSchema: z.ZodObject<{
3857
4005
  glob?: string | undefined;
3858
4006
  };
3859
4007
  enabled?: boolean | undefined;
4008
+ projects?: {
4009
+ id: string;
4010
+ pm?: string | undefined;
4011
+ client?: string | undefined;
4012
+ }[] | undefined;
3860
4013
  timezone?: string | undefined;
3861
4014
  roles?: Record<string, {
3862
4015
  title: string;
@@ -3878,6 +4031,15 @@ declare const daemonConfigSchema: z.ZodObject<{
3878
4031
  reportsTo?: string | undefined;
3879
4032
  utilizationTarget?: number | undefined;
3880
4033
  }> | undefined;
4034
+ contactMap?: {
4035
+ client: string;
4036
+ channel?: string | undefined;
4037
+ chatId?: string | undefined;
4038
+ username?: string | undefined;
4039
+ senderId?: string | undefined;
4040
+ project?: string | undefined;
4041
+ displayName?: string | undefined;
4042
+ }[] | undefined;
3881
4043
  workTickMinutes?: number | undefined;
3882
4044
  idleQueueThreshold?: number | undefined;
3883
4045
  } | undefined;
@@ -3952,6 +4114,7 @@ declare const daemonConfigSchema: z.ZodObject<{
3952
4114
  maxClaudeCodeDispatchesPerHour?: number | undefined;
3953
4115
  maxClaudeCodeDispatchesPer5h?: number | undefined;
3954
4116
  } | undefined;
4117
+ plugins?: string[] | undefined;
3955
4118
  }>;
3956
4119
  type DaemonConfig = z.infer<typeof daemonConfigSchema>;
3957
4120
  type AgentDef = z.infer<typeof agentConfigSchema>;
@@ -3985,6 +4148,14 @@ declare class A2AMesh {
3985
4148
  private healthTimer?;
3986
4149
  private config;
3987
4150
  private log;
4151
+ /** Optional callback fired on peer state transitions (recovered /
4152
+ * lost / skills changed). Daemon wires this to the event bus for
4153
+ * live operator visibility. */
4154
+ private peerChangeCallback?;
4155
+ /** Register a listener for peer state transitions. Only one listener
4156
+ * is kept — repeat calls overwrite. Used by the daemon to bridge
4157
+ * into the EventBus without introducing a circular dependency. */
4158
+ onPeerChange(cb: typeof A2AMesh.prototype.peerChangeCallback): void;
3988
4159
  constructor(config: DaemonConfig, log?: (...args: unknown[]) => void);
3989
4160
  /**
3990
4161
  * Start the mesh: discover peers and begin health checks.
@@ -4035,6 +4206,21 @@ declare class A2AMesh {
4035
4206
  */
4036
4207
  sendTask(peerName: string, text: string, agentId?: string, opts?: {
4037
4208
  timeoutMs?: number;
4209
+ /** Identity of the agent on whose behalf this call is made. Forwarded
4210
+ * in the request body so the receiving daemon can record it in
4211
+ * route_traces and (in a future protocol revision) validate that the
4212
+ * caller is allowed to act for this agent. Optional during the
4213
+ * log-warn rollout; missing values produce a server-side warning. */
4214
+ senderAgentId?: string;
4215
+ /** Origin context — channel, chatId, sender, channelMeta, etc. Forwarded
4216
+ * to the receiving daemon's /task handler verbatim so the receiver's
4217
+ * registry.execute() keys the session by the SAME (channel, chatId)
4218
+ * the sender used, instead of falling back to api/default. Without
4219
+ * this, a GitLab webhook routed across the mesh lands in the
4220
+ * recipient's api:default bucket with no project, no issue id, no
4221
+ * channelMeta — i.e., the mtgl/hasanah confusion incident on
4222
+ * 2026-04-29 issue #709. Shape matches AgentTask.context. */
4223
+ context?: Record<string, unknown>;
4038
4224
  }): Promise<string>;
4039
4225
  /**
4040
4226
  * Forward a WebRTC signaling message (SDP / ICE / hangup) to a remote peer's
@@ -4075,6 +4261,99 @@ declare class A2AMesh {
4075
4261
  }>;
4076
4262
  }
4077
4263
 
4264
+ type EventKind = "run" | "task" | "signal" | "mesh" | "channel" | "status";
4265
+ interface BaseEvent {
4266
+ kind: EventKind;
4267
+ /** Monotonic-ish identifier; just for debug/replay. */
4268
+ id: string;
4269
+ /** ISO-8601 emit time. */
4270
+ at: string;
4271
+ }
4272
+ interface RunEvent extends BaseEvent {
4273
+ kind: "run";
4274
+ runId: string;
4275
+ workflowId: string;
4276
+ /** Node that just transitioned. Optional for run-created events. */
4277
+ nodeId?: string;
4278
+ /** "created" | "ok" | "failed" | "paused" | "resumed" | "skipped" | "completed" | "timeout". */
4279
+ phase: string;
4280
+ /** Current run status after the transition. */
4281
+ status?: string;
4282
+ homeNode?: string;
4283
+ note?: string;
4284
+ }
4285
+ interface TaskEvent extends BaseEvent {
4286
+ kind: "task";
4287
+ taskId: string;
4288
+ workflowId: string;
4289
+ runId: string;
4290
+ phase: "created" | "submitted" | "canceled";
4291
+ assignedTo?: string[];
4292
+ title?: string;
4293
+ submittedBy?: string;
4294
+ }
4295
+ interface SignalEvent extends BaseEvent {
4296
+ kind: "signal";
4297
+ name: string;
4298
+ scope: "workflow" | "global";
4299
+ workflowId?: string;
4300
+ payload?: Record<string, unknown>;
4301
+ }
4302
+ interface MeshEvent extends BaseEvent {
4303
+ kind: "mesh";
4304
+ peer: string;
4305
+ healthy: boolean;
4306
+ skills?: string[];
4307
+ delta: "recovered" | "lost" | "skills-changed" | "added" | "removed";
4308
+ }
4309
+ interface ChannelEvent extends BaseEvent {
4310
+ kind: "channel";
4311
+ channel: string;
4312
+ direction: "in" | "out";
4313
+ chatId?: string;
4314
+ sender?: string;
4315
+ agentId?: string;
4316
+ textPreview?: string;
4317
+ messageId?: string;
4318
+ }
4319
+ interface StatusEvent extends BaseEvent {
4320
+ kind: "status";
4321
+ message: string;
4322
+ meta?: Record<string, unknown>;
4323
+ }
4324
+ type DaemonEvent = RunEvent | TaskEvent | SignalEvent | MeshEvent | ChannelEvent | StatusEvent;
4325
+ interface SubscriptionFilter {
4326
+ /** Match only these kinds. Empty / omitted = all kinds. */
4327
+ kinds?: EventKind[];
4328
+ /** Workflow id filter (applies to run/task/signal events). */
4329
+ workflowId?: string;
4330
+ /** Actor id filter (task.assignedTo includes this, or task.submittedBy equals). */
4331
+ actor?: string;
4332
+ /** Channel filter (for channel events). */
4333
+ channel?: string;
4334
+ /** Run id filter (run events for a specific run). */
4335
+ runId?: string;
4336
+ }
4337
+ type Listener = (e: DaemonEvent) => void;
4338
+ /** Simple in-memory publish/subscribe. No ordering guarantees beyond
4339
+ * "subscribers see events in the order they were published". No
4340
+ * durability. Listener callbacks are invoked synchronously; if a
4341
+ * listener throws, the error is swallowed so a single bad subscriber
4342
+ * can't break the fanout. */
4343
+ declare class EventBus {
4344
+ private listeners;
4345
+ subscribe(filter: SubscriptionFilter, fn: Listener): () => void;
4346
+ /** Publish an event. The `id` and `at` fields are filled in if absent.
4347
+ * We deliberately accept a loose input shape — callers construct the
4348
+ * per-kind literal and we trust them; runtime validation happens at
4349
+ * the SSE serialisation layer. */
4350
+ publish(event: {
4351
+ kind: EventKind;
4352
+ } & Record<string, unknown>): void;
4353
+ /** Active subscriber count — useful for health endpoints. */
4354
+ get size(): number;
4355
+ }
4356
+
4078
4357
  declare class AgentXDaemon {
4079
4358
  private config;
4080
4359
  private registry;
@@ -4093,12 +4372,11 @@ declare class AgentXDaemon {
4093
4372
  private workflowDispatcher?;
4094
4373
  private workflowStore?;
4095
4374
  private workflowRuns?;
4096
- /** Structured per-agent memory (Claude-Code-style user / feedback /
4097
- * project / reference). Deliberately owned by the daemon (not the
4098
- * registry) so HTTP callers — including an agent curling from its
4099
- * own Claude Code session — can write without going through the
4100
- * dispatcher. Inlined into agent system prompts happens elsewhere. */
4375
+ private db;
4376
+ private loadedPlugins;
4101
4377
  private readonly agentMemory;
4378
+ private contacts;
4379
+ readonly events: EventBus;
4102
4380
  private log;
4103
4381
  private sseClients;
4104
4382
  private configPath?;
@@ -4174,6 +4452,14 @@ declare class AgentXDaemon {
4174
4452
  */
4175
4453
  private handleOpenAICompat;
4176
4454
  private json;
4455
+ /** Pick today's usage rollup according to AGENTX_USAGE_READ:
4456
+ * - "sqlite": SQLite only (empty if db null / no rows)
4457
+ * - "json": JSON only (legacy behaviour)
4458
+ * - "sqlite-then-json" (default): try SQLite, fall back to JSON when
4459
+ * the rollup is empty (no agents seen today). Transparent for
4460
+ * operators on first deploy where SQLite is empty until the first
4461
+ * task completes. */
4462
+ private resolveTodayUsage;
4177
4463
  /** Boot-time: install the `remember` skill into every agent workspace
4178
4464
  * that doesn't already have it, and sync each agent's existing memory
4179
4465
  * into <workspace>/CLAUDE.md + .agentx-memory.md. Idempotent — safe to
@@ -4429,6 +4715,19 @@ interface AgentTask {
4429
4715
  * supports it. Workflow `agent` nodes pass this through from their
4430
4716
  * config.timeoutMinutes. */
4431
4717
  timeoutMinutes?: number;
4718
+ /** Phase 1 / 6 — when set, identifies the intent-ledger decision row
4719
+ * this task corresponds to. The registry records a resolution row
4720
+ * on this decision when the task completes (success / error / mesh-
4721
+ * fallback). Active-task safety in `decideAndCommit` reads
4722
+ * intent_resolutions to clear in-flight slots; without resolution
4723
+ * writes, every dispatched decision sits in-flight forever and
4724
+ * Inv-ActiveTaskSafety becomes vacuously over-aggressive. Channel
4725
+ * adapters (gitlab, router) set this when their record*Dispatch
4726
+ * helper returns a dispatched decision. */
4727
+ intentRef?: {
4728
+ eventId: string;
4729
+ decidedBy: string;
4730
+ };
4432
4731
  context?: {
4433
4732
  channel?: string;
4434
4733
  sender?: string;
@@ -4588,11 +4887,156 @@ declare class MemoryStore {
4588
4887
  prune(agentId: string): void;
4589
4888
  }
4590
4889
 
4890
+ /**
4891
+ * Verified channel context — facts the channel adapter knows for certain.
4892
+ * Prevents agents from hallucinating about their environment.
4893
+ */
4894
+ interface ChannelMeta {
4895
+ /** Channel name */
4896
+ channel: string;
4897
+ /** Verified agents/bots present in this chat/group/project */
4898
+ agents?: Array<{
4899
+ id: string;
4900
+ name: string;
4901
+ handle?: string;
4902
+ }>;
4903
+ /** Project path (GitLab) or group topic */
4904
+ project?: string;
4905
+ /** Issue/MR context (GitLab) */
4906
+ issue?: {
4907
+ type: string;
4908
+ iid: string;
4909
+ title: string;
4910
+ };
4911
+ /** Channel-specific facts (e.g., pipeline status, group description) */
4912
+ facts?: string[];
4913
+ }
4914
+ interface IncomingMessage {
4915
+ id: string;
4916
+ channel: string;
4917
+ accountId: string;
4918
+ sender: {
4919
+ id: string;
4920
+ name: string;
4921
+ username?: string;
4922
+ /** True when the origin is another bot account (Telegram `from.is_bot`).
4923
+ * Used by the router to apply strict @-mention-only routing — prevents
4924
+ * cross-daemon cascades from bare-word matches in bot replies. */
4925
+ isBot?: boolean;
4926
+ };
4927
+ group?: {
4928
+ id: string;
4929
+ name: string;
4930
+ };
4931
+ text: string;
4932
+ replyTo?: string;
4933
+ replyToText?: string;
4934
+ timestamp: Date;
4935
+ media?: {
4936
+ path: string;
4937
+ type: string;
4938
+ fileName?: string;
4939
+ };
4940
+ raw?: unknown;
4941
+ resolvedAgent?: string;
4942
+ preferNode?: string;
4943
+ channelMeta?: ChannelMeta;
4944
+ /** Correlator threaded by the workflow engine. When a workflow dispatches
4945
+ * an agent, this carries the run id through the pipeline so post:response
4946
+ * can re-enter the engine with `kind: agentResult` transitions. Opaque
4947
+ * everywhere outside src/workflows/. */
4948
+ workflowRunId?: string;
4949
+ /** Phase 1 / 6 — when set, identifies the intent-ledger decision row
4950
+ * this message corresponds to. The agent registry calls
4951
+ * `ledger.recordResolution(...)` when the dispatched task completes,
4952
+ * so Inv-ActiveTaskSafety's "decision in flight" check can clear.
4953
+ * Set by the channel adapter (gitlab, router) right after
4954
+ * recordGitLabTargetDispatch / recordRouterDispatch returns the
4955
+ * decision row. Undefined when the dispatch was deduped/halted by
4956
+ * the ledger (no resolution to write) or when the source is in
4957
+ * mode=off. */
4958
+ intentRef?: {
4959
+ eventId: string;
4960
+ decidedBy: string;
4961
+ };
4962
+ }
4963
+ interface OutgoingMessage {
4964
+ channel: string;
4965
+ chatId: string;
4966
+ text: string;
4967
+ replyTo?: string;
4968
+ parseMode?: "markdown" | "html" | "plain";
4969
+ agentId?: string;
4970
+ poll?: {
4971
+ name: string;
4972
+ values: string[];
4973
+ selectableCount?: number;
4974
+ };
4975
+ media?: {
4976
+ type: "image" | "document" | "audio" | "video";
4977
+ url: string;
4978
+ caption?: string;
4979
+ mimetype?: string;
4980
+ fileName?: string;
4981
+ };
4982
+ }
4983
+ interface ChannelAdapter {
4984
+ readonly name: string;
4985
+ /** Start polling or webhook listener */
4986
+ start(): Promise<void>;
4987
+ /** Stop the adapter gracefully */
4988
+ stop(): Promise<void>;
4989
+ /** Send a message back to a chat */
4990
+ send(msg: OutgoingMessage): Promise<string | void>;
4991
+ /** Edit an existing message (returns false if not supported) */
4992
+ editMessage?(chatId: string, messageId: string, text: string, parseMode?: string): Promise<boolean>;
4993
+ /** Send typing/action indicator */
4994
+ sendTyping?(chatId: string): Promise<void>;
4995
+ /** React to a message with an emoji (e.g., 👀 for seen) */
4996
+ react?(chatId: string, messageId: string, emoji?: string): Promise<void>;
4997
+ /** Register handler for incoming messages */
4998
+ onMessage(handler: (msg: IncomingMessage) => Promise<void>): void;
4999
+ /** Get verified context for a chat (optional — channels implement what they can) */
5000
+ getChannelMeta?(chatId: string): Promise<ChannelMeta | undefined>;
5001
+ /** Fetch the most recent messages from the live channel for cold-start
5002
+ * session seeding. Called once per (agent, channel, chatId, day) when a
5003
+ * fresh session is created — never on warm cache hits. Bounded by
5004
+ * maxMessages and maxChars (enforced by the caller). Channels return their
5005
+ * own externalId so SessionStore can dedup against re-seeds. */
5006
+ seedHistory?(chatId: string, opts: {
5007
+ sinceISO?: string;
5008
+ maxMessages: number;
5009
+ maxChars: number;
5010
+ }): Promise<Array<SeededMessage>>;
5011
+ }
5012
+ /** A message returned by seedHistory — flat, channel-agnostic. */
5013
+ interface SeededMessage {
5014
+ role: "user" | "agent";
5015
+ name: string;
5016
+ content: string;
5017
+ timestamp: string;
5018
+ externalId?: string;
5019
+ /** Optional channel-side account/bot identity that handled this message
5020
+ * (e.g., Telegram accountId "noqta_cx_bot"). Captured by adapters that
5021
+ * shadow-log outbound, so seeded history preserves the audit trail of
5022
+ * which bot identity actually sent each line. Renderers may ignore it. */
5023
+ accountId?: string;
5024
+ }
5025
+
4591
5026
  interface SessionMessage {
4592
5027
  role: "user" | "agent";
4593
5028
  name?: string;
4594
5029
  content: string;
4595
5030
  timestamp: string;
5031
+ /** True when this message was injected by SessionStore.seedFromAdapter()
5032
+ * reading the live channel on cold session create — never via the inbound
5033
+ * router pipeline. Marked so observers can distinguish seeded turns from
5034
+ * live ones; the model sees them identically in buildHistoryContext. */
5035
+ seeded?: boolean;
5036
+ /** Channel-side message id (Telegram update_id, GitLab note id, WhatsApp
5037
+ * key.id). Used to dedup re-seeds — if a message with this externalId is
5038
+ * already in the session, appendSeededMessages skips it. */
5039
+ externalId?: string;
4596
5040
  }
4597
5041
  interface Session {
4598
5042
  id: string;
@@ -4621,11 +5065,40 @@ declare class SessionStore {
4621
5065
  private staleMinutes;
4622
5066
  private maxTurnsPerSession;
4623
5067
  private tierTwoThresholdTokens;
5068
+ /** Resolves a channel name to its adapter — installed by the daemon after
5069
+ * channels are registered (see daemon/index.ts after router.register). The
5070
+ * cold-create branch consults it to call adapter.seedHistory(). Optional;
5071
+ * when unset, seeding is a no-op (back-compat for tests, CLI tools, and
5072
+ * any caller that constructs SessionStore standalone). */
5073
+ private adapterResolver?;
5074
+ /** In-flight seed promises keyed by sessionKey. Coalesces concurrent
5075
+ * cold-creates against the same (agent, channel, chatId, day) so we hit
5076
+ * the channel API exactly once per fresh session. */
5077
+ private seedingPromises;
4624
5078
  constructor(baseDir?: string, opts?: {
4625
5079
  staleMinutes?: number;
4626
5080
  maxTurnsPerSession?: number;
4627
5081
  tierTwoThresholdTokens?: number;
4628
5082
  });
5083
+ /** Install the channel-adapter resolver. Called once at daemon startup
5084
+ * after MessageRouter has registered every adapter. Hot-reload of channel
5085
+ * adapters re-uses the same router, so the resolver stays valid across
5086
+ * /reload cycles without re-installation. */
5087
+ setAdapterResolver(fn: (channel: string) => ChannelAdapter | undefined): void;
5088
+ /** Seed a fresh session from the live channel before any user/agent
5089
+ * messages get appended. No-op when:
5090
+ * - the session already has messages (warm cache or partially loaded)
5091
+ * - no adapter resolver is installed
5092
+ * - the channel adapter has no seedHistory method
5093
+ * Concurrent calls against the same key share one in-flight promise so
5094
+ * the channel API is hit exactly once per cold create. Errors are
5095
+ * swallowed — a seeding failure should never block a turn. */
5096
+ seedIfEmpty(agentId: string, channel: string, chatId: string): Promise<void>;
5097
+ /** Append seeded messages to a session in chronological order, deduping
5098
+ * by externalId against anything already present. Marks each entry with
5099
+ * `seeded: true` so observers can distinguish them; the model's prompt
5100
+ * (buildHistoryContext) renders them identically to live turns. */
5101
+ private appendSeededMessages;
4629
5102
  /**
4630
5103
  * Build a deterministic session key.
4631
5104
  */
@@ -4704,6 +5177,34 @@ declare class SessionStore {
4704
5177
  hasMore: boolean;
4705
5178
  totalScanned: number;
4706
5179
  };
5180
+ /** Cross-agent view of a chat. Aggregates session messages from EVERY
5181
+ * agent that has a session for `(channel, chatId)` in the requested
5182
+ * window, sorts oldest-first, and dedups exact (timestamp, role, name,
5183
+ * content) collisions (which happen when two agents in the same group
5184
+ * both record the same inbound).
5185
+ *
5186
+ * Differs from recallTurns in two ways:
5187
+ * 1. Not scoped to one agentId — answers "what's in this chat across
5188
+ * all agents", which is the question the cx→devops→marketing thread
5189
+ * on 2026-04-29 needed and didn't have.
5190
+ * 2. Returns oldest-first so the caller reads it like a transcript;
5191
+ * recallTurns returns newest-first because it paginates.
5192
+ *
5193
+ * Bounded by the same MAX_MESSAGES default as buildHistoryContext so
5194
+ * callers don't accidentally pull a 5000-turn group into one prompt. */
5195
+ recentByChatId(opts: {
5196
+ channel: string;
5197
+ chatId: string;
5198
+ sinceISO?: string;
5199
+ limit?: number;
5200
+ }): Array<{
5201
+ ts: string;
5202
+ role: "user" | "agent";
5203
+ senderName: string;
5204
+ content: string;
5205
+ agentId: string;
5206
+ sessionFile: string;
5207
+ }>;
4707
5208
  /**
4708
5209
  * Check if a Claude session is stale (idle too long for --resume to be useful).
4709
5210
  * When stale, the resumed context will be too far behind — better to start fresh.
@@ -5559,11 +6060,29 @@ declare const indexFileSchema: z.ZodObject<{
5559
6060
  type IndexFile = z.infer<typeof indexFileSchema>;
5560
6061
 
5561
6062
  /**
5562
- * Ships with the install. When .agentx/graph/schema.json is missing, the
5563
- * store seeds this so a fresh daemon has something to classify against.
6063
+ * Ships with the install. When .agentx/graph/schema.json is missing,
6064
+ * the store seeds this so a fresh daemon has something to classify
6065
+ * against.
6066
+ *
6067
+ * v2 — Phase 1 of the classifier-retire plan: verb-level intent
6068
+ * taxonomy. The classifier now answers ONLY "what kind of work is
6069
+ * this" — it does not embed client/project/agent in the leaf because
6070
+ * those already live on the event metadata (project, subject,
6071
+ * agentId). One copy of "review.merge-request" instead of one per
6072
+ * client.
5564
6073
  *
5565
- * Five levels, three primaries at the leaf (who / what / where) — matches
5566
- * the "primary colors per level" design from the plan.
6074
+ * Two levels:
6075
+ * category — broad work area (code/ops/support/admin/knowledge/social/system)
6076
+ * verb — specific intent (review.merge-request, deploy.staging, etc.)
6077
+ *
6078
+ * The category is closed-set; the verb is open-set so the operator
6079
+ * can add new ones via `agentx graph review` over time. Most paths
6080
+ * will reuse the seeded verbs below.
6081
+ *
6082
+ * Why so few levels: every level is information the classifier
6083
+ * actually has to decide. The previous v1 schema (scope > location >
6084
+ * org > unit > activity) re-encoded data already on the event —
6085
+ * forcing a 5-LLM-question prompt to recover what we already knew.
5567
6086
  */
5568
6087
  declare const STARTER_SCHEMA: GraphSchema;
5569
6088
 
@@ -5579,7 +6098,13 @@ declare class GraphStore {
5579
6098
  nodesPath(): string;
5580
6099
  indexPath(): string;
5581
6100
  classificationsPath(): string;
5582
- /** Load the graph schema, seeding STARTER_SCHEMA if the file is missing. */
6101
+ /** Load the graph schema, seeding STARTER_SCHEMA if the file is missing.
6102
+ * Phase 1 of the classifier-retire plan ships a v2 verb-level schema;
6103
+ * installs with the v1 hierarchical schema (scope > org > unit >
6104
+ * activity) get auto-migrated on next load: the v1 file is renamed
6105
+ * to schema.v1.bak.json, the v2 starter is written, and the
6106
+ * per-event nodes.json + classifications.jsonl are left alone for
6107
+ * historical reading. New events classify under v2 from then on. */
5583
6108
  loadSchema(): GraphSchema;
5584
6109
  saveSchema(schema: GraphSchema): void;
5585
6110
  loadNodes(): NodesFile;
@@ -5655,6 +6180,65 @@ declare class GraphStore {
5655
6180
  rejectClassification(c: Classification): void;
5656
6181
  }
5657
6182
 
6183
+ type MemoryType = "user" | "feedback" | "project" | "reference";
6184
+ interface MemoryRecord {
6185
+ /** Slug-safe name, unique within an agent. */
6186
+ name: string;
6187
+ type: MemoryType;
6188
+ /** One-line hook — used as the MEMORY.md index entry. */
6189
+ description: string;
6190
+ /** The body, excluding frontmatter. */
6191
+ body: string;
6192
+ createdAt: string;
6193
+ updatedAt: string;
6194
+ }
6195
+ interface AgentMemoryOptions {
6196
+ baseDir?: string;
6197
+ }
6198
+ declare class AgentMemory {
6199
+ readonly baseDir: string;
6200
+ constructor(opts?: AgentMemoryOptions);
6201
+ private dirFor;
6202
+ private fileFor;
6203
+ private indexPath;
6204
+ /** Save (create or update) a memory. Keeps createdAt stable across
6205
+ * updates; bumps updatedAt. Always rewrites MEMORY.md. */
6206
+ save(args: {
6207
+ agentId: string;
6208
+ type: MemoryType;
6209
+ name: string;
6210
+ description: string;
6211
+ body: string;
6212
+ }): MemoryRecord;
6213
+ get(agentId: string, name: string): MemoryRecord | null;
6214
+ /** Every memory for an agent. Sorted by type then name for stable
6215
+ * index rendering. */
6216
+ list(agentId: string): MemoryRecord[];
6217
+ /** Remove a memory by name. Returns true on success, false if none found. */
6218
+ remove(agentId: string, name: string): boolean;
6219
+ /** The content of MEMORY.md — used at prompt-build time to inline into
6220
+ * the agent's system prompt. Returns "" when the agent has no
6221
+ * memories (so the prompt builder can no-op). */
6222
+ indexMarkdown(agentId: string): string;
6223
+ /** Push the agent's memory into its workspace so Claude Code sessions
6224
+ * pick it up automatically. Does two things:
6225
+ *
6226
+ * 1. Writes `<workspace>/.agentx-memory.md` — full MEMORY.md content,
6227
+ * explicit file the skill can `Read` at session start.
6228
+ * 2. Merges that same content into `<workspace>/CLAUDE.md` between
6229
+ * sentinel comments so Claude Code auto-loads it into the cached
6230
+ * system context on every session. Idempotent: subsequent syncs
6231
+ * replace the sentinel block.
6232
+ *
6233
+ * Never touches content outside the sentinel block — if the file
6234
+ * doesn't exist, creates it with just the sentinels. Safe to call
6235
+ * on any save / delete. */
6236
+ syncToWorkspace(agentId: string, workspacePath: string): void;
6237
+ /** Regenerate MEMORY.md from the on-disk files. Called automatically
6238
+ * after every save / remove; safe to call manually. */
6239
+ rewriteIndex(agentId: string): void;
6240
+ }
6241
+
5658
6242
  interface MeshPeerInfo {
5659
6243
  peer: string;
5660
6244
  healthy: boolean;
@@ -5747,6 +6331,12 @@ declare class AgentRegistry {
5747
6331
  private sessions;
5748
6332
  private wikiHub;
5749
6333
  private memoryStore;
6334
+ /** Structured per-agent memory (Claude-Code-style: user / feedback /
6335
+ * project / reference). Separate from the BM25 fact store above —
6336
+ * that's for short-lived facts extracted from conversations; this is
6337
+ * for long-lived behavioural memory that inlines into the system
6338
+ * prompt on every task. */
6339
+ readonly agentMemory: AgentMemory;
5750
6340
  private patternStore;
5751
6341
  private rateLimiter;
5752
6342
  private tokenTracker;
@@ -5830,6 +6420,18 @@ declare class AgentRegistry {
5830
6420
  /** Session-store accessor — used by the /recall HTTP endpoint to expose
5831
6421
  * conversation history to agents that need to rebuild context. */
5832
6422
  getSessionStore(): SessionStore;
6423
+ /** Fire-and-forget pre-rotation memo capture. Spawns a one-shot Haiku
6424
+ * call against the about-to-be-dropped Claude session asking for the
6425
+ * facts/decisions worth carrying forward, persists the result into
6426
+ * MemoryStore. Runs asynchronously so the calling rotation path
6427
+ * doesn't pay the extraction latency — the next user turn dispatches
6428
+ * immediately on the fresh session, and the memo lands in time for
6429
+ * whichever turn arrives ~30s later (which is when atlas's bloated-
6430
+ * session WhatsApp conversations actually need it).
6431
+ *
6432
+ * Errors are swallowed by design — memory continuity is best-effort,
6433
+ * it must never block or break the rotation it's hooked into. */
6434
+ private captureRotationMemoAsync;
5833
6435
  /**
5834
6436
  * Build peer list for context engine.
5835
6437
  */
@@ -5840,8 +6442,21 @@ declare class AgentRegistry {
5840
6442
  private getChannelHandle;
5841
6443
  /**
5842
6444
  * Execute a task on an agent. Respects maxConcurrent limit.
6445
+ *
6446
+ * Wraps `executeInternal` to record an intent-ledger resolution
6447
+ * after completion when `task.intentRef` is set. The resolution
6448
+ * write clears the dispatched-but-unresolved slot in
6449
+ * `intent_decisions` so Inv-ActiveTaskSafety lookups stop blocking
6450
+ * subsequent dispatches to the same (project, subject). Without
6451
+ * this, every dispatched decision sits in-flight forever and the
6452
+ * active-task check becomes vacuously over-aggressive.
5843
6453
  */
5844
6454
  execute(task: AgentTask, onDelta?: StreamCallback): Promise<AgentResponse>;
6455
+ /**
6456
+ * Internal dispatcher — the real body. See `execute` for the public
6457
+ * wrapper that adds intent-ledger resolution recording.
6458
+ */
6459
+ private executeInternal;
5845
6460
  /**
5846
6461
  * List all agents and their status.
5847
6462
  */
@@ -5952,94 +6567,6 @@ declare class AgentRegistry {
5952
6567
  getTokenTracker(): TokenTracker;
5953
6568
  }
5954
6569
 
5955
- /**
5956
- * Verified channel context — facts the channel adapter knows for certain.
5957
- * Prevents agents from hallucinating about their environment.
5958
- */
5959
- interface ChannelMeta {
5960
- /** Channel name */
5961
- channel: string;
5962
- /** Verified agents/bots present in this chat/group/project */
5963
- agents?: Array<{
5964
- id: string;
5965
- name: string;
5966
- handle?: string;
5967
- }>;
5968
- /** Project path (GitLab) or group topic */
5969
- project?: string;
5970
- /** Issue/MR context (GitLab) */
5971
- issue?: {
5972
- type: string;
5973
- iid: string;
5974
- title: string;
5975
- };
5976
- /** Channel-specific facts (e.g., pipeline status, group description) */
5977
- facts?: string[];
5978
- }
5979
- interface IncomingMessage {
5980
- id: string;
5981
- channel: string;
5982
- accountId: string;
5983
- sender: {
5984
- id: string;
5985
- name: string;
5986
- username?: string;
5987
- /** True when the origin is another bot account (Telegram `from.is_bot`).
5988
- * Used by the router to apply strict @-mention-only routing — prevents
5989
- * cross-daemon cascades from bare-word matches in bot replies. */
5990
- isBot?: boolean;
5991
- };
5992
- group?: {
5993
- id: string;
5994
- name: string;
5995
- };
5996
- text: string;
5997
- replyTo?: string;
5998
- replyToText?: string;
5999
- timestamp: Date;
6000
- media?: {
6001
- path: string;
6002
- type: string;
6003
- fileName?: string;
6004
- };
6005
- raw?: unknown;
6006
- resolvedAgent?: string;
6007
- preferNode?: string;
6008
- channelMeta?: ChannelMeta;
6009
- /** Correlator threaded by the workflow engine. When a workflow dispatches
6010
- * an agent, this carries the run id through the pipeline so post:response
6011
- * can re-enter the engine with `kind: agentResult` transitions. Opaque
6012
- * everywhere outside src/workflows/. */
6013
- workflowRunId?: string;
6014
- }
6015
- interface OutgoingMessage {
6016
- channel: string;
6017
- chatId: string;
6018
- text: string;
6019
- replyTo?: string;
6020
- parseMode?: "markdown" | "html" | "plain";
6021
- agentId?: string;
6022
- }
6023
- interface ChannelAdapter {
6024
- readonly name: string;
6025
- /** Start polling or webhook listener */
6026
- start(): Promise<void>;
6027
- /** Stop the adapter gracefully */
6028
- stop(): Promise<void>;
6029
- /** Send a message back to a chat */
6030
- send(msg: OutgoingMessage): Promise<string | void>;
6031
- /** Edit an existing message (returns false if not supported) */
6032
- editMessage?(chatId: string, messageId: string, text: string, parseMode?: string): Promise<boolean>;
6033
- /** Send typing/action indicator */
6034
- sendTyping?(chatId: string): Promise<void>;
6035
- /** React to a message with an emoji (e.g., 👀 for seen) */
6036
- react?(chatId: string, messageId: string, emoji?: string): Promise<void>;
6037
- /** Register handler for incoming messages */
6038
- onMessage(handler: (msg: IncomingMessage) => Promise<void>): void;
6039
- /** Get verified context for a chat (optional — channels implement what they can) */
6040
- getChannelMeta?(chatId: string): Promise<ChannelMeta | undefined>;
6041
- }
6042
-
6043
6570
  interface HandoverOverride {
6044
6571
  channel: string;
6045
6572
  chatId: string;
@@ -6362,12 +6889,76 @@ declare const businessConfigSchema: z.ZodObject<{
6362
6889
  reportsTo?: string | undefined;
6363
6890
  utilizationTarget?: number | undefined;
6364
6891
  }>>>;
6892
+ /** Per-project metadata. Phase 3 — defaults to empty array, so
6893
+ * existing agentx.json files validate without modification. */
6894
+ projects: z.ZodDefault<z.ZodArray<z.ZodObject<{
6895
+ /** Project identifier. For GitLab/GitHub: "owner/repo". For
6896
+ * noqta-internal projects: a stable string. */
6897
+ id: z.ZodString;
6898
+ /** AgentId of the PM for this project. Looked up by
6899
+ * `Organization.pmFor(project)` at dispatch time. */
6900
+ pm: z.ZodOptional<z.ZodString>;
6901
+ /** Optional client this project belongs to. Used by the activity
6902
+ * graph to attribute non-projected events (Telegram chats, free-
6903
+ * text WhatsApp, cron jobs) to the right client. Falls back to
6904
+ * the leading segment of `id` (split on "/") when unset. */
6905
+ client: z.ZodOptional<z.ZodString>;
6906
+ }, "strip", z.ZodTypeAny, {
6907
+ id: string;
6908
+ pm?: string | undefined;
6909
+ client?: string | undefined;
6910
+ }, {
6911
+ id: string;
6912
+ pm?: string | undefined;
6913
+ client?: string | undefined;
6914
+ }>, "many">>;
6915
+ /** Maps free-text chat senders to a client/project so the activity
6916
+ * graph can attribute Telegram/WhatsApp/etc. to the right client
6917
+ * instead of the catch-all "internal" bucket. */
6918
+ contactMap: z.ZodDefault<z.ZodArray<z.ZodObject<{
6919
+ /** Channel kind, e.g. "telegram", "whatsapp", "slack", "discord". */
6920
+ channel: z.ZodOptional<z.ZodString>;
6921
+ /** Native chat id ("-100..." for tg groups, JID for WhatsApp). */
6922
+ chatId: z.ZodOptional<z.ZodString>;
6923
+ /** Sender username/handle as exposed by the channel adapter. */
6924
+ username: z.ZodOptional<z.ZodString>;
6925
+ /** Numeric sender id when username is unstable. */
6926
+ senderId: z.ZodOptional<z.ZodString>;
6927
+ /** Client this contact belongs to. Required. */
6928
+ client: z.ZodString;
6929
+ /** Project this contact's traffic should attribute to. Defaults to
6930
+ * `${client}/_chat`. */
6931
+ project: z.ZodOptional<z.ZodString>;
6932
+ /** Display name override for the initiator pill. */
6933
+ displayName: z.ZodOptional<z.ZodString>;
6934
+ }, "strip", z.ZodTypeAny, {
6935
+ client: string;
6936
+ channel?: string | undefined;
6937
+ chatId?: string | undefined;
6938
+ username?: string | undefined;
6939
+ senderId?: string | undefined;
6940
+ project?: string | undefined;
6941
+ displayName?: string | undefined;
6942
+ }, {
6943
+ client: string;
6944
+ channel?: string | undefined;
6945
+ chatId?: string | undefined;
6946
+ username?: string | undefined;
6947
+ senderId?: string | undefined;
6948
+ project?: string | undefined;
6949
+ displayName?: string | undefined;
6950
+ }>, "many">>;
6365
6951
  /** Work-tick cadence in minutes during business hours. Default 15. */
6366
6952
  workTickMinutes: z.ZodDefault<z.ZodNumber>;
6367
6953
  /** Max queue depth for an idle agent before skipping the work tick (avoids piling up). */
6368
6954
  idleQueueThreshold: z.ZodDefault<z.ZodNumber>;
6369
6955
  }, "strip", z.ZodTypeAny, {
6370
6956
  enabled: boolean;
6957
+ projects: {
6958
+ id: string;
6959
+ pm?: string | undefined;
6960
+ client?: string | undefined;
6961
+ }[];
6371
6962
  timezone: string;
6372
6963
  mainChannel: {
6373
6964
  channel: string;
@@ -6405,6 +6996,15 @@ declare const businessConfigSchema: z.ZodObject<{
6405
6996
  utilizationTarget: number;
6406
6997
  reportsTo?: string | undefined;
6407
6998
  }>;
6999
+ contactMap: {
7000
+ client: string;
7001
+ channel?: string | undefined;
7002
+ chatId?: string | undefined;
7003
+ username?: string | undefined;
7004
+ senderId?: string | undefined;
7005
+ project?: string | undefined;
7006
+ displayName?: string | undefined;
7007
+ }[];
6408
7008
  workTickMinutes: number;
6409
7009
  idleQueueThreshold: number;
6410
7010
  }, {
@@ -6425,6 +7025,11 @@ declare const businessConfigSchema: z.ZodObject<{
6425
7025
  glob?: string | undefined;
6426
7026
  };
6427
7027
  enabled?: boolean | undefined;
7028
+ projects?: {
7029
+ id: string;
7030
+ pm?: string | undefined;
7031
+ client?: string | undefined;
7032
+ }[] | undefined;
6428
7033
  timezone?: string | undefined;
6429
7034
  roles?: Record<string, {
6430
7035
  title: string;
@@ -6446,6 +7051,15 @@ declare const businessConfigSchema: z.ZodObject<{
6446
7051
  reportsTo?: string | undefined;
6447
7052
  utilizationTarget?: number | undefined;
6448
7053
  }> | undefined;
7054
+ contactMap?: {
7055
+ client: string;
7056
+ channel?: string | undefined;
7057
+ chatId?: string | undefined;
7058
+ username?: string | undefined;
7059
+ senderId?: string | undefined;
7060
+ project?: string | undefined;
7061
+ displayName?: string | undefined;
7062
+ }[] | undefined;
6449
7063
  workTickMinutes?: number | undefined;
6450
7064
  idleQueueThreshold?: number | undefined;
6451
7065
  }>;
@@ -6463,6 +7077,7 @@ interface EmployeeProfile {
6463
7077
  }
6464
7078
  declare class Organization {
6465
7079
  private employees;
7080
+ private projects;
6466
7081
  constructor(config: BusinessConfig);
6467
7082
  get(agentId: string): EmployeeProfile | undefined;
6468
7083
  all(): EmployeeProfile[];
@@ -6470,6 +7085,43 @@ declare class Organization {
6470
7085
  directReports(agentId: string): string[];
6471
7086
  /** Walk up the org tree to the root. First element is `agentId` itself. */
6472
7087
  chainOfCommand(agentId: string): string[];
7088
+ /**
7089
+ * Phase 3 — PM lookup. Returns the agentId of the PM responsible
7090
+ * for `project`, or `undefined` when no PM is configured. The
7091
+ * dispatcher's PM gate (in a future commit) calls this before
7092
+ * dispatching to make sure project-scoped events flow through the
7093
+ * PM first. When undefined, dispatches proceed without a PM gate
7094
+ * (the legacy direct-routing behavior).
7095
+ */
7096
+ pmFor(project: string | null | undefined): string | undefined;
7097
+ /**
7098
+ * Phase 3 — escalation chain. Returns agents to escalate to in
7099
+ * upward order, EXCLUDING the agent itself. `chainOfCommand`
7100
+ * includes self at index 0; escalationChain skips that.
7101
+ *
7102
+ * Used when an agent fails / declines / times out — the dispatcher
7103
+ * walks up the chain looking for someone to take over.
7104
+ */
7105
+ escalationChain(agentId: string): string[];
7106
+ /**
7107
+ * Phase 3 — capability check (scaffold). Returns true when:
7108
+ * - The org chart is empty (`employees.size === 0`) — this is the
7109
+ * permissive default for partially-configured `business` blocks
7110
+ * that populate `projects[].pm` for the PM gate but haven't
7111
+ * filled in `orgChart`. Without this fallback, enabling business
7112
+ * with an empty orgChart would block every dispatch.
7113
+ * - OR the agent is registered in the org chart.
7114
+ *
7115
+ * Per-project / per-intent restrictions need typed capabilities
7116
+ * (Phase 5); until then this is a permissive-when-unconfigured
7117
+ * default that matches the legacy any-registered-agent-can-handle-
7118
+ * anything behavior.
7119
+ *
7120
+ * The `decideAndCommit` PM gate will call this *after* the PM
7121
+ * approves — the PM provides the project-scoped check, this
7122
+ * provides the agent-existence check.
7123
+ */
7124
+ canHandle(agentId: string, _project: string | null | undefined, _intent: string | null | undefined): boolean;
6473
7125
  }
6474
7126
 
6475
7127
  declare class Schedule {
@@ -6492,8 +7144,11 @@ interface WorkItem {
6492
7144
  estimatedSeconds?: number;
6493
7145
  url?: string;
6494
7146
  priority?: number;
6495
- /** Board stage derived from labels (kanban column id). */
6496
- stage?: "triage" | "todo" | "doing" | "onhold" | "review" | "done";
7147
+ /** Board stage / kanban column id. Was a closed enum back when boards
7148
+ * had a fixed schema; widened to `string` because per-project boards
7149
+ * can declare arbitrary column ids. The well-known names below remain
7150
+ * the standard set most boards use. */
7151
+ stage?: string;
6497
7152
  /** Raw labels from the source (GitLab). Empty array if source doesn't support labels. */
6498
7153
  labels?: string[];
6499
7154
  /** Label name+color when the source returns colored labels (GitLab with_labels_details). */
@@ -6769,6 +7424,23 @@ declare class MessageRouter {
6769
7424
  * Stamps the key when false. Size-bounded GC avoids unbounded growth.
6770
7425
  */
6771
7426
  private isDuplicateMessage;
7427
+ /**
7428
+ * Phase 1 commit 6.b — record one router decision in the intent ledger
7429
+ * when the source is enabled (mode != "off"). Covers all router
7430
+ * channels (telegram, slack, whatsapp, discord). Each channel has its
7431
+ * own per-source mode flag so they can be promoted independently in 1c.
7432
+ * Wrapped in try/catch so a ledger failure can never break message
7433
+ * routing — legacy is still authoritative until the 1c per-source
7434
+ * promotion.
7435
+ *
7436
+ * Returns the recorded decision when ledger fired (and outcome was
7437
+ * "dispatched"), so the caller can tag the IncomingMessage with
7438
+ * `intentRef` for resolution-on-completion. Returns undefined when
7439
+ * the source is off, the channel isn't supported, ledger threw, or
7440
+ * the ledger's own decision was non-dispatched (deduped/halted —
7441
+ * no resolution to write).
7442
+ */
7443
+ private recordRouterDecisionInLedger;
6772
7444
  private handleMessage;
6773
7445
  /** The actual processing body — split out so handleMessage can wrap it in
6774
7446
  * the inflight try/finally without drowning the happy path in indentation. */
@@ -6792,6 +7464,15 @@ declare class MessageRouter {
6792
7464
  * Handle a message by routing to a mesh peer's agent.
6793
7465
  * Searches peer agent cards for mention matches.
6794
7466
  */
7467
+ /** Build the context payload that mesh.sendTask forwards to the receiving
7468
+ * daemon's /task handler. Without this, the receiver runs registry.execute
7469
+ * with default channel="api"/chatId="default" — losing the channel,
7470
+ * project, issue id, sender, and channelMeta the original adapter built
7471
+ * from the inbound webhook. Shape matches what processResolvedMessage
7472
+ * already passes to registry.execute on the local path, so a mesh-routed
7473
+ * task and a locally-routed task produce the same session keying and
7474
+ * the same prompt context. */
7475
+ private buildMeshContext;
6795
7476
  private handleViaMesh;
6796
7477
  /**
6797
7478
  * Route to a mesh peer by resolved agentId (not text matching).
@@ -6889,6 +7570,12 @@ declare class TelegramAdapter implements ChannelAdapter {
6889
7570
  private chatAccountMap;
6890
7571
  private botInfo;
6891
7572
  private groupStore;
7573
+ /** Account IDs of bots currently active (member/administrator) in a group.
7574
+ * Used by the router to pick the correct bot when an agent has multiple
7575
+ * Telegram accounts bound to it (e.g. pm-ksi → both @noqta_ksi_bot and
7576
+ * @noqta_pm_ksi_bot) — the in-group account should win, otherwise messages
7577
+ * to the absent bot get silently dropped by multi-account-dedup. */
7578
+ getGroupBotAccounts(groupId: string): string[];
6892
7579
  /**
6893
7580
  * Send a message. Returns the sent message ID.
6894
7581
  * Pass accountId to send from a specific bot account.
@@ -6896,6 +7583,12 @@ declare class TelegramAdapter implements ChannelAdapter {
6896
7583
  send(msg: OutgoingMessage & {
6897
7584
  accountId?: string;
6898
7585
  }): Promise<string>;
7586
+ /** Best-effort outbound shadow log. Captures every successful send into
7587
+ * the same per-chat shadow file the inbound writer uses, so seedHistory
7588
+ * + agent introspection see a complete picture (which bot identity sent
7589
+ * what, at which time). Failures are swallowed — the message already
7590
+ * went out on the wire; logging is observability, not correctness. */
7591
+ private recordOutboundShadow;
6899
7592
  /**
6900
7593
  * Send a message with an inline keyboard. Used by the workflow user-task
6901
7594
  * renderer so approve/reject-style tasks get one-tap URL buttons in
@@ -6967,6 +7660,26 @@ declare class TelegramAdapter implements ChannelAdapter {
6967
7660
  * Called once per group on first encounter, then maintained via my_chat_member events.
6968
7661
  */
6969
7662
  private seedGroupMembership;
7663
+ /** Append one entry to the shadow log for `chatId`. The shadow log is a
7664
+ * daily file at `.agentx/sessions/_telegram_raw:{chatId}:{day}.json` that
7665
+ * captures every allowed inbound (and adapter-side outbound) message
7666
+ * regardless of how the router resolved it — including periods when no
7667
+ * agent was bound or the daemon was offline (the next inbound triggers
7668
+ * a write). seedHistory reads it back on cold session create so a fresh
7669
+ * session mirrors the live chat instead of starting blank. */
7670
+ private appendShadowLog;
7671
+ private shadowLogPath;
7672
+ /** ChannelAdapter.seedHistory: returns the most recent shadow-logged
7673
+ * messages for `chatId`, walking back day-by-day until we hit
7674
+ * `maxMessages` or `maxChars`. Telegram's Bot API has no "fetch chat
7675
+ * history" endpoint, so the shadow log is our only source — meaning the
7676
+ * first deployment sees an empty seed for any pre-existing chat, and
7677
+ * context grows as the bot observes new traffic. */
7678
+ seedHistory(chatId: string, opts: {
7679
+ sinceISO?: string;
7680
+ maxMessages: number;
7681
+ maxChars: number;
7682
+ }): Promise<SeededMessage[]>;
6970
7683
  private apiCall;
6971
7684
  }
6972
7685
 
@@ -7145,6 +7858,16 @@ declare class WhatsAppAdapter implements ChannelAdapter {
7145
7858
  limit?: number;
7146
7859
  before?: string;
7147
7860
  }): Promise<HistoryMessage[]>;
7861
+ /** ChannelAdapter.seedHistory: thin wrapper over getHistory() that maps
7862
+ * Baileys' HistoryMessage shape to the channel-agnostic SeededMessage
7863
+ * shape consumed by SessionStore on cold session create. Bounded by the
7864
+ * caller's maxMessages/maxChars; fromMe → role:agent. The chatId is the
7865
+ * Baileys jid (e.g., "21694xxx@s.whatsapp.net" for DMs). */
7866
+ seedHistory(chatId: string, opts: {
7867
+ sinceISO?: string;
7868
+ maxMessages: number;
7869
+ maxChars: number;
7870
+ }): Promise<SeededMessage[]>;
7148
7871
  /** Token-bucket gate for live Baileys reads. Enforces
7149
7872
  * `throttleMinMs` between calls and caps the per-minute call count.
7150
7873
  * Returns a promise that resolves once it's safe to fire. */
@@ -7211,6 +7934,13 @@ declare class CronScheduler {
7211
7934
  stop(): Promise<void>;
7212
7935
  private scheduleNext;
7213
7936
  private scheduleRetry;
7937
+ /**
7938
+ * Phase 1 commit 6.d — record one cron-fire decision in the ledger
7939
+ * when the source is enabled. Wrapped in try/catch so a ledger
7940
+ * failure can never break cron dispatch — legacy stays authoritative
7941
+ * until the 1c per-source promotion lands.
7942
+ */
7943
+ private recordCronDecisionInLedger;
7214
7944
  private executeJob;
7215
7945
  /**
7216
7946
  * Detect cron jobs that should have fired while the daemon was down.