agentix-cli 0.21.0 → 0.22.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 (62) hide show
  1. package/dist/{agent-I3ILI3MO.js → agent-Y4U4R3ZZ.js} +2 -2
  2. package/dist/{board-dashboard-ASI6MDKX.js → board-dashboard-2CPBWFOB.js} +4 -4
  3. package/dist/{chunk-EQNM3TOF.js → chunk-2HXSJYAJ.js} +2 -2
  4. package/dist/{chunk-K6MC66D6.js → chunk-ABVZHJUO.js} +2 -2
  5. package/dist/chunk-BHSPAP4U.js +44 -0
  6. package/dist/chunk-BHSPAP4U.js.map +1 -0
  7. package/dist/chunk-CO4YCIIA.js +13 -0
  8. package/dist/{chunk-5GDCJ2SF.js.map → chunk-CO4YCIIA.js.map} +1 -1
  9. package/dist/{chunk-4MREHZP5.js → chunk-GOLZ6NEL.js} +2 -2
  10. package/dist/chunk-GOLZ6NEL.js.map +1 -0
  11. package/dist/{chunk-FUYZUGNQ.js → chunk-KX626FN4.js} +2 -2
  12. package/dist/chunk-OPKQNLGX.js +450 -0
  13. package/dist/chunk-OPKQNLGX.js.map +1 -0
  14. package/dist/chunk-OZOQKPJV.js +102 -0
  15. package/dist/chunk-OZOQKPJV.js.map +1 -0
  16. package/dist/{chunk-AANSVC5A.js → chunk-P3TJHGO5.js} +2 -2
  17. package/dist/{chunk-AGINLNAW.js → chunk-VYLZ7UYM.js} +2 -2
  18. package/dist/{chunk-2B2DIQKD.js → chunk-WHP4I44J.js} +2 -2
  19. package/dist/chunk-X5D5AV5N.js +833 -0
  20. package/dist/chunk-X5D5AV5N.js.map +1 -0
  21. package/dist/cli.js +1 -1
  22. package/dist/{config-RWHSEE4N.js → config-BLOKGMER.js} +2 -2
  23. package/dist/config-mutator-RG7QY2EL.js +2 -0
  24. package/dist/doctor-ZTECTUHX.js +2 -0
  25. package/dist/index.d.ts +140 -3
  26. package/dist/index.js +1 -1
  27. package/dist/program-GZOJIWTB.js +2 -0
  28. package/dist/registry-QZIUSQ5J.js +2 -0
  29. package/dist/scheduler-B2BQPV4M.js +2 -0
  30. package/dist/workspace-setup-DQCZX2NE.js +2 -0
  31. package/package.json +1 -1
  32. package/dist/chunk-4MREHZP5.js.map +0 -1
  33. package/dist/chunk-5GDCJ2SF.js +0 -13
  34. package/dist/chunk-5XZ73VM4.js +0 -102
  35. package/dist/chunk-5XZ73VM4.js.map +0 -1
  36. package/dist/chunk-BLRLUF2H.js +0 -743
  37. package/dist/chunk-BLRLUF2H.js.map +0 -1
  38. package/dist/chunk-IFQQVDM6.js +0 -442
  39. package/dist/chunk-IFQQVDM6.js.map +0 -1
  40. package/dist/chunk-ZE2YFX4K.js +0 -45
  41. package/dist/chunk-ZE2YFX4K.js.map +0 -1
  42. package/dist/config-mutator-JKCNCY2F.js +0 -2
  43. package/dist/doctor-KWN7NUFQ.js +0 -2
  44. package/dist/program-JEKM7SCS.js +0 -2
  45. package/dist/registry-EGKDMVSP.js +0 -2
  46. package/dist/scheduler-ED4FCI2Y.js +0 -2
  47. package/dist/workspace-setup-FPGN5VNY.js +0 -2
  48. /package/dist/{agent-I3ILI3MO.js.map → agent-Y4U4R3ZZ.js.map} +0 -0
  49. /package/dist/{board-dashboard-ASI6MDKX.js.map → board-dashboard-2CPBWFOB.js.map} +0 -0
  50. /package/dist/{chunk-EQNM3TOF.js.map → chunk-2HXSJYAJ.js.map} +0 -0
  51. /package/dist/{chunk-K6MC66D6.js.map → chunk-ABVZHJUO.js.map} +0 -0
  52. /package/dist/{chunk-FUYZUGNQ.js.map → chunk-KX626FN4.js.map} +0 -0
  53. /package/dist/{chunk-AANSVC5A.js.map → chunk-P3TJHGO5.js.map} +0 -0
  54. /package/dist/{chunk-AGINLNAW.js.map → chunk-VYLZ7UYM.js.map} +0 -0
  55. /package/dist/{chunk-2B2DIQKD.js.map → chunk-WHP4I44J.js.map} +0 -0
  56. /package/dist/{config-RWHSEE4N.js.map → config-BLOKGMER.js.map} +0 -0
  57. /package/dist/{config-mutator-JKCNCY2F.js.map → config-mutator-RG7QY2EL.js.map} +0 -0
  58. /package/dist/{doctor-KWN7NUFQ.js.map → doctor-ZTECTUHX.js.map} +0 -0
  59. /package/dist/{program-JEKM7SCS.js.map → program-GZOJIWTB.js.map} +0 -0
  60. /package/dist/{registry-EGKDMVSP.js.map → registry-QZIUSQ5J.js.map} +0 -0
  61. /package/dist/{scheduler-ED4FCI2Y.js.map → scheduler-B2BQPV4M.js.map} +0 -0
  62. /package/dist/{workspace-setup-FPGN5VNY.js.map → workspace-setup-DQCZX2NE.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import{a as s}from"./chunk-IFQQVDM6.js";import"./chunk-2B2DIQKD.js";import"./chunk-CNNLREHD.js";import"./chunk-FUYZUGNQ.js";import"./chunk-SZDACK6O.js";import"./chunk-GV7S5BAX.js";import"./chunk-GSX4NK2N.js";import"./chunk-BLRLUF2H.js";import"./chunk-EQNM3TOF.js";import"./chunk-N67JZAXU.js";import"./chunk-AFRHKA7B.js";import"./chunk-EW27SCCC.js";import"./chunk-ZE2YFX4K.js";import"./chunk-AGINLNAW.js";import"./chunk-SFQUP3BP.js";import"./chunk-K6MC66D6.js";import"./chunk-AANSVC5A.js";import"./chunk-5GDCJ2SF.js";import"./chunk-CDNWCYF7.js";import"./chunk-5XZ73VM4.js";import"./chunk-5F3J55LG.js";import"./chunk-WJXU5F4Z.js";import"./chunk-4MREHZP5.js";import"./chunk-VWTBHLMX.js";import"./chunk-7P5QRX5W.js";import"./chunk-JTJIPNXY.js";import"./chunk-64P4QHSZ.js";import"./chunk-BFZUG66X.js";import"./chunk-SR3X25TN.js";import"./chunk-53NRX2NA.js";import"./chunk-A6BKSZQA.js";import"./chunk-JGXE2YNV.js";import"./chunk-VJUTQAPJ.js";import"./chunk-DJEZ2HVU.js";import"./chunk-DSYKYZMT.js";import"./chunk-IKBHHTIT.js";import"./chunk-QOYAX2FT.js";import"./chunk-4YCH6IZV.js";process.on("SIGINT",()=>process.exit(0));process.on("SIGTERM",()=>process.exit(0));async function o(){let r=await s();if(process.argv.slice(2).length===0){r.outputHelp();return}r.parse()}o();
2
+ import{a as s}from"./chunk-OPKQNLGX.js";import"./chunk-WHP4I44J.js";import"./chunk-CNNLREHD.js";import"./chunk-KX626FN4.js";import"./chunk-GV7S5BAX.js";import"./chunk-SZDACK6O.js";import"./chunk-GSX4NK2N.js";import"./chunk-X5D5AV5N.js";import"./chunk-2HXSJYAJ.js";import"./chunk-N67JZAXU.js";import"./chunk-AFRHKA7B.js";import"./chunk-EW27SCCC.js";import"./chunk-VYLZ7UYM.js";import"./chunk-SFQUP3BP.js";import"./chunk-ABVZHJUO.js";import"./chunk-P3TJHGO5.js";import"./chunk-CO4YCIIA.js";import"./chunk-BHSPAP4U.js";import"./chunk-CDNWCYF7.js";import"./chunk-OZOQKPJV.js";import"./chunk-5F3J55LG.js";import"./chunk-GOLZ6NEL.js";import"./chunk-WJXU5F4Z.js";import"./chunk-VWTBHLMX.js";import"./chunk-7P5QRX5W.js";import"./chunk-JTJIPNXY.js";import"./chunk-64P4QHSZ.js";import"./chunk-BFZUG66X.js";import"./chunk-SR3X25TN.js";import"./chunk-53NRX2NA.js";import"./chunk-A6BKSZQA.js";import"./chunk-JGXE2YNV.js";import"./chunk-VJUTQAPJ.js";import"./chunk-DJEZ2HVU.js";import"./chunk-DSYKYZMT.js";import"./chunk-IKBHHTIT.js";import"./chunk-QOYAX2FT.js";import"./chunk-4YCH6IZV.js";process.on("SIGINT",()=>process.exit(0));process.on("SIGTERM",()=>process.exit(0));async function o(){let r=await s();if(process.argv.slice(2).length===0){r.outputHelp();return}r.parse()}o();
3
3
  //# sourceMappingURL=cli.js.map
@@ -1,2 +1,2 @@
1
- import{c as a,d as b,e as c,f as d}from"./chunk-5GDCJ2SF.js";import"./chunk-4YCH6IZV.js";export{a as daemonConfigSchema,b as expandEnvVars,c as loadDaemonConfig,d as validateWorkspaces};
2
- //# sourceMappingURL=config-RWHSEE4N.js.map
1
+ import{c as a,d as b,e as c,f as d}from"./chunk-CO4YCIIA.js";import"./chunk-4YCH6IZV.js";export{a as daemonConfigSchema,b as expandEnvVars,c as loadDaemonConfig,d as validateWorkspaces};
2
+ //# sourceMappingURL=config-BLOKGMER.js.map
@@ -0,0 +1,2 @@
1
+ import{a,b,c,d}from"./chunk-P3TJHGO5.js";import"./chunk-CO4YCIIA.js";import"./chunk-4YCH6IZV.js";export{a as applyConfigMutation,c as getAtPath,b as setAtPath,d as unsetAtPath};
2
+ //# sourceMappingURL=config-mutator-RG7QY2EL.js.map
@@ -0,0 +1,2 @@
1
+ import{a,b}from"./chunk-KX626FN4.js";import"./chunk-CO4YCIIA.js";import"./chunk-4YCH6IZV.js";export{b as doctor,a as runDoctorChecks};
2
+ //# sourceMappingURL=doctor-ZTECTUHX.js.map
package/dist/index.d.ts CHANGED
@@ -1366,6 +1366,19 @@ declare const agentConfigSchema: z.ZodObject<{
1366
1366
  * agents that do long investigations or multi-file refactors. */
1367
1367
  maxExecutionMinutes: z.ZodDefault<z.ZodNumber>;
1368
1368
  permissionMode: z.ZodDefault<z.ZodString>;
1369
+ /** When true, this agent's claude-code dispatches reuse a long-lived
1370
+ * subprocess per (channel, chatId) instead of spawning a fresh
1371
+ * `claude -p` per turn. Driven over stdin via stream-json input,
1372
+ * which keeps the prompt cache warm across turns within a chat
1373
+ * (turn 1 cache_create=12897 → turn 2 cache_create=20 + cache_read
1374
+ * =24575 in the 2026-05-03 spike — 3-5× latency win on chat-shaped
1375
+ * workloads). When the registry can't allocate a slot (global or
1376
+ * per-agent cap exceeded), the dispatch falls back to spawn-per-task
1377
+ * silently with a warning log. Other tiers (sdk, orchestrator)
1378
+ * ignore this flag. Default false until the persistent path
1379
+ * finishes its soak; flip per-agent first, then per-fleet.
1380
+ * See docs/architecture/persistent-claude-process.md for the design. */
1381
+ persistentProcess: z.ZodDefault<z.ZodBoolean>;
1369
1382
  queueMode: z.ZodDefault<z.ZodEnum<["collect", "followup", "drop"]>>;
1370
1383
  heartbeat: z.ZodDefault<z.ZodObject<{
1371
1384
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -1399,6 +1412,7 @@ declare const agentConfigSchema: z.ZodObject<{
1399
1412
  maxConcurrent: number;
1400
1413
  maxExecutionMinutes: number;
1401
1414
  permissionMode: string;
1415
+ persistentProcess: boolean;
1402
1416
  queueMode: "collect" | "followup" | "drop";
1403
1417
  heartbeat: {
1404
1418
  enabled: boolean;
@@ -1436,6 +1450,7 @@ declare const agentConfigSchema: z.ZodObject<{
1436
1450
  maxConcurrent?: number | undefined;
1437
1451
  maxExecutionMinutes?: number | undefined;
1438
1452
  permissionMode?: string | undefined;
1453
+ persistentProcess?: boolean | undefined;
1439
1454
  queueMode?: "collect" | "followup" | "drop" | undefined;
1440
1455
  heartbeat?: {
1441
1456
  enabled?: boolean | undefined;
@@ -1606,6 +1621,19 @@ declare const daemonConfigSchema: z.ZodObject<{
1606
1621
  * agents that do long investigations or multi-file refactors. */
1607
1622
  maxExecutionMinutes: z.ZodDefault<z.ZodNumber>;
1608
1623
  permissionMode: z.ZodDefault<z.ZodString>;
1624
+ /** When true, this agent's claude-code dispatches reuse a long-lived
1625
+ * subprocess per (channel, chatId) instead of spawning a fresh
1626
+ * `claude -p` per turn. Driven over stdin via stream-json input,
1627
+ * which keeps the prompt cache warm across turns within a chat
1628
+ * (turn 1 cache_create=12897 → turn 2 cache_create=20 + cache_read
1629
+ * =24575 in the 2026-05-03 spike — 3-5× latency win on chat-shaped
1630
+ * workloads). When the registry can't allocate a slot (global or
1631
+ * per-agent cap exceeded), the dispatch falls back to spawn-per-task
1632
+ * silently with a warning log. Other tiers (sdk, orchestrator)
1633
+ * ignore this flag. Default false until the persistent path
1634
+ * finishes its soak; flip per-agent first, then per-fleet.
1635
+ * See docs/architecture/persistent-claude-process.md for the design. */
1636
+ persistentProcess: z.ZodDefault<z.ZodBoolean>;
1609
1637
  queueMode: z.ZodDefault<z.ZodEnum<["collect", "followup", "drop"]>>;
1610
1638
  heartbeat: z.ZodDefault<z.ZodObject<{
1611
1639
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -1639,6 +1667,7 @@ declare const daemonConfigSchema: z.ZodObject<{
1639
1667
  maxConcurrent: number;
1640
1668
  maxExecutionMinutes: number;
1641
1669
  permissionMode: string;
1670
+ persistentProcess: boolean;
1642
1671
  queueMode: "collect" | "followup" | "drop";
1643
1672
  heartbeat: {
1644
1673
  enabled: boolean;
@@ -1676,6 +1705,7 @@ declare const daemonConfigSchema: z.ZodObject<{
1676
1705
  maxConcurrent?: number | undefined;
1677
1706
  maxExecutionMinutes?: number | undefined;
1678
1707
  permissionMode?: string | undefined;
1708
+ persistentProcess?: boolean | undefined;
1679
1709
  queueMode?: "collect" | "followup" | "drop" | undefined;
1680
1710
  heartbeat?: {
1681
1711
  enabled?: boolean | undefined;
@@ -2888,6 +2918,22 @@ declare const daemonConfigSchema: z.ZodObject<{
2888
2918
  }>, "many">>;
2889
2919
  workTickMinutes: z.ZodDefault<z.ZodNumber>;
2890
2920
  idleQueueThreshold: z.ZodDefault<z.ZodNumber>;
2921
+ standup: z.ZodDefault<z.ZodObject<{
2922
+ enabled: z.ZodDefault<z.ZodBoolean>;
2923
+ plansDir: z.ZodDefault<z.ZodString>;
2924
+ maxAgentsPerDay: z.ZodDefault<z.ZodNumber>;
2925
+ dryRun: z.ZodDefault<z.ZodBoolean>;
2926
+ }, "strip", z.ZodTypeAny, {
2927
+ enabled: boolean;
2928
+ plansDir: string;
2929
+ maxAgentsPerDay: number;
2930
+ dryRun: boolean;
2931
+ }, {
2932
+ enabled?: boolean | undefined;
2933
+ plansDir?: string | undefined;
2934
+ maxAgentsPerDay?: number | undefined;
2935
+ dryRun?: boolean | undefined;
2936
+ }>>;
2891
2937
  }, "strip", z.ZodTypeAny, {
2892
2938
  enabled: boolean;
2893
2939
  projects: {
@@ -2943,6 +2989,12 @@ declare const daemonConfigSchema: z.ZodObject<{
2943
2989
  }[];
2944
2990
  workTickMinutes: number;
2945
2991
  idleQueueThreshold: number;
2992
+ standup: {
2993
+ enabled: boolean;
2994
+ plansDir: string;
2995
+ maxAgentsPerDay: number;
2996
+ dryRun: boolean;
2997
+ };
2946
2998
  }, {
2947
2999
  mainChannel: {
2948
3000
  channel: string;
@@ -2998,6 +3050,12 @@ declare const daemonConfigSchema: z.ZodObject<{
2998
3050
  }[] | undefined;
2999
3051
  workTickMinutes?: number | undefined;
3000
3052
  idleQueueThreshold?: number | undefined;
3053
+ standup?: {
3054
+ enabled?: boolean | undefined;
3055
+ plansDir?: string | undefined;
3056
+ maxAgentsPerDay?: number | undefined;
3057
+ dryRun?: boolean | undefined;
3058
+ } | undefined;
3001
3059
  }>>;
3002
3060
  boards: z.ZodDefault<z.ZodArray<z.ZodObject<{
3003
3061
  id: z.ZodString;
@@ -3399,6 +3457,7 @@ declare const daemonConfigSchema: z.ZodObject<{
3399
3457
  maxConcurrent: number;
3400
3458
  maxExecutionMinutes: number;
3401
3459
  permissionMode: string;
3460
+ persistentProcess: boolean;
3402
3461
  queueMode: "collect" | "followup" | "drop";
3403
3462
  heartbeat: {
3404
3463
  enabled: boolean;
@@ -3750,6 +3809,12 @@ declare const daemonConfigSchema: z.ZodObject<{
3750
3809
  }[];
3751
3810
  workTickMinutes: number;
3752
3811
  idleQueueThreshold: number;
3812
+ standup: {
3813
+ enabled: boolean;
3814
+ plansDir: string;
3815
+ maxAgentsPerDay: number;
3816
+ dryRun: boolean;
3817
+ };
3753
3818
  } | undefined;
3754
3819
  }, {
3755
3820
  node: {
@@ -3778,6 +3843,7 @@ declare const daemonConfigSchema: z.ZodObject<{
3778
3843
  maxConcurrent?: number | undefined;
3779
3844
  maxExecutionMinutes?: number | undefined;
3780
3845
  permissionMode?: string | undefined;
3846
+ persistentProcess?: boolean | undefined;
3781
3847
  queueMode?: "collect" | "followup" | "drop" | undefined;
3782
3848
  heartbeat?: {
3783
3849
  enabled?: boolean | undefined;
@@ -4042,6 +4108,12 @@ declare const daemonConfigSchema: z.ZodObject<{
4042
4108
  }[] | undefined;
4043
4109
  workTickMinutes?: number | undefined;
4044
4110
  idleQueueThreshold?: number | undefined;
4111
+ standup?: {
4112
+ enabled?: boolean | undefined;
4113
+ plansDir?: string | undefined;
4114
+ maxAgentsPerDay?: number | undefined;
4115
+ dryRun?: boolean | undefined;
4116
+ } | undefined;
4045
4117
  } | undefined;
4046
4118
  boards?: {
4047
4119
  id: string;
@@ -4376,6 +4448,13 @@ declare class AgentXDaemon {
4376
4448
  private loadedPlugins;
4377
4449
  private readonly agentMemory;
4378
4450
  private contacts;
4451
+ /** Persistent-claude process registry. Null when no agent has
4452
+ * persistentProcess: true (legacy spawn-per-task path). */
4453
+ private processRegistry;
4454
+ /** Unified observability bus. Publishers (workflow engine, mesh,
4455
+ * channels, signals) emit here; subscribers (SSE, CLI, board
4456
+ * dashboard) filter by kind / workflow / actor. Created eagerly so
4457
+ * early boot events don't fall through. */
4379
4458
  readonly events: EventBus;
4380
4459
  private log;
4381
4460
  private sseClients;
@@ -4728,6 +4807,12 @@ interface AgentTask {
4728
4807
  eventId: string;
4729
4808
  decidedBy: string;
4730
4809
  };
4810
+ /** ULID for the per-execution trace row in task_traces (improvement
4811
+ * plan #2). Allocated by registry.execute right before emitting the
4812
+ * task:started bus event so per-step capture sites in the streaming
4813
+ * parser can append rows under the same id. Optional — when unset,
4814
+ * step capture is a no-op. */
4815
+ taskId?: string;
4731
4816
  context?: {
4732
4817
  channel?: string;
4733
4818
  sender?: string;
@@ -4811,9 +4896,6 @@ declare function executeSdk(agent: AgentDef, task: AgentTask, apiKey: string, hi
4811
4896
  * Execute a task using agentx's own orchestrator (tier: "orchestrator").
4812
4897
  */
4813
4898
  declare function executeOrchestrator(agent: AgentDef, task: AgentTask, apiKey?: string, historyContext?: string): Promise<AgentResponse>;
4814
- /**
4815
- * Route a task to the correct execution tier.
4816
- */
4817
4899
  declare function executeTask(agent: AgentDef, task: AgentTask, providers: Record<string, {
4818
4900
  apiKey?: string;
4819
4901
  }>, onDelta?: StreamCallback, historyContext?: string, resumeSessionId?: string, onEvent?: (event: any) => void): Promise<AgentResponse>;
@@ -6952,6 +7034,37 @@ declare const businessConfigSchema: z.ZodObject<{
6952
7034
  workTickMinutes: z.ZodDefault<z.ZodNumber>;
6953
7035
  /** Max queue depth for an idle agent before skipping the work tick (avoids piling up). */
6954
7036
  idleQueueThreshold: z.ZodDefault<z.ZodNumber>;
7037
+ /** Standup-tick gating + plan-driven priorities. The day-cycle's morning
7038
+ * STANDUP used to fire unconditionally per agent — operators reported it
7039
+ * as "mechanical" because it dispatched a Claude turn even when no human
7040
+ * had set a plan for the day. Now: the cycle reads
7041
+ * .agentx/plans/<date>.md (day → week → month fallback) and either
7042
+ * injects the plan as standup priorities, or posts a "no plan today"
7043
+ * notification to mainChannel and dispatches no agent. */
7044
+ standup: z.ZodDefault<z.ZodObject<{
7045
+ /** Master switch. When false, fireStandup is a no-op (silent). */
7046
+ enabled: z.ZodDefault<z.ZodBoolean>;
7047
+ /** Plans directory, relative to the daemon CWD. */
7048
+ plansDir: z.ZodDefault<z.ZodString>;
7049
+ /** Hard ceiling on how many agents the standup tick can dispatch in a
7050
+ * single morning — guards against an org-chart misconfiguration
7051
+ * fanning out to dozens of Claude calls before anyone notices. */
7052
+ maxAgentsPerDay: z.ZodDefault<z.ZodNumber>;
7053
+ /** When true, even with a plan present the cycle won't dispatch — only
7054
+ * posts the resolved plan to mainChannel for human review. Useful as
7055
+ * a kill-switch while iterating on plan formats. */
7056
+ dryRun: z.ZodDefault<z.ZodBoolean>;
7057
+ }, "strip", z.ZodTypeAny, {
7058
+ enabled: boolean;
7059
+ plansDir: string;
7060
+ maxAgentsPerDay: number;
7061
+ dryRun: boolean;
7062
+ }, {
7063
+ enabled?: boolean | undefined;
7064
+ plansDir?: string | undefined;
7065
+ maxAgentsPerDay?: number | undefined;
7066
+ dryRun?: boolean | undefined;
7067
+ }>>;
6955
7068
  }, "strip", z.ZodTypeAny, {
6956
7069
  enabled: boolean;
6957
7070
  projects: {
@@ -7007,6 +7120,12 @@ declare const businessConfigSchema: z.ZodObject<{
7007
7120
  }[];
7008
7121
  workTickMinutes: number;
7009
7122
  idleQueueThreshold: number;
7123
+ standup: {
7124
+ enabled: boolean;
7125
+ plansDir: string;
7126
+ maxAgentsPerDay: number;
7127
+ dryRun: boolean;
7128
+ };
7010
7129
  }, {
7011
7130
  mainChannel: {
7012
7131
  channel: string;
@@ -7062,6 +7181,12 @@ declare const businessConfigSchema: z.ZodObject<{
7062
7181
  }[] | undefined;
7063
7182
  workTickMinutes?: number | undefined;
7064
7183
  idleQueueThreshold?: number | undefined;
7184
+ standup?: {
7185
+ enabled?: boolean | undefined;
7186
+ plansDir?: string | undefined;
7187
+ maxAgentsPerDay?: number | undefined;
7188
+ dryRun?: boolean | undefined;
7189
+ } | undefined;
7065
7190
  }>;
7066
7191
  type BusinessConfig = z.infer<typeof businessConfigSchema>;
7067
7192
  type BusinessRole = z.infer<typeof roleSchema>;
@@ -7295,6 +7420,9 @@ declare class DayCycle {
7295
7420
  private day;
7296
7421
  private currentDate;
7297
7422
  private runningTask;
7423
+ private standupsFiredToday;
7424
+ private noPlanNoticePostedFor?;
7425
+ private planStore;
7298
7426
  constructor(config: BusinessConfig, org: Organization, schedule: Schedule, registry: AgentRegistry, reporter: Reporter, kpi: KPI, workSource: WorkSource, daemonBase: string, log: (...args: unknown[]) => void);
7299
7427
  start(): void;
7300
7428
  stop(): void;
@@ -7302,6 +7430,15 @@ declare class DayCycle {
7302
7430
  private roleContext;
7303
7431
  private runLifecycle;
7304
7432
  private fireStandup;
7433
+ /** Post one "no plan today" line to mainChannel per calendar date. After
7434
+ * the first STANDUP would have fired, we let the team know the cycle
7435
+ * saw nothing to do — quiet enough not to be annoying, loud enough that
7436
+ * a forgotten plan doesn't go unnoticed for a whole morning. */
7437
+ private postNoPlanNoticeOnce;
7438
+ /** Dry-run preview: post the resolved plan to the main channel so the
7439
+ * operator can eyeball it before turning dryRun off. Once per day per
7440
+ * resolved plan. */
7441
+ private postPlanPreviewOnce;
7305
7442
  private fireWorkTick;
7306
7443
  private fireWrap;
7307
7444
  }
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{a as U}from"./chunk-4ITQP3XL.js";import{a as yt,b as re,c as ae,d as vt,e as xt,f as Ct,i as bt,j as $t,k as Tt}from"./chunk-BLRLUF2H.js";import"./chunk-EQNM3TOF.js";import"./chunk-N67JZAXU.js";import"./chunk-AFRHKA7B.js";import"./chunk-EW27SCCC.js";import"./chunk-ZE2YFX4K.js";import{A as H,B as E,C as I,a as be,b as $e,c as Te,d as je,e as _e,f as Ue,g as Be,h as We,i as qe,j as Ve,k as Ye,l as F,m as Q,n as Ze,o as et,p as tt,q as j,r as st,s as ot,t as nt,u as P,v as Z,w as it,x as rt,y as at,z as ht}from"./chunk-AGINLNAW.js";import{a as X,b as M,c as _}from"./chunk-SFQUP3BP.js";import{f as Pt}from"./chunk-K6MC66D6.js";import"./chunk-AANSVC5A.js";import{c as kt,e as wt,f as St}from"./chunk-5GDCJ2SF.js";import"./chunk-CDNWCYF7.js";import{a as lt,b as ct,c as dt,d as pt,i as ut}from"./chunk-5XZ73VM4.js";import"./chunk-5F3J55LG.js";import"./chunk-WJXU5F4Z.js";import"./chunk-4MREHZP5.js";import"./chunk-VWTBHLMX.js";import{a as gt,b as mt,e as ft}from"./chunk-7P5QRX5W.js";import"./chunk-JTJIPNXY.js";import"./chunk-64P4QHSZ.js";import"./chunk-BFZUG66X.js";import"./chunk-SR3X25TN.js";import"./chunk-53NRX2NA.js";import"./chunk-A6BKSZQA.js";import{a as Re,b as Ae,c as Me,d as Ee,e as Ie,f as Oe,g as Ge,h as De,i as Le,j as Ne,k as Fe,l as A}from"./chunk-JGXE2YNV.js";import{a as m}from"./chunk-VJUTQAPJ.js";import{a as He,b as Je,c as N,d as Ke}from"./chunk-DJEZ2HVU.js";import{a as ze,b as Xe,e as Qe}from"./chunk-DSYKYZMT.js";import"./chunk-IKBHHTIT.js";import"./chunk-QOYAX2FT.js";import"./chunk-4YCH6IZV.js";import{execa as Rt}from"execa";import{existsSync as le,promises as ee}from"fs";import x from"path";import pe from"node-fetch";async function At(n,e){let t=n.split("/");if(t.length<2)throw new Error(`Invalid skill package ID: ${n}. Expected format: owner/repo`);let s=t[0],i=t[1],o=t[2];try{let a=o?`${s}/${i}/${o}`:`${s}/${i}`;await Rt("npx",["-y","skills","add",a],{cwd:e,stdio:"pipe"}),m.success(`Installed skill package: ${n}`);let r=x.resolve(e,".skills",`${s}_${i}`);if(le(r))return await de(r,n);let l=x.resolve(e,".skills");return le(l)?await de(l,n):[]}catch{return m.info(`Fetching skills directly from GitHub: ${s}/${i}`),await Mt(s,i,o,e)}}async function Mt(n,e,t,s){let i=[];try{let o=`https://api.github.com/repos/${n}/${e}/git/trees/main?recursive=1`,a=await pe(o);if(!a.ok)throw new Error(`GitHub API error: ${a.status}`);let l=(await a.json()).tree.filter(c=>c.type==="blob"&&c.path.endsWith("SKILL.md"));if(t){let c=l.find(g=>g.path.includes(`/${t}/`)||g.path===`${t}/SKILL.md`);if(c){let g=await ce(n,e,c.path,s);g&&i.push(g)}}else for(let c of l){let g=await ce(n,e,c.path,s);g&&i.push(g)}}catch(o){m.error(`Failed to fetch skills from GitHub: ${o.message}`)}return i}async function ce(n,e,t,s){try{let i=`https://raw.githubusercontent.com/${n}/${e}/main/${t}`,o=await pe(i);if(!o.ok)return null;let a=await o.text(),r=N(a);if(r){r.source="remote",r.packageId=`${n}/${e}`;let l=x.dirname(t),c=x.resolve(s,".skills",`${n}_${e}`,l);await ee.mkdir(c,{recursive:!0}),await ee.writeFile(x.resolve(c,"SKILL.md"),a),r.path=x.resolve(c,"SKILL.md")}return r}catch{return null}}async function de(n,e){let t=[],{default:s}=await import("fast-glob"),i=await s.glob("**/SKILL.md",{cwd:n,deep:5});for(let o of i)try{let a=x.resolve(n,o),r=await ee.readFile(a,"utf8"),l=N(r);l&&(l.source="remote",l.path=a,l.packageId=e,t.push(l))}catch{}return t}async function Et(n,e,t,s,i){let o=["---",`name: ${n}`,`description: ${e}`];if(s?.length){o.push("tags:");for(let a of s)o.push(` - ${a}`)}if(i?.length){o.push("globs:");for(let a of i)o.push(` - ${a}`)}return o.push("---"),`${o.join(`
1
+ import{a as U}from"./chunk-4ITQP3XL.js";import{a as yt,b as re,c as ae,d as vt,e as xt,f as Ct,m as bt,n as $t,o as Tt}from"./chunk-X5D5AV5N.js";import"./chunk-2HXSJYAJ.js";import"./chunk-N67JZAXU.js";import"./chunk-AFRHKA7B.js";import"./chunk-EW27SCCC.js";import{A as H,B as E,C as I,a as be,b as $e,c as Te,d as je,e as _e,f as Ue,g as Be,h as We,i as qe,j as Ve,k as Ye,l as F,m as Q,n as Ze,o as et,p as tt,q as j,r as st,s as ot,t as nt,u as P,v as Z,w as it,x as rt,y as at,z as ht}from"./chunk-VYLZ7UYM.js";import{a as X,b as M,c as _}from"./chunk-SFQUP3BP.js";import{f as Pt}from"./chunk-ABVZHJUO.js";import"./chunk-P3TJHGO5.js";import{c as kt,e as wt,f as St}from"./chunk-CO4YCIIA.js";import"./chunk-BHSPAP4U.js";import"./chunk-CDNWCYF7.js";import{c as lt,d as ct,e as dt,f as pt,n as ut}from"./chunk-OZOQKPJV.js";import"./chunk-5F3J55LG.js";import"./chunk-GOLZ6NEL.js";import"./chunk-WJXU5F4Z.js";import"./chunk-VWTBHLMX.js";import{a as gt,b as mt,e as ft}from"./chunk-7P5QRX5W.js";import"./chunk-JTJIPNXY.js";import"./chunk-64P4QHSZ.js";import"./chunk-BFZUG66X.js";import"./chunk-SR3X25TN.js";import"./chunk-53NRX2NA.js";import"./chunk-A6BKSZQA.js";import{a as Re,b as Ae,c as Me,d as Ee,e as Ie,f as Oe,g as Ge,h as De,i as Le,j as Ne,k as Fe,l as A}from"./chunk-JGXE2YNV.js";import{a as m}from"./chunk-VJUTQAPJ.js";import{a as He,b as Je,c as N,d as Ke}from"./chunk-DJEZ2HVU.js";import{a as ze,b as Xe,e as Qe}from"./chunk-DSYKYZMT.js";import"./chunk-IKBHHTIT.js";import"./chunk-QOYAX2FT.js";import"./chunk-4YCH6IZV.js";import{execa as Rt}from"execa";import{existsSync as le,promises as ee}from"fs";import x from"path";import pe from"node-fetch";async function At(n,e){let t=n.split("/");if(t.length<2)throw new Error(`Invalid skill package ID: ${n}. Expected format: owner/repo`);let s=t[0],i=t[1],o=t[2];try{let a=o?`${s}/${i}/${o}`:`${s}/${i}`;await Rt("npx",["-y","skills","add",a],{cwd:e,stdio:"pipe"}),m.success(`Installed skill package: ${n}`);let r=x.resolve(e,".skills",`${s}_${i}`);if(le(r))return await de(r,n);let l=x.resolve(e,".skills");return le(l)?await de(l,n):[]}catch{return m.info(`Fetching skills directly from GitHub: ${s}/${i}`),await Mt(s,i,o,e)}}async function Mt(n,e,t,s){let i=[];try{let o=`https://api.github.com/repos/${n}/${e}/git/trees/main?recursive=1`,a=await pe(o);if(!a.ok)throw new Error(`GitHub API error: ${a.status}`);let l=(await a.json()).tree.filter(c=>c.type==="blob"&&c.path.endsWith("SKILL.md"));if(t){let c=l.find(g=>g.path.includes(`/${t}/`)||g.path===`${t}/SKILL.md`);if(c){let g=await ce(n,e,c.path,s);g&&i.push(g)}}else for(let c of l){let g=await ce(n,e,c.path,s);g&&i.push(g)}}catch(o){m.error(`Failed to fetch skills from GitHub: ${o.message}`)}return i}async function ce(n,e,t,s){try{let i=`https://raw.githubusercontent.com/${n}/${e}/main/${t}`,o=await pe(i);if(!o.ok)return null;let a=await o.text(),r=N(a);if(r){r.source="remote",r.packageId=`${n}/${e}`;let l=x.dirname(t),c=x.resolve(s,".skills",`${n}_${e}`,l);await ee.mkdir(c,{recursive:!0}),await ee.writeFile(x.resolve(c,"SKILL.md"),a),r.path=x.resolve(c,"SKILL.md")}return r}catch{return null}}async function de(n,e){let t=[],{default:s}=await import("fast-glob"),i=await s.glob("**/SKILL.md",{cwd:n,deep:5});for(let o of i)try{let a=x.resolve(n,o),r=await ee.readFile(a,"utf8"),l=N(r);l&&(l.source="remote",l.path=a,l.packageId=e,t.push(l))}catch{}return t}async function Et(n,e,t,s,i){let o=["---",`name: ${n}`,`description: ${e}`];if(s?.length){o.push("tags:");for(let a of s)o.push(` - ${a}`)}if(i?.length){o.push("globs:");for(let a of i)o.push(` - ${a}`)}return o.push("---"),`${o.join(`
2
2
  `)}
3
3
 
4
4
  ${t}`}async function It(n){let{loadLocalSkills:e}=await import("./loader-5CA3O5GM.js");return e(n)}async function Ot(n,e,t,s,i){let a=[{role:"system",content:`You are a skill author for the Agent Skills ecosystem (skills.sh).
@@ -0,0 +1,2 @@
1
+ import{a}from"./chunk-OPKQNLGX.js";import"./chunk-WHP4I44J.js";import"./chunk-CNNLREHD.js";import"./chunk-KX626FN4.js";import"./chunk-GV7S5BAX.js";import"./chunk-SZDACK6O.js";import"./chunk-GSX4NK2N.js";import"./chunk-X5D5AV5N.js";import"./chunk-2HXSJYAJ.js";import"./chunk-N67JZAXU.js";import"./chunk-AFRHKA7B.js";import"./chunk-EW27SCCC.js";import"./chunk-VYLZ7UYM.js";import"./chunk-SFQUP3BP.js";import"./chunk-ABVZHJUO.js";import"./chunk-P3TJHGO5.js";import"./chunk-CO4YCIIA.js";import"./chunk-BHSPAP4U.js";import"./chunk-CDNWCYF7.js";import"./chunk-OZOQKPJV.js";import"./chunk-5F3J55LG.js";import"./chunk-GOLZ6NEL.js";import"./chunk-WJXU5F4Z.js";import"./chunk-VWTBHLMX.js";import"./chunk-7P5QRX5W.js";import"./chunk-JTJIPNXY.js";import"./chunk-64P4QHSZ.js";import"./chunk-BFZUG66X.js";import"./chunk-SR3X25TN.js";import"./chunk-53NRX2NA.js";import"./chunk-A6BKSZQA.js";import"./chunk-JGXE2YNV.js";import"./chunk-VJUTQAPJ.js";import"./chunk-DJEZ2HVU.js";import"./chunk-DSYKYZMT.js";import"./chunk-IKBHHTIT.js";import"./chunk-QOYAX2FT.js";import"./chunk-4YCH6IZV.js";export{a as buildProgram};
2
+ //# sourceMappingURL=program-GZOJIWTB.js.map
@@ -0,0 +1,2 @@
1
+ import{i as a,j as b,k as c,l as d,m as e,n as f}from"./chunk-OZOQKPJV.js";import"./chunk-5F3J55LG.js";import"./chunk-GOLZ6NEL.js";import"./chunk-WJXU5F4Z.js";import"./chunk-VWTBHLMX.js";import"./chunk-7P5QRX5W.js";import"./chunk-JTJIPNXY.js";import"./chunk-64P4QHSZ.js";import"./chunk-BFZUG66X.js";import"./chunk-SR3X25TN.js";import"./chunk-53NRX2NA.js";import"./chunk-A6BKSZQA.js";import"./chunk-JGXE2YNV.js";import"./chunk-VJUTQAPJ.js";import"./chunk-DSYKYZMT.js";import"./chunk-IKBHHTIT.js";import"./chunk-QOYAX2FT.js";import"./chunk-4YCH6IZV.js";export{f as AgentRegistry,c as TRACE_STEP_SUMMARY_BYTES,d as clipForTrace,e as emitTraceStepsFromStreamEvent,b as getGlobalRegistry,a as setGlobalRegistry};
2
+ //# sourceMappingURL=registry-QZIUSQ5J.js.map
@@ -0,0 +1,2 @@
1
+ import{e as a,f as b}from"./chunk-ABVZHJUO.js";import"./chunk-P3TJHGO5.js";import"./chunk-CO4YCIIA.js";import"./chunk-GOLZ6NEL.js";import"./chunk-VJUTQAPJ.js";import"./chunk-4YCH6IZV.js";export{b as CronScheduler,a as getNextCronDate};
2
+ //# sourceMappingURL=scheduler-B2BQPV4M.js.map
@@ -0,0 +1,2 @@
1
+ import{a,b,c,d}from"./chunk-BHSPAP4U.js";import"./chunk-WJXU5F4Z.js";import"./chunk-4YCH6IZV.js";export{b as generateClaudeMd,a as readManagedHash,d as setupAllWorkspaces,c as setupWorkspace};
2
+ //# sourceMappingURL=workspace-setup-DQCZX2NE.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentix-cli",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "AI operations layer for small & medium businesses. Routes Telegram, WhatsApp, Slack, Discord, GitLab, crons, webhooks, and mesh tasks to AI agents on Claude, OpenAI, or any LLM. Web wizard for non-technical operators, CLI for engineers. Self-hosted.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/intent/ledger.ts","../src/intent/ulid.ts","../src/intent/instance.ts"],"sourcesContent":["import Database from \"better-sqlite3\"\nimport { existsSync, mkdirSync } from \"fs\"\nimport { dirname, resolve } from \"path\"\nimport { newEventId } from \"./ulid\"\nimport type {\n IntentDecision,\n IntentDivergence,\n IntentEvent,\n IntentEventInput,\n IntentResolution,\n IntentSource,\n} from \"./types\"\n\n// Intent ledger — Phase 1 of the architectural rescue.\n//\n// Append-only SQLite store that becomes the single source of truth for\n// dispatch decisions across all sources (channel router, workflow\n// dispatcher, gitlab handler, cron, mesh). This commit only opens the db\n// and runs the schema migrations. The read/write API arrives in commit 2;\n// the `decideAndCommit` skeleton in commit 3.\n//\n// Lives in its own database file (`.agentx/intent/ledger.sqlite`) rather\n// than sharing the existing observability db at `.agentx/db.sqlite`:\n// - Different lifecycle: dispatch is hotter than observability, no point\n// making fsync compete.\n// - Different durability story: the ledger is canonical state; the\n// observability tables are derived. Different backup/retention policies.\n// - Cleaner schema separation — ledger migrations don't entangle with the\n// observability ones.\n//\n// WAL mode + synchronous=NORMAL — same balance the observability db uses.\n\nexport interface OpenLedgerOptions {\n /** Resolved relative to cwd. Default: .agentx/intent/ledger.sqlite */\n path?: string\n}\n\n/**\n * IntentLedger handle. One instance per database file. Tests open per-test\n * tmp instances; the daemon opens one process-global instance (added in a\n * later commit when daemon wiring lands).\n */\nexport class IntentLedger {\n readonly db: Database.Database\n readonly path: string\n\n constructor(opts: OpenLedgerOptions = {}) {\n this.path = resolve(process.cwd(), opts.path ?? \".agentx/intent/ledger.sqlite\")\n mkdirSync(dirname(this.path), { recursive: true })\n this.db = new Database(this.path)\n this.db.pragma(\"journal_mode = WAL\")\n this.db.pragma(\"synchronous = NORMAL\")\n this.db.pragma(\"foreign_keys = ON\")\n runMigrations(this.db)\n }\n\n /** Test-only — production never closes. */\n close(): void {\n this.db.close()\n }\n\n /** Current schema version. New migrations append to the runMigrations\n * list and bump the version. Useful for tests + diagnostics. */\n schemaVersion(): number {\n const row = this.db.prepare(\"SELECT MAX(v) AS v FROM schema_version\").get() as { v: number | null }\n return row.v ?? 0\n }\n\n // -------------------------------------------------------------------------\n // Events\n // -------------------------------------------------------------------------\n\n /**\n * Append an event. Idempotent on `(source, sourceEventId)` — a re-delivery\n * with the same external id returns the previously-stored row without\n * inserting a duplicate. Events with `sourceEventId === null` (cron tics,\n * ad-hoc internal events) are NOT deduplicated; each call writes a new row.\n *\n * Returns the canonical (existing or new) event row including its id.\n */\n recordEvent(input: IntentEventInput): IntentEvent {\n if (input.sourceEventId !== null) {\n const existing = this.findEventBySourceId(input.source, input.sourceEventId)\n if (existing) return existing\n }\n const event: IntentEvent = {\n id: input.id ?? newEventId(input.ts),\n ts: input.ts,\n source: input.source,\n sourceEventId: input.sourceEventId,\n project: input.project,\n subject: input.subject,\n intent: input.intent,\n rawJson: input.rawJson,\n }\n this.db\n .prepare(\n `INSERT INTO intent_events (id, ts, source, source_event_id, project, subject, intent, raw_json)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n event.id,\n event.ts,\n event.source,\n event.sourceEventId,\n event.project,\n event.subject,\n event.intent,\n event.rawJson,\n )\n return event\n }\n\n /** Idempotency probe — used by `recordEvent` and by external callers that\n * want to check existence before constructing a full event payload. */\n findEventBySourceId(source: IntentSource, sourceEventId: string): IntentEvent | null {\n const row = this.db\n .prepare(\n `SELECT id, ts, source, source_event_id, project, subject, intent, raw_json\n FROM intent_events WHERE source = ? AND source_event_id = ?`,\n )\n .get(source, sourceEventId) as EventRow | undefined\n return row ? rowToEvent(row) : null\n }\n\n /** Lookup by primary key. Returns null when not found. */\n getEventById(id: string): IntentEvent | null {\n const row = this.db\n .prepare(\n `SELECT id, ts, source, source_event_id, project, subject, intent, raw_json\n FROM intent_events WHERE id = ?`,\n )\n .get(id) as EventRow | undefined\n return row ? rowToEvent(row) : null\n }\n\n // -------------------------------------------------------------------------\n // Decisions\n // -------------------------------------------------------------------------\n\n /**\n * Append a decision. Throws on `(eventId, decidedBy)` conflict — decisions\n * are once-and-only-once per decider. Idempotency for the *event* is the\n * caller's responsibility (use `recordEvent` for that); decisions\n * intentionally do not retry-coalesce so a buggy caller surfaces loudly\n * instead of silently overwriting prior judgement.\n */\n recordDecision(decision: IntentDecision): void {\n this.db\n .prepare(\n `INSERT INTO intent_decisions (event_id, decided_at, decided_by, agent_id, outcome, reason)\n VALUES (?, ?, ?, ?, ?, ?)`,\n )\n .run(\n decision.eventId,\n decision.decidedAt,\n decision.decidedBy,\n decision.agentId,\n decision.outcome,\n decision.reason,\n )\n }\n\n /** All decisions for one event, oldest first. The chain (channel-router →\n * pm-gate → workflow) reads naturally in insertion order. */\n getDecisionsForEvent(eventId: string): IntentDecision[] {\n const rows = this.db\n .prepare(\n `SELECT event_id, decided_at, decided_by, agent_id, outcome, reason\n FROM intent_decisions WHERE event_id = ? ORDER BY decided_at ASC`,\n )\n .all(eventId) as DecisionRow[]\n return rows.map(rowToDecision)\n }\n\n /**\n * The active-task-safety check. Returns the most recent `dispatched`\n * decision for `(project, subject)` that has not yet been resolved\n * (no row in `intent_resolutions`). `decideAndCommit` (commit 3) consults\n * this to enforce the at-most-one-in-flight invariant.\n *\n * Returns null when project or subject is null — the active-task concept\n * is meaningless without both axes pinned.\n */\n getActiveDecisionForSubject(project: string | null, subject: string | null): IntentDecision | null {\n if (project === null || subject === null) return null\n const row = this.db\n .prepare(\n `SELECT d.event_id, d.decided_at, d.decided_by, d.agent_id, d.outcome, d.reason\n FROM intent_decisions d\n JOIN intent_events e ON e.id = d.event_id\n LEFT JOIN intent_resolutions r\n ON r.decision_event_id = d.event_id AND r.decision_decided_by = d.decided_by\n WHERE e.project = ? AND e.subject = ?\n AND d.outcome = 'dispatched'\n AND r.decision_event_id IS NULL\n ORDER BY d.decided_at DESC\n LIMIT 1`,\n )\n .get(project, subject) as DecisionRow | undefined\n return row ? rowToDecision(row) : null\n }\n\n // -------------------------------------------------------------------------\n // Resolutions\n // -------------------------------------------------------------------------\n\n /**\n * Append a resolution. Throws if a resolution already exists for the\n * decision — the ledger is append-only, so revising an outcome is a new\n * event, not an overwrite. The FK to `intent_decisions` ensures the\n * decision exists.\n */\n recordResolution(resolution: IntentResolution): void {\n this.db\n .prepare(\n `INSERT INTO intent_resolutions (decision_event_id, decision_decided_by, resolved_at, status, duration_ms, result_summary)\n VALUES (?, ?, ?, ?, ?, ?)`,\n )\n .run(\n resolution.decisionEventId,\n resolution.decisionDecidedBy,\n resolution.resolvedAt,\n resolution.status,\n resolution.durationMs,\n resolution.resultSummary,\n )\n }\n\n /**\n * Close every dispatched-and-unresolved decision by writing a\n * resolution with status \"canceled\" and the given reason. Used at\n * daemon startup to clean up dispatches whose agent execution died\n * with the previous process — without this, those decisions sit\n * in-flight forever and Inv-ActiveTaskSafety blocks legitimate\n * re-dispatches on the same slot.\n *\n * Returns the count of resolutions written. Idempotent — repeated\n * calls write 0 the second time because nothing remains in-flight.\n */\n cleanupOrphanedDispatches(reason: string = \"daemon-restart\", now: () => number = Date.now): number {\n const rows = this.db\n .prepare(`\n SELECT d.event_id, d.decided_by FROM intent_decisions d\n LEFT JOIN intent_resolutions r\n ON r.decision_event_id = d.event_id AND r.decision_decided_by = d.decided_by\n WHERE d.outcome = 'dispatched' AND r.decision_event_id IS NULL\n `)\n .all() as Array<{ event_id: string; decided_by: string }>\n const ts = now()\n let count = 0\n for (const row of rows) {\n try {\n this.recordResolution({\n decisionEventId: row.event_id,\n decisionDecidedBy: row.decided_by,\n resolvedAt: ts,\n status: \"canceled\",\n durationMs: null,\n resultSummary: reason,\n })\n count++\n } catch {\n // Skip — resolution already exists (race with normal completion).\n }\n }\n return count\n }\n\n /** Returns the resolution for a decision, or null if still in-flight. */\n getResolution(eventId: string, decidedBy: string): IntentResolution | null {\n const row = this.db\n .prepare(\n `SELECT decision_event_id, decision_decided_by, resolved_at, status, duration_ms, result_summary\n FROM intent_resolutions WHERE decision_event_id = ? AND decision_decided_by = ?`,\n )\n .get(eventId, decidedBy) as ResolutionRow | undefined\n return row ? rowToResolution(row) : null\n }\n\n // -------------------------------------------------------------------------\n // Divergences (shadow-mode soak surface)\n // -------------------------------------------------------------------------\n\n /**\n * Append a divergence record. Throws on duplicate `id`. The composite FK\n * to `intent_decisions` (`event_id`, `decided_by`) enforces that a\n * divergence cannot exist without its ledger-side decision — so callers\n * must record the decision before reporting the divergence.\n */\n recordDivergence(divergence: IntentDivergence): void {\n this.db\n .prepare(\n `INSERT INTO intent_divergences (\n id, ts, source, event_id, decided_by,\n ledger_agent_id, ledger_outcome, ledger_reason,\n legacy_agent_id, legacy_outcome, legacy_reason\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n divergence.id,\n divergence.ts,\n divergence.source,\n divergence.eventId,\n divergence.decidedBy,\n divergence.ledgerAgentId,\n divergence.ledgerOutcome,\n divergence.ledgerReason,\n divergence.legacyAgentId,\n divergence.legacyOutcome,\n divergence.legacyReason,\n )\n }\n\n /**\n * Recent divergences, newest first. Filters compose: `source` narrows to\n * one dispatch source, `since` to a recency window, `limit` to top-N.\n * The intended consumer is the soak dashboard (commit 8) and `agentx\n * audit --divergence` queries; per-record introspection is rare so we\n * don't expose a single-id getter.\n */\n getDivergences(opts: {\n source?: IntentSource\n since?: number\n limit?: number\n } = {}): IntentDivergence[] {\n const wheres: string[] = []\n const params: Array<string | number> = []\n if (opts.source !== undefined) {\n wheres.push(\"source = ?\")\n params.push(opts.source)\n }\n if (opts.since !== undefined) {\n wheres.push(\"ts >= ?\")\n params.push(opts.since)\n }\n const whereSql = wheres.length ? `WHERE ${wheres.join(\" AND \")}` : \"\"\n const limitSql = opts.limit !== undefined ? \"LIMIT ?\" : \"\"\n if (opts.limit !== undefined) params.push(opts.limit)\n\n const rows = this.db\n .prepare(\n `SELECT id, ts, source, event_id, decided_by,\n ledger_agent_id, ledger_outcome, ledger_reason,\n legacy_agent_id, legacy_outcome, legacy_reason\n FROM intent_divergences\n ${whereSql}\n ORDER BY ts DESC\n ${limitSql}`,\n )\n .all(...params) as DivergenceRow[]\n return rows.map(rowToDivergence)\n }\n}\n\n// --- Row → typed-object adapters --------------------------------------------\n//\n// SQLite returns snake_case rows; the typed surface uses camelCase. Mapping\n// once here keeps every consumer in TS-ergonomic shape and makes column\n// renames a single-file refactor.\n\ninterface EventRow {\n id: string\n ts: number\n source: string\n source_event_id: string | null\n project: string | null\n subject: string | null\n intent: string | null\n raw_json: string\n}\n\ninterface DecisionRow {\n event_id: string\n decided_at: number\n decided_by: string\n agent_id: string | null\n outcome: string\n reason: string | null\n}\n\ninterface ResolutionRow {\n decision_event_id: string\n decision_decided_by: string\n resolved_at: number\n status: string\n duration_ms: number | null\n result_summary: string | null\n}\n\ninterface DivergenceRow {\n id: string\n ts: number\n source: string\n event_id: string\n decided_by: string\n ledger_agent_id: string | null\n ledger_outcome: string\n ledger_reason: string | null\n legacy_agent_id: string | null\n legacy_outcome: string\n legacy_reason: string | null\n}\n\nfunction rowToEvent(row: EventRow): IntentEvent {\n return {\n id: row.id,\n ts: row.ts,\n source: row.source as IntentSource,\n sourceEventId: row.source_event_id,\n project: row.project,\n subject: row.subject,\n intent: row.intent,\n rawJson: row.raw_json,\n }\n}\n\nfunction rowToDecision(row: DecisionRow): IntentDecision {\n return {\n eventId: row.event_id,\n decidedAt: row.decided_at,\n decidedBy: row.decided_by,\n agentId: row.agent_id,\n outcome: row.outcome as IntentDecision[\"outcome\"],\n reason: row.reason,\n }\n}\n\nfunction rowToResolution(row: ResolutionRow): IntentResolution {\n return {\n decisionEventId: row.decision_event_id,\n decisionDecidedBy: row.decision_decided_by,\n resolvedAt: row.resolved_at,\n status: row.status as IntentResolution[\"status\"],\n durationMs: row.duration_ms,\n resultSummary: row.result_summary,\n }\n}\n\nfunction rowToDivergence(row: DivergenceRow): IntentDivergence {\n return {\n id: row.id,\n ts: row.ts,\n source: row.source as IntentSource,\n eventId: row.event_id,\n decidedBy: row.decided_by,\n ledgerAgentId: row.ledger_agent_id,\n ledgerOutcome: row.ledger_outcome as IntentDivergence[\"ledgerOutcome\"],\n ledgerReason: row.ledger_reason,\n legacyAgentId: row.legacy_agent_id,\n legacyOutcome: row.legacy_outcome as IntentDivergence[\"legacyOutcome\"],\n legacyReason: row.legacy_reason,\n }\n}\n\n/**\n * Per-version schema steps. New tables / columns go in a new migration\n * function — never mutate an old one. Each step is idempotent so re-running\n * on an already-current db is safe.\n */\nfunction runMigrations(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS schema_version (\n v INTEGER PRIMARY KEY\n );\n `)\n const current = (db\n .prepare(\"SELECT MAX(v) AS v FROM schema_version\")\n .get() as { v: number | null }).v ?? 0\n\n if (current < 1) migrationV1(db)\n if (current < 2) migrationV2(db)\n}\n\nfunction migrationV1(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS intent_events (\n -- TEXT PRIMARY KEY isn't auto-NOT-NULL in SQLite; spell it out so the\n -- ULID invariant is enforced at the schema level rather than relying on\n -- the application layer.\n id TEXT NOT NULL PRIMARY KEY,\n ts INTEGER NOT NULL,\n source TEXT NOT NULL,\n source_event_id TEXT,\n project TEXT,\n subject TEXT,\n intent TEXT,\n raw_json TEXT NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_intent_events_subject\n ON intent_events (project, subject);\n CREATE INDEX IF NOT EXISTS idx_intent_events_source_event_id\n ON intent_events (source, source_event_id);\n CREATE INDEX IF NOT EXISTS idx_intent_events_ts\n ON intent_events (ts);\n\n CREATE TABLE IF NOT EXISTS intent_decisions (\n event_id TEXT NOT NULL REFERENCES intent_events(id),\n decided_at INTEGER NOT NULL,\n decided_by TEXT NOT NULL,\n agent_id TEXT,\n outcome TEXT NOT NULL,\n reason TEXT,\n PRIMARY KEY (event_id, decided_by)\n );\n\n CREATE INDEX IF NOT EXISTS idx_intent_decisions_agent\n ON intent_decisions (agent_id, decided_at);\n CREATE INDEX IF NOT EXISTS idx_intent_decisions_outcome\n ON intent_decisions (outcome, decided_at);\n\n CREATE TABLE IF NOT EXISTS intent_resolutions (\n decision_event_id TEXT NOT NULL,\n decision_decided_by TEXT NOT NULL,\n resolved_at INTEGER NOT NULL,\n status TEXT NOT NULL,\n duration_ms INTEGER,\n result_summary TEXT,\n PRIMARY KEY (decision_event_id, decision_decided_by),\n FOREIGN KEY (decision_event_id, decision_decided_by)\n REFERENCES intent_decisions(event_id, decided_by)\n );\n\n CREATE INDEX IF NOT EXISTS idx_intent_resolutions_status\n ON intent_resolutions (status, resolved_at);\n `)\n db.prepare(\"INSERT INTO schema_version (v) VALUES (1)\").run()\n}\n\nfunction migrationV2(db: Database.Database): void {\n // Phase 1 commit 5 — divergence reporter surface. Each row records one\n // observed mismatch between the ledger's decision and the legacy path's\n // outcome during shadow-mode operation. The composite FK to\n // intent_decisions ensures a divergence cannot exist without its\n // ledger-side decision row.\n db.exec(`\n CREATE TABLE IF NOT EXISTS intent_divergences (\n id TEXT NOT NULL PRIMARY KEY,\n ts INTEGER NOT NULL,\n source TEXT NOT NULL,\n event_id TEXT NOT NULL,\n decided_by TEXT NOT NULL,\n ledger_agent_id TEXT,\n ledger_outcome TEXT NOT NULL,\n ledger_reason TEXT,\n legacy_agent_id TEXT,\n legacy_outcome TEXT NOT NULL,\n legacy_reason TEXT,\n FOREIGN KEY (event_id, decided_by)\n REFERENCES intent_decisions(event_id, decided_by)\n );\n\n -- \"show me recent divergences from gitlab\" — the soak's primary query\n CREATE INDEX IF NOT EXISTS idx_intent_divergences_source_ts\n ON intent_divergences (source, ts);\n -- \"are any deciders consistently wrong\" — per-decider analysis\n CREATE INDEX IF NOT EXISTS idx_intent_divergences_decided_by\n ON intent_divergences (decided_by, ts);\n `)\n db.prepare(\"INSERT INTO schema_version (v) VALUES (2)\").run()\n}\n\n// existsSync is imported for symmetry with src/storage/sqlite.ts; the\n// ledger constructor doesn't currently branch on file presence (the\n// migration is idempotent regardless), but keeping the import documents\n// the intent should later code need to distinguish first-open from re-open.\nvoid existsSync\n","import { randomBytes } from \"crypto\"\n\n// In-tree ULID generator — Phase 1 of the architectural rescue.\n//\n// The ledger demands monotonically-sortable ids so chronological queries\n// can use ID ranges and the audit trail reads coherently. ULIDs give us\n// that (10-char Crockford-base32 timestamp + 16-char randomness, total\n// 26 chars). The full `ulid` npm package is overkill for the few hundred\n// lines of generation logic we need, and adding a dep means another\n// `pnpm install --prod` step on clawd-server. So we inline.\n//\n// This is not a cryptographic primitive — its job is to be a unique,\n// time-sortable identifier. Random bytes come from `crypto.randomBytes`\n// purely because it's the right tool for \"give me bytes\" and there's no\n// reason to use weaker randomness.\n\nconst ENCODING = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\" // Crockford base32\n\nconst TIME_LEN = 10\nconst RAND_LEN = 16\n\nfunction encodeTime(ms: number): string {\n let out = \"\"\n for (let i = TIME_LEN - 1; i >= 0; i--) {\n out = ENCODING[ms & 31] + out\n ms = Math.floor(ms / 32)\n }\n return out\n}\n\nfunction encodeRandom(): string {\n const bytes = randomBytes(RAND_LEN)\n let out = \"\"\n for (let i = 0; i < RAND_LEN; i++) {\n out += ENCODING[bytes[i] & 31]\n }\n return out\n}\n\n/** Generate a new ULID. Pass `now` for deterministic tests. */\nexport function newEventId(now: number = Date.now()): string {\n return encodeTime(now) + encodeRandom()\n}\n\n/** Extract the timestamp portion from a ULID. Inverse of `newEventId`'s\n * time prefix. Useful for sanity checks; not load-bearing. */\nexport function decodeTime(id: string): number {\n let ms = 0\n for (let i = 0; i < TIME_LEN; i++) {\n const v = ENCODING.indexOf(id[i])\n if (v < 0) throw new Error(`invalid ULID character at position ${i}: ${id[i]}`)\n ms = ms * 32 + v\n }\n return ms\n}\n","import { IntentLedger } from \"./ledger\"\n\n// Daemon-process singleton accessor for the IntentLedger.\n//\n// Phase 1 commit 6.0 — scaffolding for the call-site wiring that lands in\n// 6.a (gitlab), 6.b (router), 6.c (workflow), 6.d (cron + mesh). Each\n// source's wrapper will read this singleton when the mode is non-\"off\".\n//\n// The singleton is lazy because the ledger isn't needed in production\n// until the operator flips INTENT_LEDGER_MODE off the default. Avoiding\n// eager construction means an unflipped agentx process never opens the\n// ledger db, never holds the file, never writes WAL pages — exactly what\n// \"deploy-time default is off\" means in practice.\n//\n// Tests use `setLedgerForTesting` to inject an in-memory or tmp-dir\n// ledger and `resetLedgerForTesting` to release it between cases. Outside\n// of tests, callers go through `getDefaultLedger()`.\n\nlet _ledger: IntentLedger | undefined\n\nexport interface DefaultLedgerOptions {\n /** Path override. Default: `.agentx/intent/ledger.sqlite` (resolved\n * against cwd by the IntentLedger constructor). */\n path?: string\n}\n\n/**\n * Lazy singleton getter. Idempotent — repeated calls return the same\n * handle. Constructs the ledger on first call.\n *\n * Throws on construction failure (file unwritable, native binding broken,\n * etc.). Call sites should gate the call behind `getLedgerMode(...) !== \"off\"`\n * so a bad ledger never reaches construction in normal \"off\" production.\n *\n * The decision to throw rather than fail-soft (return null like\n * src/storage/sqlite.ts does) is deliberate: the ledger is canonical\n * state once `mode !== \"off\"`, so a silent failure would leave the\n * dispatcher in an undefined \"I tried to record but didn't\" state. Loud\n * failure surfaces misconfig at startup rather than days into a soak.\n */\nexport function getDefaultLedger(opts: DefaultLedgerOptions = {}): IntentLedger {\n if (_ledger) return _ledger\n _ledger = new IntentLedger({ path: opts.path })\n return _ledger\n}\n\n/** Test-only: inject a pre-built ledger so per-test tmp dirs work without\n * fighting the singleton. Production code MUST NOT call this. */\nexport function setLedgerForTesting(ledger: IntentLedger): void {\n _ledger = ledger\n}\n\n/** Test-only: drop the singleton reference. Use in `afterEach` so the\n * next test's `setLedgerForTesting` or `getDefaultLedger` starts fresh.\n * Does NOT close the underlying db — the caller (test) owns close(). */\nexport function resetLedgerForTesting(): void {\n _ledger = undefined\n}\n"],"mappings":"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","names":["Database","existsSync","mkdirSync","dirname","resolve","randomBytes","ENCODING","TIME_LEN","RAND_LEN","encodeTime","ms","out","i","encodeRandom","bytes","newEventId","now","IntentLedger","opts","resolve","mkdirSync","dirname","Database","runMigrations","input","existing","event","newEventId","source","sourceEventId","row","rowToEvent","id","decision","eventId","rowToDecision","project","subject","resolution","reason","now","rows","ts","count","decidedBy","rowToResolution","divergence","wheres","params","whereSql","limitSql","rowToDivergence","db","current","migrationV1","migrationV2","_ledger","getDefaultLedger","opts","IntentLedger"]}
@@ -1,13 +0,0 @@
1
- import{z as e}from"zod";import{readFileSync as x,existsSync as f}from"fs";import{resolve as g}from"path";import{z as t}from"zod";var S=t.enum(["mon","tue","wed","thu","fri","sat","sun"]),j=t.object({days:t.array(S).default(["mon","tue","wed","thu","fri"]),start:t.string().regex(/^\d{2}:\d{2}$/),end:t.string().regex(/^\d{2}:\d{2}$/),lunch:t.object({start:t.string().regex(/^\d{2}:\d{2}$/),end:t.string().regex(/^\d{2}:\d{2}$/)}).optional()}),w=t.object({title:t.string(),responsibilities:t.array(t.string()).default([]),sopPath:t.string().optional(),kpis:t.array(t.string()).default([])}),C=t.object({role:t.string(),reportsTo:t.string().optional(),schedule:j,utilizationTarget:t.number().min(0).max(1).default(.8)}),v=t.object({channel:t.string(),chatId:t.string(),accountId:t.string().optional()}),T=t.discriminatedUnion("type",[t.object({type:t.literal("backlog"),path:t.string().default(".agentx/backlog.md")}),t.object({type:t.literal("gitlab"),projects:t.array(t.string()).default([])}),t.object({type:t.literal("wiki"),path:t.string(),glob:t.string().default("**/*.md")})]),$=t.object({id:t.string(),pm:t.string().optional(),client:t.string().optional()}),I=t.object({channel:t.string().optional(),chatId:t.string().optional(),username:t.string().optional(),senderId:t.string().optional(),client:t.string(),project:t.string().optional(),displayName:t.string().optional()}),b=t.object({enabled:t.boolean().default(!1),timezone:t.string().default("UTC"),mainChannel:v,workSource:T,roles:t.record(t.string(),w).default({}),orgChart:t.record(t.string(),C).default({}),projects:t.array($).default([]),contactMap:t.array(I).default([]),workTickMinutes:t.number().int().min(1).max(60).default(15),idleQueueThreshold:t.number().int().min(0).default(0)});import{z as n}from"zod";var L=n.object({name:n.string(),color:n.string().regex(/^#[0-9a-f]{6}$/i).default("#6366f1"),description:n.string().optional()}),z=n.object({id:n.string().regex(/^[a-z0-9_-]+$/,"column id must be lowercase slug"),title:n.string(),kind:n.enum(["open-backlog","scoped-label","closed","label"]).default("label"),mapsToLabel:n.string().optional(),scopedLabel:n.string().optional(),scopedPrefix:n.string().default("Status"),accent:n.string().optional()}),B=n.object({enabled:n.boolean().default(!0),staleDoingMinutes:n.number().int().min(1).default(45),respectLunchBreak:n.boolean().default(!0),respectSchedule:n.boolean().default(!0),action:n.enum(["badge","notify"]).default("badge")}),D=n.discriminatedUnion("type",[n.object({type:n.literal("gitlab"),projects:n.array(n.string()).min(1)})]),P=[{id:"open",title:"Open",kind:"open-backlog",scopedPrefix:"Status",accent:"#6b7280"},{id:"todo",title:"To Do",kind:"scoped-label",scopedLabel:"Status::To Do",scopedPrefix:"Status",accent:"#fb7a35"},{id:"doing",title:"Doing",kind:"scoped-label",scopedLabel:"Status::Doing",scopedPrefix:"Status",accent:"#22c55e"},{id:"onhold",title:"On Hold",kind:"scoped-label",scopedLabel:"Status::On Hold",scopedPrefix:"Status",accent:"#f59e0b"},{id:"review",title:"Review",kind:"scoped-label",scopedLabel:"Status::Review",scopedPrefix:"Status",accent:"#3b82f6"},{id:"closed",title:"Closed",kind:"closed",scopedPrefix:"Status",accent:"#6b7280"}],A=n.object({id:n.string().regex(/^[a-z0-9_-]+$/,"board id must be lowercase slug"),name:n.string(),source:D,primaryToolLabel:n.string().optional(),labels:n.array(L).default([]),columns:n.array(z).min(1).default(P),timeRangeDays:n.number().int().min(1).max(365).default(30),closedWindowDays:n.number().int().min(1).max(365).default(30),reconciliation:B.default({})}),h=n.array(A).default([]),M=n.object({name:n.string(),url:n.string(),dashboardUrl:n.string().optional(),token:n.string().optional(),dashboardToken:n.string().optional()}),y=n.object({enabled:n.boolean().default(!1),port:n.number().int().default(4202),bind:n.string().default("127.0.0.1"),token:n.string().optional(),daemonUrl:n.string().default("http://localhost:18800"),daemons:n.array(M).default([]),draftAgent:n.string().optional()}).default({});function Y(a,i){let s=new Set(a.labels||[]);if(a.state==="closed"){let r=i.find(l=>l.kind==="closed");if(r)return r.id}for(let r of i)if(r.kind==="scoped-label"&&r.scopedLabel&&s.has(r.scopedLabel))return r.id;for(let r of i)if(r.kind==="label"&&r.mapsToLabel&&s.has(r.mapsToLabel))return r.id;let o=i.find(r=>r.kind==="open-backlog");if(o){let r=o.scopedPrefix+"::";return[...s].some(c=>c.startsWith(r))?null:o.id}return i[0]?.id??null}function ee(a,i){let s=[],o=[],r=!1,l=!1;return i.kind==="scoped-label"&&i.scopedLabel?s.push(i.scopedLabel):i.kind==="label"&&i.mapsToLabel?s.push(i.mapsToLabel):i.kind==="closed"&&(r=!0),a&&(a.kind==="scoped-label"&&a.scopedLabel?o.push(a.scopedLabel):a.kind==="label"&&a.mapsToLabel?o.push(a.mapsToLabel):a.kind==="closed"&&i.kind!=="closed"&&(l=!0)),{addLabels:s.length?s:void 0,removeLabels:o.length?o:void 0,closeIssue:r||void 0,reopen:l||void 0}}function E(a){let i=g(a,".env");if(!f(i))return;let s=x(i,"utf-8");for(let o of s.split(`
2
- `)){let r=o.trim();if(!r||r.startsWith("#"))continue;let l=r.indexOf("=");if(l===-1)continue;let c=r.slice(0,l).trim(),d=r.slice(l+1).trim();process.env[c]||(process.env[c]=d)}}var O=e.object({apiKey:e.string().optional(),defaultModel:e.string().optional(),baseUrl:e.string().optional()}),R=e.object({command:e.string(),args:e.array(e.string()).default([]),env:e.record(e.string(),e.string()).optional()}),U=e.object({name:e.string(),workspace:e.string(),tier:e.enum(["claude-code","sdk","orchestrator"]).default("claude-code"),provider:e.string().optional(),model:e.string().optional(),systemPrompt:e.string().optional(),mentions:e.array(e.string()).default([]),intents:e.array(e.string()).default([]),maxDelegationDepth:e.number().int().min(0).max(50).default(5),mcp:e.record(e.string(),R).optional(),contextStrategy:e.enum(["layered","planner"]).optional(),contextReferences:e.boolean().default(!1),maxConcurrent:e.number().default(1),maxExecutionMinutes:e.number().int().min(1).max(240).default(20),permissionMode:e.string().default("default"),queueMode:e.enum(["collect","followup","drop"]).default("collect"),heartbeat:e.object({enabled:e.boolean().default(!1),intervalMinutes:e.number().default(30),prompt:e.string().default("Check inbox, pending tasks, and system health. Report anything that needs attention."),channel:e.string().default("heartbeat")}).default({}),access:e.enum(["private","public"]).default("private")}),_=e.object({token:e.string(),agentBinding:e.string(),allowFrom:e.array(e.string()).optional(),pollInbound:e.boolean().default(!0)}),N=e.object({telegram:e.object({enabled:e.boolean().default(!1),accounts:e.record(e.string(),_).default({}),policy:e.object({dm:e.enum(["pair","block"]).default("pair"),group:e.enum(["mention-required","all"]).default("mention-required"),allowFrom:e.array(e.string()).optional()}).default({})}).default({}),whatsapp:e.object({enabled:e.boolean().default(!1),sessionDir:e.string().default(".agentx/whatsapp-sessions"),defaultAgent:e.string().optional(),allowFrom:e.array(e.string()).optional(),routes:e.array(e.object({contact:e.string().optional(),group:e.string().optional(),agent:e.string()})).default([]),ingest:e.object({enabled:e.boolean().default(!1),mode:e.enum(["metadata-only","messages"]).default("metadata-only"),allowContacts:e.array(e.string()).default([]),allowGroups:e.array(e.string()).default([]),denyContacts:e.array(e.string()).default([]),denyGroups:e.array(e.string()).default([]),messageCap:e.number().int().min(1).max(500).default(50),historyDays:e.number().int().min(1).max(365).default(30),contactRefreshDays:e.number().int().min(1).max(90).default(7),throttle:e.object({minMsBetweenCalls:e.number().int().min(100).default(1500),maxCallsPerMinute:e.number().int().min(1).default(20),maxChatsPerSweep:e.number().int().min(1).default(25)}).default({}),retentionDays:e.number().int().min(0).default(0)}).default({})}).default({}),discord:e.object({enabled:e.boolean().default(!1),token:e.string().optional(),agentBinding:e.string().optional()}).default({}),slack:e.object({enabled:e.boolean().default(!1),botToken:e.string().optional(),appToken:e.string().optional(),agentBinding:e.string().optional()}).default({}),gitlab:e.object({enabled:e.boolean().default(!1),webhookPort:e.number().default(18810),webhookSecret:e.string().optional(),host:e.string().default("https://gitlab.com"),token:e.string().optional(),routes:e.array(e.object({project:e.string(),agent:e.string()})).default([]),agentMappings:e.array(e.object({agentId:e.string(),gitlabUsernames:e.array(e.string()).default([]),keywords:e.array(e.string()).default([]),token:e.string().optional(),node:e.string().optional()})).default([])}).default({}),github:e.object({enabled:e.boolean().default(!1),token:e.string().optional(),tokenFile:e.string().optional(),appId:e.number().optional(),clientId:e.string().optional(),privateKeyFile:e.string().optional(),webhookSecret:e.string().optional(),routes:e.array(e.object({repo:e.string(),agent:e.string()})).default([]),agentMappings:e.array(e.object({agentId:e.string(),githubUsernames:e.array(e.string()).default([]),token:e.string().optional(),tokenFile:e.string().optional(),node:e.string().optional()})).default([])}).default({}),webrtc:e.object({enabled:e.boolean().default(!1),stunServers:e.array(e.string()).default(["stun:stun.l.google.com:19302"]),turnServers:e.array(e.object({urls:e.string(),username:e.string().optional(),credential:e.string().optional()})).default([]),allowedCallers:e.array(e.string()).default([]),ringNotify:e.array(e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()})).default([]),callUrlBase:e.string().optional(),bot:e.object({enabled:e.boolean().default(!1),defaultAgentId:e.string().optional(),whisperBackend:e.enum(["auto","mlx","openai"]).default("auto"),whisperModel:e.string().optional(),whisperLanguage:e.string().default("auto"),mlxBinary:e.string().optional(),transcriptChannel:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional(),maxCallMinutes:e.number().int().min(1).max(240).default(30)}).default({})}).default({})}),F=e.object({enabled:e.boolean().default(!0),schedule:e.string(),timezone:e.string().default("UTC"),agent:e.string(),prompt:e.string(),timeout:e.number().default(600),model:e.string().optional(),maxOutputTokens:e.number().int().min(50).max(8e3).optional(),onError:e.union([e.enum(["log","notify","disable"]),e.array(e.enum(["log","notify","disable"]))]).default("log").transform(a=>Array.isArray(a)?a:[a]),notify:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional()}),W=e.object({name:e.string(),triggers:e.array(e.object({pattern:e.string(),channel:e.string().optional()})),allowedContacts:e.array(e.string()).optional(),agent:e.string(),prompt:e.string(),schedule:e.string().optional(),timezone:e.string().default("UTC"),notify:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional()}),q=e.object({url:e.string(),name:e.string(),token:e.string().optional()}),H=e.object({enabled:e.boolean().default(!1),peers:e.array(q).default([]),discovery:e.enum(["static","mdns"]).default("static"),healthCheck:e.object({interval:e.number().default(60),timeout:e.number().default(10)}).default({})}),J=e.object({enabled:e.boolean().default(!1),baseDir:e.string().default(".agentx/graph"),draftAgent:e.string().optional(),reviewAgent:e.string().optional(),autoApproveStructure:e.enum(["strict","extend-leaves","any"]).default("extend-leaves"),autoApproveConfidence:e.number().min(0).max(1).default(1),classifierModel:e.string().default("claude-haiku-4-5-20251001"),retrievalWeights:e.object({graph:e.number().min(0).default(.6),bm25:e.number().min(0).default(.4)}).default({})}).default({}),K=e.object({longTaskThreshold:e.number().default(30),destination:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional(),on:e.object({taskComplete:e.boolean().default(!0),taskError:e.boolean().default(!0),taskQueued:e.boolean().default(!1)}).default({})}).default({}),G=e.object({node:e.object({id:e.string(),name:e.string(),bind:e.string().default("127.0.0.1:18800"),defaultAgent:e.string().optional()}),providers:e.record(e.string(),O).default({}),agents:e.record(e.string(),U).default({}),channels:N.default({}),crons:e.record(e.string(),F).default({}),services:e.record(e.string(),W).default({}),notifications:K,mesh:H.default({}),business:b.optional(),boards:h,dashboard:y,graph:J,workflows:e.object({enabled:e.boolean().default(!1),dir:e.string().default(".agentx/workflows"),editor:e.enum(["disabled","readonly","edit"]).default("edit")}).default({}),webhooks:e.array(e.object({id:e.string().regex(/^[a-z0-9][a-z0-9_-]*$/,"webhook id must be lowercase slug"),source:e.enum(["gitlab","github","sentry","stripe","discord","slack","custom"]),agentId:e.string(),secretEnv:e.string().optional(),description:e.string().optional(),enabled:e.boolean().default(!0),node:e.string().optional(),triggers:e.record(e.string(),e.string()).optional(),defaultWorkflow:e.string().optional()})).default([]),session:e.object({staleMinutes:e.number().int().min(1).max(1440).default(45),maxTurnsPerSession:e.number().int().min(2).max(200).default(15),tierTwoThresholdTokens:e.number().int().min(5e4).max(2e5).default(195e3),contextStrategy:e.enum(["layered","planner"]).default("layered"),maxClaudeCodeDispatchesPerHour:e.number().int().min(1).max(1e4).default(80),maxClaudeCodeDispatchesPer5h:e.number().int().min(1).max(5e4).default(180)}).default({}),plugins:e.array(e.string()).default([])});function p(a){if(typeof a=="string")return a.replace(/\$\{(\w+)\}/g,(i,s)=>process.env[s]||"");if(Array.isArray(a))return a.map(p);if(a!==null&&typeof a=="object"){let i={};for(let[s,o]of Object.entries(a))i[s]=p(o);return i}return a}function Q(a,i){let s=[],o=new Set;for(let r of a){let l=r.path.join("."),c=/^channels\.telegram\.accounts\.([^.]+)\.token$/.exec(l);if(c&&r.message.toLowerCase().includes("required")){let d=c[1],u=`tg:${d}`;if(o.has(u))continue;o.add(u),s.push(`Hint: Telegram account "${d}" is enabled but has no bot token.
3
- Either:
4
- A) Open ${i} and set channels.telegram.accounts.${d}.token to your bot token (or "\${TG_${d.toUpperCase()}_BOT_TOKEN}" + the matching .env entry).
5
- B) If you don't need Telegram on this instance, remove the entire "telegram" block under "channels" (or set channels.telegram.enabled to false AND drop accounts.${d}).`)}}return s}function se(a){let i=a?[a]:[g(process.cwd(),"agentx.json"),g(process.cwd(),".agentx/config.json")];E(process.cwd());let s,o;for(let d of i)if(f(d)){s=x(d,"utf-8"),o=d;break}if(!s||!o)throw new Error(`No config found. Create agentx.json or .agentx/config.json
6
- Searched: ${i.join(", ")}`);let r;try{r=JSON.parse(s)}catch(d){throw new Error(`Invalid JSON in ${o}: ${d.message}`)}let l=p(r),c=G.safeParse(l);if(!c.success){let d=c.error.issues.map(m=>` ${m.path.join(".")}: ${m.message}`).join(`
7
- `),u=Q(c.error.issues,o),k=u.length?`
8
-
9
- ${u.join(`
10
-
11
- `)}`:"";throw new Error(`Config validation failed (${o}):
12
- ${d}${k}`)}return c.data}function le(a){let i=[];for(let[s,o]of Object.entries(a.agents)){if(!f(o.workspace)){i.push(`Agent "${s}": workspace not found at ${o.workspace}`);continue}if(o.tier==="claude-code"){let c=g(o.workspace,".claude");f(c)||i.push(`Agent "${s}": no .claude/ directory in workspace ${o.workspace}. Claude Code native features (hooks, MCP, skills) won't be available.`)}let r=o.provider||"claude",l=a.providers[r];o.tier!=="claude-code"&&(!l||!l.apiKey)&&i.push(`Agent "${s}": provider "${r}" has no API key configured. Set providers.${r}.apiKey in config or use tier "claude-code" for subscription.`)}for(let[s,o]of Object.entries(a.crons))a.agents[o.agent]||i.push(`Cron "${s}": references unknown agent "${o.agent}"`);if(a.channels.telegram.enabled)for(let[s,o]of Object.entries(a.channels.telegram.accounts))a.agents[o.agentBinding]||i.push(`Telegram account "${s}": references unknown agent "${o.agentBinding}"`);return i}export{Y as a,ee as b,G as c,p as d,se as e,le as f};
13
- //# sourceMappingURL=chunk-5GDCJ2SF.js.map