claude-flow 3.56.8 → 3.56.10

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 (92) hide show
  1. package/.claude/.proven-config-version +1 -0
  2. package/.claude/helpers/hook-handler.cjs +109 -23
  3. package/.claude/helpers/intelligence.cjs +51 -9
  4. package/.claude/proven-config.json +42 -0
  5. package/.claude/skills/verification-quality/SKILL.md +14 -11
  6. package/README.md +1 -1
  7. package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
  8. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts +1 -0
  9. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts.map +1 -1
  10. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js +3 -1
  11. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js.map +1 -1
  12. package/node_modules/@claude-flow/codex/dist/index.d.ts +3 -3
  13. package/node_modules/@claude-flow/codex/dist/index.js +1 -1
  14. package/node_modules/@claude-flow/codex/package.json +1 -1
  15. package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
  16. package/node_modules/@claude-flow/security/dist/input-validator.d.ts +6 -6
  17. package/node_modules/@claude-flow/security/dist/policy/envelope.js +6 -6
  18. package/node_modules/@claude-flow/security/dist/policy/envelope.js.map +1 -1
  19. package/node_modules/@claude-flow/security/package.json +1 -1
  20. package/package.json +5 -5
  21. package/v3/@claude-flow/cli/dist/src/autopilot-state.d.ts +2 -0
  22. package/v3/@claude-flow/cli/dist/src/autopilot-state.js +36 -8
  23. package/v3/@claude-flow/cli/dist/src/command-resolution.d.ts +15 -0
  24. package/v3/@claude-flow/cli/dist/src/command-resolution.js +25 -0
  25. package/v3/@claude-flow/cli/dist/src/commands/agent.js +17 -17
  26. package/v3/@claude-flow/cli/dist/src/commands/analyze.js +40 -29
  27. package/v3/@claude-flow/cli/dist/src/commands/claims.js +6 -8
  28. package/v3/@claude-flow/cli/dist/src/commands/daemon.js +86 -31
  29. package/v3/@claude-flow/cli/dist/src/commands/memory.js +14 -2
  30. package/v3/@claude-flow/cli/dist/src/commands/security-code-patterns.d.ts +2 -2
  31. package/v3/@claude-flow/cli/dist/src/commands/security-code-patterns.js +61 -3
  32. package/v3/@claude-flow/cli/dist/src/commands/security.d.ts +24 -0
  33. package/v3/@claude-flow/cli/dist/src/commands/security.js +81 -35
  34. package/v3/@claude-flow/cli/dist/src/commands/tui.d.ts +9 -0
  35. package/v3/@claude-flow/cli/dist/src/commands/tui.js +29 -3
  36. package/v3/@claude-flow/cli/dist/src/config/proven-config-refresh.d.ts +9 -0
  37. package/v3/@claude-flow/cli/dist/src/config/proven-config-refresh.js +24 -3
  38. package/v3/@claude-flow/cli/dist/src/funnel/local-signals.js +2 -1
  39. package/v3/@claude-flow/cli/dist/src/index.js +6 -42
  40. package/v3/@claude-flow/cli/dist/src/init/helpers-generator.js +109 -23
  41. package/v3/@claude-flow/cli/dist/src/init/types.js +1 -1
  42. package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-execute-core.d.ts +2 -0
  43. package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-execute-core.js +15 -0
  44. package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-health-metrics.d.ts +22 -0
  45. package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-health-metrics.js +33 -0
  46. package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-tools.js +45 -22
  47. package/v3/@claude-flow/cli/dist/src/mcp-tools/agentbbs-federation.d.ts +1 -1
  48. package/v3/@claude-flow/cli/dist/src/mcp-tools/agentbbs-federation.js +7 -2
  49. package/v3/@claude-flow/cli/dist/src/mcp-tools/agentdb-tools.js +25 -2
  50. package/v3/@claude-flow/cli/dist/src/mcp-tools/aidefence-known-misses.d.ts +22 -0
  51. package/v3/@claude-flow/cli/dist/src/mcp-tools/aidefence-known-misses.js +57 -0
  52. package/v3/@claude-flow/cli/dist/src/mcp-tools/analyze-tools.js +10 -11
  53. package/v3/@claude-flow/cli/dist/src/mcp-tools/autopilot-tools.js +2 -2
  54. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-intent-tools.js +6 -2
  55. package/v3/@claude-flow/cli/dist/src/mcp-tools/embeddings-tools.js +29 -7
  56. package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +4 -0
  57. package/v3/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +70 -20
  58. package/v3/@claude-flow/cli/dist/src/mcp-tools/neural-tools.js +28 -4
  59. package/v3/@claude-flow/cli/dist/src/mcp-tools/ruvllm-tools.js +6 -0
  60. package/v3/@claude-flow/cli/dist/src/mcp-tools/security-tools.js +55 -17
  61. package/v3/@claude-flow/cli/dist/src/mcp-tools/workflow-tools.js +66 -17
  62. package/v3/@claude-flow/cli/dist/src/memory/intelligence.d.ts +5 -1
  63. package/v3/@claude-flow/cli/dist/src/memory/intelligence.js +105 -29
  64. package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +10 -3
  65. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +2 -1
  66. package/v3/@claude-flow/cli/dist/src/memory/search-defaults.d.ts +54 -0
  67. package/v3/@claude-flow/cli/dist/src/memory/search-defaults.js +37 -0
  68. package/v3/@claude-flow/cli/dist/src/memory/sona-optimizer.d.ts +28 -0
  69. package/v3/@claude-flow/cli/dist/src/memory/sona-optimizer.js +59 -2
  70. package/v3/@claude-flow/cli/dist/src/mods/command-registry/schema.d.ts +24 -24
  71. package/v3/@claude-flow/cli/dist/src/parser.js +32 -22
  72. package/v3/@claude-flow/cli/dist/src/production/monitoring.d.ts +1 -0
  73. package/v3/@claude-flow/cli/dist/src/production/monitoring.js +19 -6
  74. package/v3/@claude-flow/cli/dist/src/production/rate-limiter.js +5 -4
  75. package/v3/@claude-flow/cli/dist/src/production/retry.js +3 -0
  76. package/v3/@claude-flow/cli/dist/src/ruvector/agent-wasm.d.ts +10 -0
  77. package/v3/@claude-flow/cli/dist/src/ruvector/agent-wasm.js +27 -1
  78. package/v3/@claude-flow/cli/dist/src/ruvector/diff-classifier.d.ts +2 -4
  79. package/v3/@claude-flow/cli/dist/src/ruvector/diff-classifier.js +202 -69
  80. package/v3/@claude-flow/cli/dist/src/ruvector/ruvllm-wasm.d.ts +4 -0
  81. package/v3/@claude-flow/cli/dist/src/ruvector/ruvllm-wasm.js +12 -1
  82. package/v3/@claude-flow/cli/dist/src/services/config-file-manager.js +3 -3
  83. package/v3/@claude-flow/cli/dist/src/services/policy-runtime.d.ts +1 -1
  84. package/v3/@claude-flow/cli/dist/src/services/policy-runtime.js +7 -1
  85. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.d.ts +11 -0
  86. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +37 -0
  87. package/v3/@claude-flow/cli/dist/src/transfer/store/discovery.js +16 -17
  88. package/v3/@claude-flow/cli/dist/src/transfer/store/types.d.ts +4 -0
  89. package/v3/@claude-flow/cli/package.json +7 -7
  90. package/v3/@claude-flow/shared/dist/resilience/rate-limiter.js +3 -3
  91. package/v3/@claude-flow/shared/dist/resilience/retry.js +3 -1
  92. package/v3/@claude-flow/shared/package.json +1 -1
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Default relevance floor for memory search (#4129).
3
+ *
4
+ * The bundled 384-dim embedding model scores a correct paraphrase match at
5
+ * roughly 0.25-0.5 and unrelated text at 0.0-0.15, so the old 0.3 floor sent
6
+ * back an empty list for natural-language queries whose right answer was
7
+ * clearly the best match (0.28 in the report). 0.2 sits between the two
8
+ * populations. An explicit `threshold` always wins, including 0.
9
+ *
10
+ * #2558 constraint stays: the fused recall score of a full-coverage exact
11
+ * keyword hit with semantic cosine <= 0 tops out at 0.40, so this must stay
12
+ * well below 0.4.
13
+ */
14
+ export const DEFAULT_SEARCH_THRESHOLD = 0.2;
15
+ /** How many below-threshold matches an empty default search reports as hints. */
16
+ export const NEAR_MISS_LIMIT = 3;
17
+ /**
18
+ * When a search that used the DEFAULT threshold returns nothing, say what was
19
+ * close instead of leaving the caller to assume the store has no answer
20
+ * (#4129). One extra, cheap query at threshold 0; never throws; absent when
21
+ * nothing at all matched.
22
+ */
23
+ export async function nearMisses(search, req) {
24
+ try {
25
+ const r = await search({ ...req, limit: NEAR_MISS_LIMIT, threshold: 0 });
26
+ if (!r?.success || !r.results?.length)
27
+ return undefined;
28
+ return {
29
+ nearMisses: r.results.map(e => ({ key: e.key, namespace: e.namespace, similarity: e.score })),
30
+ hint: `No result reached the default threshold (${DEFAULT_SEARCH_THRESHOLD}); the closest matches are in nearMisses. Pass threshold: 0 to include them in results.`,
31
+ };
32
+ }
33
+ catch {
34
+ return undefined;
35
+ }
36
+ }
37
+ //# sourceMappingURL=search-defaults.js.map
@@ -93,6 +93,8 @@ export interface SONAStats {
93
93
  agents: number;
94
94
  } | 'unavailable';
95
95
  }
96
+ /** The routing gate in getRoutingSuggestion: confidence x matchRatio must reach this for a learned pattern to be used. */
97
+ export declare const SONA_ROUTING_GATE = 0.6;
96
98
  /** Agent roles supported by the SONA routing optimizer. */
97
99
  export declare function isSupportedSonaAgent(agent: string): boolean;
98
100
  /**
@@ -101,6 +103,17 @@ export declare function isSupportedSonaAgent(agent: string): boolean;
101
103
  * Learns from past task outcomes to improve future routing decisions.
102
104
  * Integrates with Q-learning router for hybrid routing strategy.
103
105
  */
106
+ /**
107
+ * How close a pattern's confidence is to the routing gate. `successesNeeded`
108
+ * assumes a perfect keyword match (matchRatio 1) and the 0.1 success increment;
109
+ * a partial match needs more.
110
+ */
111
+ export declare function routingGateStatus(confidence: number): {
112
+ threshold: number;
113
+ confidence: number;
114
+ reached: boolean;
115
+ successesNeeded: number;
116
+ };
104
117
  export declare class SONAOptimizer {
105
118
  private patterns;
106
119
  private trajectoriesProcessed;
@@ -140,6 +153,15 @@ export declare class SONAOptimizer {
140
153
  patternKey: string;
141
154
  confidence: number;
142
155
  keywordsExtracted: string[];
156
+ /** True when this trajectory reinforced an existing near-identical pattern instead of creating a new one (#4134). */
157
+ reinforcedNeighbour?: boolean;
158
+ /** How far the pattern is from influencing routing, so a user can see learning has not kicked in yet (#4134). */
159
+ routingGate?: {
160
+ threshold: number;
161
+ confidence: number;
162
+ reached: boolean;
163
+ successesNeeded: number;
164
+ };
143
165
  };
144
166
  /**
145
167
  * Get routing suggestion based on learned patterns.
@@ -202,6 +224,12 @@ export declare class SONAOptimizer {
202
224
  * Create a unique pattern key from keywords and agent
203
225
  */
204
226
  private createPatternKey;
227
+ /**
228
+ * The key of the existing pattern for `agent` whose keyword set overlaps
229
+ * `keywords` the most, if that overlap (Jaccard) reaches NEIGHBOUR_MIN_JACCARD.
230
+ * Ties go to the more confident pattern. Linear in the pattern count (<= 1000).
231
+ */
232
+ private findNeighbourPattern;
205
233
  /**
206
234
  * Find the best matching pattern for given keywords
207
235
  */
@@ -26,6 +26,10 @@ const CONFIDENCE_INCREMENT = 0.1;
26
26
  const CONFIDENCE_DECREMENT = 0.15;
27
27
  const DECAY_RATE = 0.01; // Per day
28
28
  const MAX_PATTERNS = 1000;
29
+ /** #4134: a trajectory reinforces an existing pattern of the same agent whose keyword set overlaps at least this much (Jaccard). */
30
+ const NEIGHBOUR_MIN_JACCARD = 0.6;
31
+ /** The routing gate in getRoutingSuggestion: confidence x matchRatio must reach this for a learned pattern to be used. */
32
+ export const SONA_ROUTING_GATE = 0.6;
29
33
  // ============================================================================
30
34
  // Contrastive Trainer (lazy-loaded from @ruvector/ruvllm)
31
35
  // ============================================================================
@@ -119,6 +123,18 @@ const KEYWORD_CATEGORIES = {
119
123
  * Learns from past task outcomes to improve future routing decisions.
120
124
  * Integrates with Q-learning router for hybrid routing strategy.
121
125
  */
126
+ /**
127
+ * How close a pattern's confidence is to the routing gate. `successesNeeded`
128
+ * assumes a perfect keyword match (matchRatio 1) and the 0.1 success increment;
129
+ * a partial match needs more.
130
+ */
131
+ export function routingGateStatus(confidence) {
132
+ const reached = confidence >= SONA_ROUTING_GATE;
133
+ const successesNeeded = reached || confidence >= MAX_CONFIDENCE
134
+ ? 0
135
+ : Math.ceil(Math.log((1 - SONA_ROUTING_GATE) / (1 - confidence)) / Math.log(1 - CONFIDENCE_INCREMENT));
136
+ return { threshold: SONA_ROUTING_GATE, confidence, reached, successesNeeded };
137
+ }
122
138
  export class SONAOptimizer {
123
139
  patterns = new Map();
124
140
  trajectoriesProcessed = 0;
@@ -210,7 +226,18 @@ export class SONAOptimizer {
210
226
  };
211
227
  }
212
228
  // Create pattern key from sorted keywords
213
- const patternKey = this.createPatternKey(keywords, agent);
229
+ let patternKey = this.createPatternKey(keywords, agent);
230
+ // #4134: near-identical tasks ("Verify the X plugin ..." / "Verify the Y plugin ...")
231
+ // used to create a separate pattern each at confidence 0.5, so none ever reached
232
+ // the routing gate. Reinforce the closest existing pattern of the same agent instead.
233
+ let reinforcedNeighbour = false;
234
+ if (!this.patterns.has(patternKey)) {
235
+ const neighbour = this.findNeighbourPattern(keywords, agent);
236
+ if (neighbour) {
237
+ patternKey = neighbour;
238
+ reinforcedNeighbour = true;
239
+ }
240
+ }
214
241
  // Get or create pattern
215
242
  let pattern = this.patterns.get(patternKey);
216
243
  if (!pattern) {
@@ -264,6 +291,8 @@ export class SONAOptimizer {
264
291
  patternKey,
265
292
  confidence: pattern.confidence,
266
293
  keywordsExtracted: keywords,
294
+ reinforcedNeighbour,
295
+ routingGate: routingGateStatus(pattern.confidence),
267
296
  };
268
297
  }
269
298
  /**
@@ -305,7 +334,7 @@ export class SONAOptimizer {
305
334
  const keywords = this.extractKeywords(task);
306
335
  // Priority 2: Try SONA learned pattern matching
307
336
  const sonaResult = this.findBestPatternMatch(keywords);
308
- if (sonaResult && sonaResult.confidence >= 0.6) {
337
+ if (sonaResult && sonaResult.confidence >= SONA_ROUTING_GATE) {
309
338
  return {
310
339
  agent: sonaResult.agent,
311
340
  confidence: sonaResult.confidence,
@@ -532,6 +561,34 @@ export class SONAOptimizer {
532
561
  const sortedKeywords = [...keywords].sort();
533
562
  return `${agent}:${sortedKeywords.join('+')}`;
534
563
  }
564
+ /**
565
+ * The key of the existing pattern for `agent` whose keyword set overlaps
566
+ * `keywords` the most, if that overlap (Jaccard) reaches NEIGHBOUR_MIN_JACCARD.
567
+ * Ties go to the more confident pattern. Linear in the pattern count (<= 1000).
568
+ */
569
+ findNeighbourPattern(keywords, agent) {
570
+ const wanted = new Set(keywords);
571
+ let bestKey = null;
572
+ let bestJaccard = 0;
573
+ let bestConfidence = -1;
574
+ for (const [key, pattern] of this.patterns) {
575
+ if (pattern.agent !== agent)
576
+ continue;
577
+ let shared = 0;
578
+ for (const k of pattern.keywords)
579
+ if (wanted.has(k))
580
+ shared++;
581
+ const union = wanted.size + pattern.keywords.length - shared;
582
+ const jaccard = union === 0 ? 0 : shared / union;
583
+ if (jaccard >= NEIGHBOUR_MIN_JACCARD &&
584
+ (jaccard > bestJaccard || (jaccard === bestJaccard && pattern.confidence > bestConfidence))) {
585
+ bestKey = key;
586
+ bestJaccard = jaccard;
587
+ bestConfidence = pattern.confidence;
588
+ }
589
+ }
590
+ return bestKey;
591
+ }
535
592
  /**
536
593
  * Find the best matching pattern for given keywords
537
594
  */
@@ -63,19 +63,19 @@ export declare const commandDefinitionSchema: z.ZodEffects<z.ZodObject<{
63
63
  path: z.ZodString;
64
64
  }, "strict", z.ZodTypeAny, {
65
65
  path: string;
66
- commit: string;
67
66
  repository: string;
67
+ commit: string;
68
68
  }, {
69
69
  path: string;
70
- commit: string;
71
70
  repository: string;
71
+ commit: string;
72
72
  }>;
73
73
  }, "strict", z.ZodTypeAny, {
74
74
  id: string;
75
75
  source: {
76
76
  path: string;
77
- commit: string;
78
77
  repository: string;
78
+ commit: string;
79
79
  };
80
80
  schemaVersion: 1;
81
81
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -100,8 +100,8 @@ export declare const commandDefinitionSchema: z.ZodEffects<z.ZodObject<{
100
100
  id: string;
101
101
  source: {
102
102
  path: string;
103
- commit: string;
104
103
  repository: string;
104
+ commit: string;
105
105
  };
106
106
  schemaVersion: 1;
107
107
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -126,8 +126,8 @@ export declare const commandDefinitionSchema: z.ZodEffects<z.ZodObject<{
126
126
  id: string;
127
127
  source: {
128
128
  path: string;
129
- commit: string;
130
129
  repository: string;
130
+ commit: string;
131
131
  };
132
132
  schemaVersion: 1;
133
133
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -152,8 +152,8 @@ export declare const commandDefinitionSchema: z.ZodEffects<z.ZodObject<{
152
152
  id: string;
153
153
  source: {
154
154
  path: string;
155
- commit: string;
156
155
  repository: string;
156
+ commit: string;
157
157
  };
158
158
  schemaVersion: 1;
159
159
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -244,19 +244,19 @@ export declare const catalogEntrySchema: z.ZodObject<{
244
244
  path: z.ZodString;
245
245
  }, "strict", z.ZodTypeAny, {
246
246
  path: string;
247
- commit: string;
248
247
  repository: string;
248
+ commit: string;
249
249
  }, {
250
250
  path: string;
251
- commit: string;
252
251
  repository: string;
252
+ commit: string;
253
253
  }>;
254
254
  }, "strict", z.ZodTypeAny, {
255
255
  id: string;
256
256
  source: {
257
257
  path: string;
258
- commit: string;
259
258
  repository: string;
259
+ commit: string;
260
260
  };
261
261
  schemaVersion: 1;
262
262
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -281,8 +281,8 @@ export declare const catalogEntrySchema: z.ZodObject<{
281
281
  id: string;
282
282
  source: {
283
283
  path: string;
284
- commit: string;
285
284
  repository: string;
285
+ commit: string;
286
286
  };
287
287
  schemaVersion: 1;
288
288
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -307,8 +307,8 @@ export declare const catalogEntrySchema: z.ZodObject<{
307
307
  id: string;
308
308
  source: {
309
309
  path: string;
310
- commit: string;
311
310
  repository: string;
311
+ commit: string;
312
312
  };
313
313
  schemaVersion: 1;
314
314
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -333,8 +333,8 @@ export declare const catalogEntrySchema: z.ZodObject<{
333
333
  id: string;
334
334
  source: {
335
335
  path: string;
336
- commit: string;
337
336
  repository: string;
337
+ commit: string;
338
338
  };
339
339
  schemaVersion: 1;
340
340
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -404,8 +404,8 @@ export declare const catalogEntrySchema: z.ZodObject<{
404
404
  id: string;
405
405
  source: {
406
406
  path: string;
407
- commit: string;
408
407
  repository: string;
408
+ commit: string;
409
409
  };
410
410
  schemaVersion: 1;
411
411
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -445,8 +445,8 @@ export declare const catalogEntrySchema: z.ZodObject<{
445
445
  id: string;
446
446
  source: {
447
447
  path: string;
448
- commit: string;
449
448
  repository: string;
449
+ commit: string;
450
450
  };
451
451
  schemaVersion: 1;
452
452
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -510,19 +510,19 @@ export declare const commandCatalogSchema: z.ZodObject<{
510
510
  path: z.ZodString;
511
511
  }, "strict", z.ZodTypeAny, {
512
512
  path: string;
513
- commit: string;
514
513
  repository: string;
514
+ commit: string;
515
515
  }, {
516
516
  path: string;
517
- commit: string;
518
517
  repository: string;
518
+ commit: string;
519
519
  }>;
520
520
  }, "strict", z.ZodTypeAny, {
521
521
  id: string;
522
522
  source: {
523
523
  path: string;
524
- commit: string;
525
524
  repository: string;
525
+ commit: string;
526
526
  };
527
527
  schemaVersion: 1;
528
528
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -547,8 +547,8 @@ export declare const commandCatalogSchema: z.ZodObject<{
547
547
  id: string;
548
548
  source: {
549
549
  path: string;
550
- commit: string;
551
550
  repository: string;
551
+ commit: string;
552
552
  };
553
553
  schemaVersion: 1;
554
554
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -573,8 +573,8 @@ export declare const commandCatalogSchema: z.ZodObject<{
573
573
  id: string;
574
574
  source: {
575
575
  path: string;
576
- commit: string;
577
576
  repository: string;
577
+ commit: string;
578
578
  };
579
579
  schemaVersion: 1;
580
580
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -599,8 +599,8 @@ export declare const commandCatalogSchema: z.ZodObject<{
599
599
  id: string;
600
600
  source: {
601
601
  path: string;
602
- commit: string;
603
602
  repository: string;
603
+ commit: string;
604
604
  };
605
605
  schemaVersion: 1;
606
606
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -670,8 +670,8 @@ export declare const commandCatalogSchema: z.ZodObject<{
670
670
  id: string;
671
671
  source: {
672
672
  path: string;
673
- commit: string;
674
673
  repository: string;
674
+ commit: string;
675
675
  };
676
676
  schemaVersion: 1;
677
677
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -711,8 +711,8 @@ export declare const commandCatalogSchema: z.ZodObject<{
711
711
  id: string;
712
712
  source: {
713
713
  path: string;
714
- commit: string;
715
714
  repository: string;
715
+ commit: string;
716
716
  };
717
717
  schemaVersion: 1;
718
718
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -754,8 +754,8 @@ export declare const commandCatalogSchema: z.ZodObject<{
754
754
  id: string;
755
755
  source: {
756
756
  path: string;
757
- commit: string;
758
757
  repository: string;
758
+ commit: string;
759
759
  };
760
760
  schemaVersion: 1;
761
761
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -800,8 +800,8 @@ export declare const commandCatalogSchema: z.ZodObject<{
800
800
  id: string;
801
801
  source: {
802
802
  path: string;
803
- commit: string;
804
803
  repository: string;
804
+ commit: string;
805
805
  };
806
806
  schemaVersion: 1;
807
807
  kind: "help" | "query" | "prompt" | "workflow" | "mutation";
@@ -134,7 +134,6 @@ export class CommandParser {
134
134
  // command, because that's what caused `daemon start` to resolve as `start`
135
135
  // with `daemon` left as a positional.
136
136
  let resolvedCmd;
137
- let resolvedSub;
138
137
  const resolvedCommands = [];
139
138
  let sawFirstPositional = false;
140
139
  for (let index = 0; index < args.length; index++) {
@@ -173,16 +172,16 @@ export class CommandParser {
173
172
  const child = parent.subcommands.find(sc => sc.name === arg || sc.aliases?.includes(arg));
174
173
  if (child) {
175
174
  resolvedCommands.push(child);
176
- resolvedSub ??= child;
177
175
  continue;
178
176
  }
179
177
  }
180
178
  // A positional that is not the next subcommand ends the command path.
181
179
  break;
182
180
  }
183
- const { aliases, booleanFlags, stringFlags } = this.getFlagScope(resolvedCommands);
181
+ const { aliases, booleanFlags, stringFlags, arrayFlags } = this.getFlagScope(resolvedCommands);
184
182
  let i = 0;
185
183
  let parsingFlags = true;
184
+ let commandSlotAvailable = true;
186
185
  while (i < args.length) {
187
186
  const arg = args[i];
188
187
  // Check for end of flags marker
@@ -194,8 +193,17 @@ export class CommandParser {
194
193
  // Handle flags
195
194
  if (parsingFlags && arg.startsWith('-')) {
196
195
  const parseResult = this.parseFlag(args, i, aliases, booleanFlags, stringFlags);
196
+ // Array declarations retain text and accumulate repeated occurrences.
197
+ for (const key of arrayFlags) {
198
+ const value = parseResult.flags[key];
199
+ if (value !== undefined && value !== true && value !== false) {
200
+ const previous = result.flags[key];
201
+ parseResult.flags[key] = [...(Array.isArray(previous) ? previous : []), String(value)];
202
+ }
203
+ }
197
204
  // Apply to result flags
198
- Object.assign(result.flags, parseResult.flags);
205
+ const { _: ignoredPositionals, ...parsedFlags } = parseResult.flags;
206
+ Object.assign(result.flags, parsedFlags);
199
207
  i = parseResult.nextIndex;
200
208
  continue;
201
209
  }
@@ -203,7 +211,9 @@ export class CommandParser {
203
211
  // Fix for #1596: treat lazy command names as commands here too so that
204
212
  // downstream dispatch sees `commandPath = ['daemon', 'start']` instead of
205
213
  // `commandPath = ['start'], positional = ['daemon']`.
206
- if (result.command.length === 0 && this.isKnownCommandName(arg)) {
214
+ const isCommandSlot = commandSlotAvailable && parsingFlags;
215
+ commandSlotAvailable = false;
216
+ if (isCommandSlot && this.isKnownCommandName(arg)) {
207
217
  // This is a command
208
218
  result.command.push(arg);
209
219
  // Check for subcommand (level 1) — only possible for sync commands
@@ -248,7 +258,7 @@ export class CommandParser {
248
258
  // `default: true` silently dropped and reached the action handler as
249
259
  // `undefined`. That trapped `memory store --upsert` and every other
250
260
  // subcommand that leaned on a per-flag default.
251
- this.applyDefaults(result.flags, resolvedCmd, resolvedSub);
261
+ this.applyDefaults(result.flags, resolvedCommands);
252
262
  return result;
253
263
  }
254
264
  parseFlag(args, index, aliases, booleanFlags, stringFlags) {
@@ -472,11 +482,18 @@ export class CommandParser {
472
482
  aliases[option.short] = option.name;
473
483
  }
474
484
  }
485
+ const arrayFlags = new Set((this.options.arrayFlags ?? []).map(flag => this.normalizeKey(flag)));
486
+ for (const flag of arrayFlags)
487
+ stringFlags.add(flag);
475
488
  const layers = [this.globalOptions, ...commands.map(command => command.options ?? [])];
476
489
  for (const options of layers) {
477
490
  for (const option of options) {
478
491
  const key = this.normalizeKey(option.name);
479
- if (option.type === 'string')
492
+ if (option.type === 'array')
493
+ arrayFlags.add(key);
494
+ else if (option.type)
495
+ arrayFlags.delete(key);
496
+ if (option.type === 'string' || option.type === 'array')
480
497
  stringFlags.add(key);
481
498
  else if (option.type)
482
499
  stringFlags.delete(key);
@@ -488,7 +505,7 @@ export class CommandParser {
488
505
  }
489
506
  for (const flag of stringFlags)
490
507
  booleanFlags.delete(flag);
491
- return { aliases, booleanFlags, stringFlags };
508
+ return { aliases, booleanFlags, stringFlags, arrayFlags };
492
509
  }
493
510
  getBooleanFlags() {
494
511
  const flags = new Set();
@@ -526,23 +543,16 @@ export class CommandParser {
526
543
  }
527
544
  return flags;
528
545
  }
529
- applyDefaults(flags, command, subcommand) {
530
- // #2775: apply defaults from globals AND the resolved command/subcommand.
531
- // Subcommand > command > global (later writes lose to earlier — because
532
- // we only set when `undefined`, so the FIRST option definition that
533
- // supplies a default wins; walk narrow-to-broad so subcommand options
534
- // apply before broader ones do).
535
- const layers = [];
536
- if (subcommand?.options)
537
- layers.push(subcommand.options);
538
- if (command?.options)
539
- layers.push(command.options);
546
+ applyDefaults(flags, commands) {
547
+ // Walk every resolved command from narrow to broad so nested defaults
548
+ // have the same precedence as their declared flag types.
549
+ const layers = [...commands].reverse().map(command => command.options ?? []);
540
550
  layers.push(this.globalOptions);
541
551
  for (const layer of layers) {
542
552
  for (const opt of layer) {
543
553
  const key = this.normalizeKey(opt.name);
544
554
  if (flags[key] === undefined && opt.default !== undefined) {
545
- flags[key] = opt.default;
555
+ flags[key] = (Array.isArray(opt.default) ? [...opt.default] : opt.default);
546
556
  }
547
557
  }
548
558
  }
@@ -551,7 +561,7 @@ export class CommandParser {
551
561
  for (const [key, value] of Object.entries(this.options.defaults)) {
552
562
  const normalizedKey = this.normalizeKey(key);
553
563
  if (flags[normalizedKey] === undefined) {
554
- flags[normalizedKey] = value;
564
+ flags[normalizedKey] = (Array.isArray(value) ? [...value] : value);
555
565
  }
556
566
  }
557
567
  }
@@ -572,7 +582,7 @@ export class CommandParser {
572
582
  // followed by another flag) is parsed as boolean `true`. Commands then treat `true` as
573
583
  // the value: `hooks route --task` crashed with "task.trim is not a function" and
574
584
  // `memory retrieve --key` looked up the key "true". Report it instead.
575
- if ((opt.type === 'string' || opt.type === 'number') && flags[key] === true) {
585
+ if ((opt.type === 'string' || opt.type === 'number' || opt.type === 'array') && flags[key] === true) {
576
586
  errors.push(`Option --${opt.name} needs a value`);
577
587
  }
578
588
  // Check choices
@@ -71,6 +71,7 @@ export declare class MonitoringHooks {
71
71
  private healthStatus;
72
72
  private startTime;
73
73
  private activeRequests;
74
+ private requestGeneration;
74
75
  private requestCount;
75
76
  private errorCount;
76
77
  private healthChecks;
@@ -37,6 +37,7 @@ export class MonitoringHooks {
37
37
  healthStatus;
38
38
  startTime = Date.now();
39
39
  activeRequests = 0;
40
+ requestGeneration = 0;
40
41
  requestCount = 0;
41
42
  errorCount = 0;
42
43
  healthChecks = new Map();
@@ -101,9 +102,14 @@ export class MonitoringHooks {
101
102
  this.activeRequests++;
102
103
  this.requestCount++;
103
104
  const startTime = Date.now();
105
+ const generation = this.requestGeneration;
106
+ let completed = false;
104
107
  this.counter('request_started', 1, { requestId });
105
108
  // Return end function
106
109
  return () => {
110
+ if (completed || generation !== this.requestGeneration)
111
+ return;
112
+ completed = true;
107
113
  this.activeRequests--;
108
114
  const duration = Date.now() - startTime;
109
115
  this.responseTimes.push(duration);
@@ -156,7 +162,7 @@ export class MonitoringHooks {
156
162
  catch (error) {
157
163
  checks[name] = {
158
164
  status: 'unhealthy',
159
- message: error.message,
165
+ message: error instanceof Error ? error.message : String(error),
160
166
  lastCheck: Date.now(),
161
167
  responseTimeMs: Date.now() - startTime,
162
168
  };
@@ -168,13 +174,13 @@ export class MonitoringHooks {
168
174
  checks,
169
175
  timestamp: Date.now(),
170
176
  };
171
- return this.healthStatus;
177
+ return structuredClone(this.healthStatus);
172
178
  }
173
179
  /**
174
180
  * Get current health status
175
181
  */
176
182
  getHealthStatus() {
177
- return this.healthStatus;
183
+ return structuredClone(this.healthStatus);
178
184
  }
179
185
  // ============================================================================
180
186
  // Performance Metrics
@@ -217,7 +223,7 @@ export class MonitoringHooks {
217
223
  if (level) {
218
224
  filtered = filtered.filter(a => a.level === level);
219
225
  }
220
- return filtered;
226
+ return filtered.map(alert => ({ ...alert }));
221
227
  }
222
228
  /**
223
229
  * Acknowledge an alert
@@ -248,7 +254,7 @@ export class MonitoringHooks {
248
254
  if (since) {
249
255
  filtered = filtered.filter(m => m.timestamp >= since);
250
256
  }
251
- return filtered;
257
+ return filtered.map(metric => ({ ...metric, labels: { ...metric.labels } }));
252
258
  }
253
259
  /**
254
260
  * Get all metrics summary
@@ -278,6 +284,7 @@ export class MonitoringHooks {
278
284
  * Reset all metrics
279
285
  */
280
286
  reset() {
287
+ this.requestGeneration++;
281
288
  this.metrics = [];
282
289
  this.responseTimes = [];
283
290
  this.alerts = [];
@@ -308,7 +315,13 @@ export class MonitoringHooks {
308
315
  const recentAlert = this.alerts.find(a => a.metric === name &&
309
316
  !a.acknowledged &&
310
317
  Date.now() - a.timestamp < 60000);
311
- if (!recentAlert) {
318
+ if (recentAlert && level === 'critical' && recentAlert.level !== 'critical') {
319
+ Object.assign(recentAlert, {
320
+ level, value, threshold, timestamp: Date.now(),
321
+ message: `${name} exceeded ${level} threshold: ${value} >= ${threshold}`,
322
+ });
323
+ }
324
+ else if (!recentAlert) {
312
325
  this.alerts.push({
313
326
  id: `alert_${Date.now()}_${Math.random().toString(36).substring(7)}`,
314
327
  level,
@@ -90,6 +90,7 @@ export class RateLimiter {
90
90
  */
91
91
  getStatus(operation, userId) {
92
92
  const limits = this.getLimits(operation);
93
+ const capacity = Math.floor(limits.maxRequests * this.config.burstMultiplier);
93
94
  const bucketKey = userId && this.config.perUserLimits
94
95
  ? `${operation}:${userId}`
95
96
  : `global:${operation}`;
@@ -98,8 +99,8 @@ export class RateLimiter {
98
99
  if (!bucket) {
99
100
  return {
100
101
  current: 0,
101
- limit: limits.maxRequests,
102
- remaining: limits.maxRequests,
102
+ limit: capacity,
103
+ remaining: capacity,
103
104
  resetAt: now + limits.windowMs,
104
105
  };
105
106
  }
@@ -107,8 +108,8 @@ export class RateLimiter {
107
108
  const validRequests = bucket.requests.filter(t => t > now - limits.windowMs);
108
109
  return {
109
110
  current: validRequests.length,
110
- limit: limits.maxRequests,
111
- remaining: Math.max(0, limits.maxRequests - validRequests.length),
111
+ limit: capacity,
112
+ remaining: Math.max(0, capacity - validRequests.length),
112
113
  resetAt: validRequests.length > 0
113
114
  ? validRequests[0] + limits.windowMs
114
115
  : now + limits.windowMs,