@triflux/core 10.0.3 → 10.25.1

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 (143) hide show
  1. package/hooks/agent-route-guard.mjs +14 -10
  2. package/hooks/cross-review-tracker.mjs +21 -8
  3. package/hooks/error-context.mjs +47 -7
  4. package/hooks/hook-adaptive-collector.mjs +18 -16
  5. package/hooks/hook-manager.mjs +93 -32
  6. package/hooks/hook-orchestrator.mjs +279 -33
  7. package/hooks/hook-registry.json +104 -7
  8. package/hooks/hooks.json +49 -1
  9. package/hooks/keyword-rules.json +183 -23
  10. package/hooks/lib/resolve-root.mjs +1 -1
  11. package/hooks/mcp-config-watcher.mjs +6 -2
  12. package/hooks/permission-safe-allow.mjs +233 -0
  13. package/hooks/pipeline-stop.mjs +369 -22
  14. package/hooks/post-tool-tips.mjs +306 -0
  15. package/hooks/pre-compact-snapshot.mjs +181 -0
  16. package/hooks/safety-guard.mjs +308 -18
  17. package/hooks/session-end-cleanup.mjs +345 -0
  18. package/hooks/session-start-fast.mjs +214 -0
  19. package/hooks/subagent-tracker.mjs +267 -0
  20. package/hooks/subagent-verifier.mjs +9 -4
  21. package/hub/account-broker.mjs +1036 -66
  22. package/hub/adaptive-diagnostic.mjs +75 -48
  23. package/hub/adaptive-inject.mjs +96 -58
  24. package/hub/adaptive-memory.mjs +156 -42
  25. package/hub/adaptive.mjs +60 -31
  26. package/hub/assign-callbacks.mjs +163 -126
  27. package/hub/bridge.mjs +832 -291
  28. package/hub/cli-adapter-base.mjs +321 -97
  29. package/hub/codex-adapter.mjs +179 -93
  30. package/hub/codex-compat.mjs +4 -3
  31. package/hub/codex-preflight.mjs +113 -37
  32. package/hub/delegator/contracts.mjs +19 -23
  33. package/hub/delegator/index.mjs +14 -14
  34. package/hub/delegator/service.mjs +88 -64
  35. package/hub/delegator/tool-definitions.mjs +35 -35
  36. package/hub/dynamic-routing-engine.mjs +511 -0
  37. package/hub/fullcycle.mjs +34 -17
  38. package/hub/gemini-adapter.mjs +77 -95
  39. package/hub/hitl.mjs +202 -143
  40. package/hub/intent.mjs +171 -39
  41. package/hub/lib/bash-path.mjs +73 -0
  42. package/hub/lib/cache-guard.mjs +43 -17
  43. package/hub/lib/env-detect.mjs +145 -0
  44. package/hub/lib/hosts-compat.mjs +319 -0
  45. package/hub/lib/mcp-response-cache.mjs +239 -0
  46. package/hub/lib/memory-store.mjs +303 -107
  47. package/hub/lib/path-utils.mjs +165 -0
  48. package/hub/lib/phase-manager.mjs +493 -0
  49. package/hub/lib/process-utils.mjs +797 -104
  50. package/hub/lib/prompt-tmp.mjs +33 -0
  51. package/hub/lib/risk-tier.mjs +53 -0
  52. package/hub/lib/spawn-trace.mjs +561 -0
  53. package/hub/lib/ssh-command.mjs +44 -24
  54. package/hub/lib/ssh-retry.mjs +5 -1
  55. package/hub/lib/staged-file-detect.mjs +43 -0
  56. package/hub/lib/state-snapshot.mjs +293 -0
  57. package/hub/lib/tfx-route-args.mjs +309 -0
  58. package/hub/lib/timeline-adapter.mjs +190 -0
  59. package/hub/lib/trace-recorder.mjs +153 -0
  60. package/hub/lib/uuidv7.mjs +4 -3
  61. package/hub/memory-doctor.mjs +266 -106
  62. package/hub/middleware/quota-middleware.mjs +70 -0
  63. package/hub/middleware/request-logger.mjs +61 -34
  64. package/hub/paths.mjs +9 -9
  65. package/hub/pipeline/gates/confidence.mjs +34 -15
  66. package/hub/pipeline/gates/consensus.mjs +27 -15
  67. package/hub/pipeline/gates/index.mjs +7 -3
  68. package/hub/pipeline/gates/selfcheck.mjs +57 -19
  69. package/hub/pipeline/index.mjs +77 -42
  70. package/hub/pipeline/state.mjs +10 -10
  71. package/hub/pipeline/transitions.mjs +40 -23
  72. package/hub/platform.mjs +65 -48
  73. package/hub/quality/deslop.mjs +70 -49
  74. package/hub/reflexion.mjs +116 -58
  75. package/hub/research.mjs +34 -25
  76. package/hub/router.mjs +941 -791
  77. package/hub/routing/complexity.mjs +132 -29
  78. package/hub/routing/index.mjs +17 -12
  79. package/hub/routing/q-learning.mjs +76 -28
  80. package/hub/routing-snapshot.mjs +261 -0
  81. package/hub/session-fingerprint.mjs +129 -63
  82. package/hub/state.mjs +86 -44
  83. package/hub/team/retry-state-machine.mjs +267 -0
  84. package/hub/token-mode.mjs +76 -72
  85. package/hub/workers/worker-utils.mjs +22 -16
  86. package/hud/colors.mjs +27 -9
  87. package/hud/constants.mjs +184 -27
  88. package/hud/context-monitor.mjs +133 -44
  89. package/hud/hud-qos-status.mjs +153 -50
  90. package/hud/mission-board.mjs +56 -0
  91. package/hud/providers/claude.mjs +398 -114
  92. package/hud/providers/codex.mjs +96 -21
  93. package/hud/providers/gemini.mjs +184 -51
  94. package/hud/renderers.mjs +413 -123
  95. package/hud/terminal.mjs +101 -31
  96. package/hud/utils.mjs +81 -39
  97. package/mesh/index.mjs +69 -0
  98. package/mesh/mesh-budget.mjs +137 -0
  99. package/mesh/mesh-heartbeat.mjs +100 -0
  100. package/mesh/mesh-protocol.mjs +96 -0
  101. package/mesh/mesh-queue.mjs +163 -0
  102. package/mesh/mesh-registry.mjs +78 -0
  103. package/mesh/mesh-router.mjs +77 -0
  104. package/package.json +5 -1
  105. package/scripts/lib/agent-json.mjs +27 -0
  106. package/scripts/lib/async.mjs +174 -0
  107. package/scripts/lib/claudemd-scanner.mjs +6 -1
  108. package/scripts/lib/cli-agy.mjs +62 -0
  109. package/scripts/lib/cli-claude.mjs +78 -0
  110. package/scripts/lib/cli-codex.mjs +199 -0
  111. package/scripts/lib/cli-gemini.mjs +67 -0
  112. package/scripts/lib/codex-recovery.sh +50 -0
  113. package/scripts/lib/context.mjs +3 -3
  114. package/scripts/lib/cross-review-utils.mjs +13 -5
  115. package/scripts/lib/doctor-env-checks.mjs +121 -0
  116. package/scripts/lib/dynamic-route-cli.mjs +107 -0
  117. package/scripts/lib/env-probe.mjs +58 -30
  118. package/scripts/lib/env.mjs +14 -0
  119. package/scripts/lib/gemini-profiles.mjs +44 -10
  120. package/scripts/lib/handoff.mjs +33 -17
  121. package/scripts/lib/hook-utils.mjs +8 -6
  122. package/scripts/lib/hub.mjs +48 -0
  123. package/scripts/lib/keyword-rules.mjs +48 -19
  124. package/scripts/lib/logger.mjs +24 -24
  125. package/scripts/lib/mcp-filter.mjs +393 -249
  126. package/scripts/lib/mcp-gateway-health-check.mjs +159 -0
  127. package/scripts/lib/mcp-gateway-servers.mjs +70 -0
  128. package/scripts/lib/mcp-gateway-wrapper-check.mjs +35 -0
  129. package/scripts/lib/mcp-guard-engine.mjs +1282 -116
  130. package/scripts/lib/mcp-health.mjs +773 -0
  131. package/scripts/lib/mcp-manifest.mjs +24 -14
  132. package/scripts/lib/mcp-server-catalog.mjs +355 -118
  133. package/scripts/lib/pid.mjs +63 -0
  134. package/scripts/lib/process-utils.mjs +26 -0
  135. package/scripts/lib/psmux-info.mjs +72 -12
  136. package/scripts/lib/quota.mjs +47 -0
  137. package/scripts/lib/remote-spawn-transfer.mjs +44 -14
  138. package/scripts/lib/skill-state.mjs +220 -0
  139. package/scripts/lib/skill-template.mjs +36 -11
  140. package/scripts/lib/team.mjs +75 -0
  141. package/scripts/lib/timeout.mjs +66 -0
  142. package/scripts/lib/tmp.mjs +26 -0
  143. package/scripts/lib/toml.mjs +113 -0
@@ -1,6 +1,7 @@
1
1
  // hub/lib/memory-store.mjs — In-memory store (SQLite-free fallback for @triflux/core)
2
- import { uuidv7 } from './uuidv7.mjs';
3
- import { recalcConfidence } from '../reflexion.mjs';
2
+
3
+ import { recalcConfidence } from "../reflexion.mjs";
4
+ import { uuidv7 } from "./uuidv7.mjs";
4
5
 
5
6
  export function clone(value) {
6
7
  return value == null ? value : JSON.parse(JSON.stringify(value));
@@ -55,8 +56,8 @@ export function clampRetentionMs(value, fallback = 30 * 24 * 3600 * 1000) {
55
56
  }
56
57
 
57
58
  export function normalizeAdaptiveRuleIdentity(projectSlug, pattern) {
58
- const normalizedProject = String(projectSlug || '');
59
- const normalizedPattern = String(pattern || '');
59
+ const normalizedProject = String(projectSlug || "");
60
+ const normalizedPattern = String(pattern || "");
60
61
  if (!normalizedProject || !normalizedPattern) return null;
61
62
  return { project_slug: normalizedProject, pattern: normalizedPattern };
62
63
  }
@@ -68,17 +69,26 @@ export function buildAdaptiveRuleRow({
68
69
  hit_count = 1,
69
70
  last_seen_ms,
70
71
  created_ms,
72
+ error_message,
73
+ solution,
74
+ context,
71
75
  } = {}) {
72
76
  const identity = normalizeAdaptiveRuleIdentity(project_slug, pattern);
73
77
  if (!identity) return null;
74
78
  const createdAt = coerceTimestamp(created_ms);
75
- const lastSeenAt = Math.max(createdAt, coerceTimestamp(last_seen_ms, createdAt));
79
+ const lastSeenAt = Math.max(
80
+ createdAt,
81
+ coerceTimestamp(last_seen_ms, createdAt),
82
+ );
76
83
  return {
77
84
  ...identity,
78
85
  confidence: clampConfidence(confidence, 0.5),
79
86
  hit_count: clampHitCount(hit_count, 1),
80
87
  last_seen_ms: lastSeenAt,
81
88
  created_ms: createdAt,
89
+ error_message: typeof error_message === "string" ? error_message : null,
90
+ solution: typeof solution === "string" ? solution : null,
91
+ context: typeof context === "string" ? context : null,
82
92
  };
83
93
  }
84
94
 
@@ -94,7 +104,7 @@ function buildAssignCallbackEvent(row) {
94
104
  /**
95
105
  * 인메모리 방식의 데이터 저장소를 생성합니다.
96
106
  * SQLite를 사용할 수 없는 환경에서 폴백(fallback)으로 사용됩니다.
97
- *
107
+ *
98
108
  * @returns {object} 인메모리 스토어 객체
99
109
  */
100
110
  export function createMemoryStore() {
@@ -114,12 +124,16 @@ export function createMemoryStore() {
114
124
  function notifyAssignStatusListeners(row) {
115
125
  const event = buildAssignCallbackEvent(row);
116
126
  for (const listener of Array.from(assignStatusListeners)) {
117
- try { listener(event, clone(row)); } catch {}
127
+ try {
128
+ listener(event, clone(row));
129
+ } catch {}
118
130
  }
119
131
  }
120
132
 
121
133
  function getRecentMessages() {
122
- return Array.from(messages.values()).sort((left, right) => right.created_at_ms - left.created_at_ms);
134
+ return Array.from(messages.values()).sort(
135
+ (left, right) => right.created_at_ms - left.created_at_ms,
136
+ );
123
137
  }
124
138
 
125
139
  function upsertMessage(message) {
@@ -128,13 +142,21 @@ export function createMemoryStore() {
128
142
  }
129
143
 
130
144
  const store = {
131
- type: 'memory',
145
+ type: "memory",
132
146
  db: null,
133
147
  uuidv7,
134
148
 
135
149
  close() {},
136
150
 
137
- registerAgent({ agent_id, cli, pid, capabilities = [], topics = [], heartbeat_ttl_ms = 30000, metadata = {} }) {
151
+ registerAgent({
152
+ agent_id,
153
+ cli,
154
+ pid,
155
+ capabilities = [],
156
+ topics = [],
157
+ heartbeat_ttl_ms = 30000,
158
+ metadata = {},
159
+ }) {
138
160
  const now = Date.now();
139
161
  const current = agents.get(agent_id) || {};
140
162
  const next = {
@@ -146,11 +168,16 @@ export function createMemoryStore() {
146
168
  topics: clone(topics),
147
169
  last_seen_ms: now,
148
170
  lease_expires_ms: now + heartbeat_ttl_ms,
149
- status: 'online',
171
+ status: "online",
150
172
  metadata: clone(metadata),
151
173
  };
152
174
  agents.set(agent_id, next);
153
- return { agent_id, lease_id: uuidv7(), lease_expires_ms: next.lease_expires_ms, server_time_ms: now };
175
+ return {
176
+ agent_id,
177
+ lease_id: uuidv7(),
178
+ lease_expires_ms: next.lease_expires_ms,
179
+ server_time_ms: now,
180
+ };
154
181
  },
155
182
 
156
183
  getAgent(id) {
@@ -159,12 +186,21 @@ export function createMemoryStore() {
159
186
 
160
187
  refreshLease(agentId, ttlMs = 30000) {
161
188
  const current = agents.get(agentId);
162
- if (!current) return { agent_id: agentId, lease_expires_ms: Date.now() + ttlMs, server_time_ms: Date.now() };
189
+ if (!current)
190
+ return {
191
+ agent_id: agentId,
192
+ lease_expires_ms: Date.now() + ttlMs,
193
+ server_time_ms: Date.now(),
194
+ };
163
195
  const now = Date.now();
164
196
  current.last_seen_ms = now;
165
197
  current.lease_expires_ms = now + ttlMs;
166
- current.status = 'online';
167
- return { agent_id: agentId, lease_expires_ms: current.lease_expires_ms, server_time_ms: now };
198
+ current.status = "online";
199
+ return {
200
+ agent_id: agentId,
201
+ lease_expires_ms: current.lease_expires_ms,
202
+ server_time_ms: now,
203
+ };
168
204
  },
169
205
 
170
206
  updateAgentTopics(agentId, topics = []) {
@@ -177,7 +213,7 @@ export function createMemoryStore() {
177
213
 
178
214
  listOnlineAgents() {
179
215
  return Array.from(agents.values())
180
- .filter((agent) => agent.status !== 'offline')
216
+ .filter((agent) => agent.status !== "offline")
181
217
  .map((agent) => clone(agent));
182
218
  },
183
219
 
@@ -187,7 +223,12 @@ export function createMemoryStore() {
187
223
 
188
224
  getAgentsByTopic(topic) {
189
225
  return Array.from(agents.values())
190
- .filter((agent) => agent.status !== 'offline' && Array.isArray(agent.topics) && agent.topics.includes(topic))
226
+ .filter(
227
+ (agent) =>
228
+ agent.status !== "offline" &&
229
+ Array.isArray(agent.topics) &&
230
+ agent.topics.includes(topic),
231
+ )
191
232
  .map((agent) => clone(agent));
192
233
  },
193
234
 
@@ -196,11 +237,14 @@ export function createMemoryStore() {
196
237
  let stale = 0;
197
238
  let offline = 0;
198
239
  for (const agent of agents.values()) {
199
- if (agent.status === 'online' && agent.lease_expires_ms < now) {
200
- agent.status = 'stale';
240
+ if (agent.status === "online" && agent.lease_expires_ms < now) {
241
+ agent.status = "stale";
201
242
  stale += 1;
202
- } else if (agent.status === 'stale' && agent.lease_expires_ms < now - 300000) {
203
- agent.status = 'offline';
243
+ } else if (
244
+ agent.status === "stale" &&
245
+ agent.lease_expires_ms < now - 300000
246
+ ) {
247
+ agent.status = "offline";
204
248
  offline += 1;
205
249
  }
206
250
  }
@@ -214,7 +258,18 @@ export function createMemoryStore() {
214
258
  return true;
215
259
  },
216
260
 
217
- auditLog({ type, from, to, topic, priority = 5, ttl_ms = 300000, payload = {}, trace_id, correlation_id, status = 'queued' }) {
261
+ auditLog({
262
+ type,
263
+ from,
264
+ to,
265
+ topic,
266
+ priority = 5,
267
+ ttl_ms = 300000,
268
+ payload = {},
269
+ trace_id,
270
+ correlation_id,
271
+ status = "queued",
272
+ }) {
218
273
  const now = Date.now();
219
274
  const row = {
220
275
  id: uuidv7(),
@@ -243,7 +298,12 @@ export function createMemoryStore() {
243
298
  },
244
299
 
245
300
  getResponseByCorrelation(cid) {
246
- return getRecentMessages().find((message) => message.correlation_id === cid && message.type === 'response') || null;
301
+ return (
302
+ getRecentMessages().find(
303
+ (message) =>
304
+ message.correlation_id === cid && message.type === "response",
305
+ ) || null
306
+ );
247
307
  },
248
308
 
249
309
  getMessagesByTrace(tid) {
@@ -260,17 +320,23 @@ export function createMemoryStore() {
260
320
  return true;
261
321
  },
262
322
 
263
- getAuditMessagesForAgent(agentId, { max_messages = 20, include_topics = null } = {}) {
323
+ getAuditMessagesForAgent(
324
+ agentId,
325
+ { max_messages = 20, include_topics = null } = {},
326
+ ) {
264
327
  const limit = clampMaxMessages(max_messages);
265
- const topics = Array.isArray(include_topics) && include_topics.length
266
- ? include_topics
267
- : (agents.get(agentId)?.topics || []);
328
+ const topics =
329
+ Array.isArray(include_topics) && include_topics.length
330
+ ? include_topics
331
+ : agents.get(agentId)?.topics || [];
268
332
  const topicSet = new Set(topics);
269
333
  return getRecentMessages()
270
- .filter((message) => (
271
- message.to_agent === agentId
272
- || (String(message.to_agent || '').startsWith('topic:') && topicSet.has(message.topic))
273
- ))
334
+ .filter(
335
+ (message) =>
336
+ message.to_agent === agentId ||
337
+ (String(message.to_agent || "").startsWith("topic:") &&
338
+ topicSet.has(message.topic)),
339
+ )
274
340
  .slice(0, limit)
275
341
  .map((message) => clone(message));
276
342
  },
@@ -285,14 +351,26 @@ export function createMemoryStore() {
285
351
  },
286
352
 
287
353
  pollForAgent(agentId, { max_messages = 20, include_topics = null } = {}) {
288
- return store.getAuditMessagesForAgent(agentId, { max_messages, include_topics });
354
+ return store.getAuditMessagesForAgent(agentId, {
355
+ max_messages,
356
+ include_topics,
357
+ });
289
358
  },
290
359
 
291
360
  ackMessages() {
292
361
  return 0;
293
362
  },
294
363
 
295
- insertHumanRequest({ requester_agent, kind, prompt, requested_schema = {}, deadline_ms, default_action, correlation_id, trace_id }) {
364
+ insertHumanRequest({
365
+ requester_agent,
366
+ kind,
367
+ prompt,
368
+ requested_schema = {},
369
+ deadline_ms,
370
+ default_action,
371
+ correlation_id,
372
+ trace_id,
373
+ }) {
296
374
  const requestId = uuidv7();
297
375
  const now = Date.now();
298
376
  const row = {
@@ -301,7 +379,7 @@ export function createMemoryStore() {
301
379
  kind,
302
380
  prompt,
303
381
  schema: clone(requested_schema),
304
- state: 'pending',
382
+ state: "pending",
305
383
  deadline_ms: now + deadline_ms,
306
384
  default_action,
307
385
  correlation_id: correlation_id || uuidv7(),
@@ -309,7 +387,11 @@ export function createMemoryStore() {
309
387
  response: null,
310
388
  };
311
389
  humanRequests.set(requestId, row);
312
- return { request_id: requestId, state: 'pending', deadline_ms: row.deadline_ms };
390
+ return {
391
+ request_id: requestId,
392
+ state: "pending",
393
+ deadline_ms: row.deadline_ms,
394
+ };
313
395
  },
314
396
 
315
397
  getHumanRequest(id) {
@@ -326,7 +408,7 @@ export function createMemoryStore() {
326
408
 
327
409
  getPendingHumanRequests() {
328
410
  return Array.from(humanRequests.values())
329
- .filter((request) => request.state === 'pending')
411
+ .filter((request) => request.state === "pending")
330
412
  .map((request) => clone(request));
331
413
  },
332
414
 
@@ -334,8 +416,8 @@ export function createMemoryStore() {
334
416
  let changed = 0;
335
417
  const now = Date.now();
336
418
  for (const request of humanRequests.values()) {
337
- if (request.state === 'pending' && request.deadline_ms < now) {
338
- request.state = 'timed_out';
419
+ if (request.state === "pending" && request.deadline_ms < now) {
420
+ request.state = "timed_out";
339
421
  changed += 1;
340
422
  }
341
423
  }
@@ -344,7 +426,7 @@ export function createMemoryStore() {
344
426
 
345
427
  moveToDeadLetter(messageId, reason, lastError = null) {
346
428
  const current = messages.get(messageId);
347
- if (current) current.status = 'dead_letter';
429
+ if (current) current.status = "dead_letter";
348
430
  deadLetters.set(messageId, {
349
431
  message_id: messageId,
350
432
  reason,
@@ -365,10 +447,10 @@ export function createMemoryStore() {
365
447
  job_id,
366
448
  supervisor_agent,
367
449
  worker_agent,
368
- topic = 'assign.job',
369
- task = '',
450
+ topic = "assign.job",
451
+ task = "",
370
452
  payload = {},
371
- status = 'queued',
453
+ status = "queued",
372
454
  attempt = 1,
373
455
  retry_count = 0,
374
456
  max_retries = 0,
@@ -388,8 +470,8 @@ export function createMemoryStore() {
388
470
  job_id: job_id || uuidv7(),
389
471
  supervisor_agent,
390
472
  worker_agent,
391
- topic: String(topic || 'assign.job'),
392
- task: String(task || ''),
473
+ topic: String(topic || "assign.job"),
474
+ task: String(task || ""),
393
475
  payload: clone(payload || {}),
394
476
  status,
395
477
  attempt: Math.max(1, Number(attempt) || 1),
@@ -408,8 +490,10 @@ export function createMemoryStore() {
408
490
  error: error == null ? null : clone(error),
409
491
  created_at_ms: now,
410
492
  updated_at_ms: now,
411
- started_at_ms: status === 'running' ? now : null,
412
- completed_at_ms: ['succeeded', 'failed', 'timed_out'].includes(status) ? now : null,
493
+ started_at_ms: status === "running" ? now : null,
494
+ completed_at_ms: ["succeeded", "failed", "timed_out"].includes(status)
495
+ ? now
496
+ : null,
413
497
  last_retry_at_ms: retry_count > 0 ? now : null,
414
498
  };
415
499
  assignJobs.set(row.job_id, row);
@@ -428,8 +512,13 @@ export function createMemoryStore() {
428
512
  const snapshot = clone(current);
429
513
  const now = Date.now();
430
514
  const nextStatus = status || current.status;
431
- const isTerminal = ['succeeded', 'failed', 'timed_out'].includes(nextStatus);
432
- const nextTimeout = clampDuration(patch.timeout_ms ?? current.timeout_ms, current.timeout_ms);
515
+ const isTerminal = ["succeeded", "failed", "timed_out"].includes(
516
+ nextStatus,
517
+ );
518
+ const nextTimeout = clampDuration(
519
+ patch.timeout_ms ?? current.timeout_ms,
520
+ current.timeout_ms,
521
+ );
433
522
  const nextRow = {
434
523
  ...current,
435
524
  supervisor_agent: patch.supervisor_agent ?? current.supervisor_agent,
@@ -438,39 +527,65 @@ export function createMemoryStore() {
438
527
  task: patch.task ?? current.task,
439
528
  payload: clone(patch.payload ?? current.payload ?? {}),
440
529
  status: nextStatus,
441
- attempt: Math.max(1, Number(patch.attempt ?? current.attempt) || current.attempt || 1),
442
- retry_count: Math.max(0, Number(patch.retry_count ?? current.retry_count) || 0),
443
- max_retries: Math.max(0, Number(patch.max_retries ?? current.max_retries) || 0),
444
- priority: clampPriority(patch.priority ?? current.priority, current.priority || 5),
445
- ttl_ms: clampDuration(patch.ttl_ms ?? current.ttl_ms, current.ttl_ms || nextTimeout),
530
+ attempt: Math.max(
531
+ 1,
532
+ Number(patch.attempt ?? current.attempt) || current.attempt || 1,
533
+ ),
534
+ retry_count: Math.max(
535
+ 0,
536
+ Number(patch.retry_count ?? current.retry_count) || 0,
537
+ ),
538
+ max_retries: Math.max(
539
+ 0,
540
+ Number(patch.max_retries ?? current.max_retries) || 0,
541
+ ),
542
+ priority: clampPriority(
543
+ patch.priority ?? current.priority,
544
+ current.priority || 5,
545
+ ),
546
+ ttl_ms: clampDuration(
547
+ patch.ttl_ms ?? current.ttl_ms,
548
+ current.ttl_ms || nextTimeout,
549
+ ),
446
550
  timeout_ms: nextTimeout,
447
551
  deadline_ms: (() => {
448
- if (Object.hasOwn(patch, 'deadline_ms')) {
449
- return patch.deadline_ms == null ? null : Math.trunc(Number(patch.deadline_ms));
552
+ if (Object.hasOwn(patch, "deadline_ms")) {
553
+ return patch.deadline_ms == null
554
+ ? null
555
+ : Math.trunc(Number(patch.deadline_ms));
450
556
  }
451
557
  if (isTerminal) return null;
452
- if (nextStatus === 'running' && !current.deadline_ms) return now + nextTimeout;
558
+ if (nextStatus === "running" && !current.deadline_ms)
559
+ return now + nextTimeout;
453
560
  return current.deadline_ms;
454
561
  })(),
455
562
  trace_id: patch.trace_id ?? current.trace_id,
456
563
  correlation_id: patch.correlation_id ?? current.correlation_id,
457
- last_message_id: Object.hasOwn(patch, 'last_message_id')
564
+ last_message_id: Object.hasOwn(patch, "last_message_id")
458
565
  ? patch.last_message_id
459
566
  : current.last_message_id,
460
- result: Object.hasOwn(patch, 'result')
461
- ? (patch.result == null ? null : clone(patch.result))
567
+ result: Object.hasOwn(patch, "result")
568
+ ? patch.result == null
569
+ ? null
570
+ : clone(patch.result)
462
571
  : current.result,
463
- error: Object.hasOwn(patch, 'error')
464
- ? (patch.error == null ? null : clone(patch.error))
572
+ error: Object.hasOwn(patch, "error")
573
+ ? patch.error == null
574
+ ? null
575
+ : clone(patch.error)
465
576
  : current.error,
466
577
  updated_at_ms: now,
467
- started_at_ms: Object.hasOwn(patch, 'started_at_ms')
578
+ started_at_ms: Object.hasOwn(patch, "started_at_ms")
468
579
  ? patch.started_at_ms
469
- : (nextStatus === 'running' ? (current.started_at_ms || now) : current.started_at_ms),
470
- completed_at_ms: Object.hasOwn(patch, 'completed_at_ms')
580
+ : nextStatus === "running"
581
+ ? current.started_at_ms || now
582
+ : current.started_at_ms,
583
+ completed_at_ms: Object.hasOwn(patch, "completed_at_ms")
471
584
  ? patch.completed_at_ms
472
- : (isTerminal ? (current.completed_at_ms || now) : current.completed_at_ms),
473
- last_retry_at_ms: Object.hasOwn(patch, 'last_retry_at_ms')
585
+ : isTerminal
586
+ ? current.completed_at_ms || now
587
+ : current.completed_at_ms,
588
+ last_retry_at_ms: Object.hasOwn(patch, "last_retry_at_ms")
474
589
  ? patch.last_retry_at_ms
475
590
  : current.last_retry_at_ms,
476
591
  };
@@ -491,17 +606,29 @@ export function createMemoryStore() {
491
606
  active_before_ms,
492
607
  limit = 50,
493
608
  } = {}) {
494
- const statusList = Array.isArray(statuses) && statuses.length
495
- ? statuses
496
- : (status ? [status] : []);
609
+ const statusList =
610
+ Array.isArray(statuses) && statuses.length
611
+ ? statuses
612
+ : status
613
+ ? [status]
614
+ : [];
497
615
  return Array.from(assignJobs.values())
498
- .filter((job) => !supervisor_agent || job.supervisor_agent === supervisor_agent)
616
+ .filter(
617
+ (job) =>
618
+ !supervisor_agent || job.supervisor_agent === supervisor_agent,
619
+ )
499
620
  .filter((job) => !worker_agent || job.worker_agent === worker_agent)
500
621
  .filter((job) => !trace_id || job.trace_id === trace_id)
501
- .filter((job) => !correlation_id || job.correlation_id === correlation_id)
622
+ .filter(
623
+ (job) => !correlation_id || job.correlation_id === correlation_id,
624
+ )
502
625
  .filter((job) => !statusList.length || statusList.includes(job.status))
503
- .filter((job) => !Number.isFinite(Number(active_before_ms))
504
- || (job.deadline_ms != null && job.deadline_ms <= Number(active_before_ms)))
626
+ .filter(
627
+ (job) =>
628
+ !Number.isFinite(Number(active_before_ms)) ||
629
+ (job.deadline_ms != null &&
630
+ job.deadline_ms <= Number(active_before_ms)),
631
+ )
505
632
  .sort((left, right) => right.updated_at_ms - left.updated_at_ms)
506
633
  .slice(0, clampMaxMessages(limit, 50))
507
634
  .map((job) => clone(job));
@@ -510,10 +637,19 @@ export function createMemoryStore() {
510
637
  retryAssign(jobId, patch = {}) {
511
638
  const current = assignJobs.get(jobId);
512
639
  if (!current) return null;
513
- const nextRetryCount = Math.max(0, Number(patch.retry_count ?? current.retry_count + 1) || 0);
514
- const nextAttempt = Math.max(current.attempt + 1, Number(patch.attempt ?? current.attempt + 1) || 1);
515
- const nextTimeout = clampDuration(patch.timeout_ms ?? current.timeout_ms, current.timeout_ms);
516
- return store.updateAssignStatus(jobId, 'queued', {
640
+ const nextRetryCount = Math.max(
641
+ 0,
642
+ Number(patch.retry_count ?? current.retry_count + 1) || 0,
643
+ );
644
+ const nextAttempt = Math.max(
645
+ current.attempt + 1,
646
+ Number(patch.attempt ?? current.attempt + 1) || 1,
647
+ );
648
+ const nextTimeout = clampDuration(
649
+ patch.timeout_ms ?? current.timeout_ms,
650
+ current.timeout_ms,
651
+ );
652
+ return store.updateAssignStatus(jobId, "queued", {
517
653
  retry_count: nextRetryCount,
518
654
  attempt: nextAttempt,
519
655
  timeout_ms: nextTimeout,
@@ -523,7 +659,7 @@ export function createMemoryStore() {
523
659
  started_at_ms: null,
524
660
  last_retry_at_ms: Date.now(),
525
661
  result: patch.result ?? null,
526
- error: Object.hasOwn(patch, 'error') ? patch.error : current.error,
662
+ error: Object.hasOwn(patch, "error") ? patch.error : current.error,
527
663
  last_message_id: null,
528
664
  });
529
665
  },
@@ -532,11 +668,11 @@ export function createMemoryStore() {
532
668
  const now = Date.now();
533
669
  let expiredMessages = 0;
534
670
  for (const message of messages.values()) {
535
- if (message.status === 'queued' && message.expires_at_ms < now) {
536
- message.status = 'dead_letter';
671
+ if (message.status === "queued" && message.expires_at_ms < now) {
672
+ message.status = "dead_letter";
537
673
  deadLetters.set(message.id, {
538
674
  message_id: message.id,
539
- reason: 'ttl_expired',
675
+ reason: "ttl_expired",
540
676
  failed_at_ms: now,
541
677
  last_error: null,
542
678
  });
@@ -544,14 +680,18 @@ export function createMemoryStore() {
544
680
  }
545
681
  }
546
682
  const humanRequestsExpired = store.expireHumanRequests();
547
- return { messages: expiredMessages, human_requests: humanRequestsExpired };
683
+ return {
684
+ messages: expiredMessages,
685
+ human_requests: humanRequestsExpired,
686
+ };
548
687
  },
549
688
 
550
689
  getQueueDepths() {
551
690
  const depths = { urgent: 0, normal: 0, dlq: deadLetters.size };
552
691
  const now = Date.now();
553
692
  for (const message of messages.values()) {
554
- if (message.status !== 'queued' || message.expires_at_ms < now) continue;
693
+ if (message.status !== "queued" || message.expires_at_ms < now)
694
+ continue;
555
695
  if (message.priority >= 7) depths.urgent += 1;
556
696
  else depths.normal += 1;
557
697
  }
@@ -559,7 +699,7 @@ export function createMemoryStore() {
559
699
  },
560
700
 
561
701
  onAssignStatusChange(listener) {
562
- if (typeof listener !== 'function') {
702
+ if (typeof listener !== "function") {
563
703
  return () => {};
564
704
  }
565
705
  assignStatusListeners.add(listener);
@@ -568,36 +708,46 @@ export function createMemoryStore() {
568
708
 
569
709
  getDeliveryStats() {
570
710
  const cutoff = Date.now() - 300000;
571
- const totalDeliveries = Array.from(messages.values())
572
- .filter((message) => message.status === 'acked' && message.created_at_ms > cutoff)
573
- .length;
711
+ const totalDeliveries = Array.from(messages.values()).filter(
712
+ (message) =>
713
+ message.status === "acked" && message.created_at_ms > cutoff,
714
+ ).length;
574
715
  return { total_deliveries: totalDeliveries, avg_delivery_ms: 0 };
575
716
  },
576
717
 
577
718
  getHubStats() {
578
719
  return {
579
- online_agents: Array.from(agents.values()).filter((agent) => agent.status === 'online').length,
720
+ online_agents: Array.from(agents.values()).filter(
721
+ (agent) => agent.status === "online",
722
+ ).length,
580
723
  total_messages: messages.size,
581
- active_assign_jobs: Array.from(assignJobs.values()).filter((job) => ['queued', 'running'].includes(job.status)).length,
724
+ active_assign_jobs: Array.from(assignJobs.values()).filter((job) =>
725
+ ["queued", "running"].includes(job.status),
726
+ ).length,
582
727
  ...store.getQueueDepths(),
583
728
  };
584
729
  },
585
730
 
586
731
  getAuditStats() {
587
- const countByStatus = (targetStatus) => Array.from(assignJobs.values()).filter((job) => job.status === targetStatus).length;
732
+ const countByStatus = (targetStatus) =>
733
+ Array.from(assignJobs.values()).filter(
734
+ (job) => job.status === targetStatus,
735
+ ).length;
588
736
  return {
589
- online_agents: Array.from(agents.values()).filter((agent) => agent.status === 'online').length,
737
+ online_agents: Array.from(agents.values()).filter(
738
+ (agent) => agent.status === "online",
739
+ ).length,
590
740
  total_messages: messages.size,
591
741
  dlq: deadLetters.size,
592
- assign_queued: countByStatus('queued'),
593
- assign_running: countByStatus('running'),
594
- assign_failed: countByStatus('failed'),
595
- assign_timed_out: countByStatus('timed_out'),
742
+ assign_queued: countByStatus("queued"),
743
+ assign_running: countByStatus("running"),
744
+ assign_failed: countByStatus("failed"),
745
+ assign_timed_out: countByStatus("timed_out"),
596
746
  };
597
747
  },
598
748
 
599
749
  addReflexion({
600
- type = 'reflexion',
750
+ type = "reflexion",
601
751
  error_pattern,
602
752
  error_message,
603
753
  context = {},
@@ -638,8 +788,17 @@ export function createMemoryStore() {
638
788
 
639
789
  findReflexion(errorPattern, context = {}) {
640
790
  const entries = Array.from(reflexionEntries.values())
641
- .filter((entry) => entry.error_pattern === errorPattern || entry.error_pattern.includes(errorPattern) || errorPattern.includes(entry.error_pattern))
642
- .filter((entry) => Object.entries(context).every(([key, value]) => value == null || entry.context?.[key] === value))
791
+ .filter(
792
+ (entry) =>
793
+ entry.error_pattern === errorPattern ||
794
+ entry.error_pattern.includes(errorPattern) ||
795
+ errorPattern.includes(entry.error_pattern),
796
+ )
797
+ .filter((entry) =>
798
+ Object.entries(context).every(
799
+ ([key, value]) => value == null || entry.context?.[key] === value,
800
+ ),
801
+ )
643
802
  .sort((left, right) => right.confidence - left.confidence);
644
803
  return entries.map((entry) => clone(entry));
645
804
  },
@@ -657,11 +816,18 @@ export function createMemoryStore() {
657
816
  },
658
817
 
659
818
  listReflexion(filters = {}) {
660
- const { type, minConfidence = Number.NEGATIVE_INFINITY, projectSlug } = filters;
819
+ const {
820
+ type,
821
+ minConfidence = Number.NEGATIVE_INFINITY,
822
+ projectSlug,
823
+ } = filters;
661
824
  return Array.from(reflexionEntries.values())
662
825
  .filter((entry) => !type || entry.type === type)
663
826
  .filter((entry) => entry.confidence >= minConfidence)
664
- .filter((entry) => !projectSlug || entry.adaptive_state?.project_slug === projectSlug)
827
+ .filter(
828
+ (entry) =>
829
+ !projectSlug || entry.adaptive_state?.project_slug === projectSlug,
830
+ )
665
831
  .sort((left, right) => right.confidence - left.confidence)
666
832
  .map((entry) => clone(entry));
667
833
  },
@@ -673,7 +839,9 @@ export function createMemoryStore() {
673
839
  ...current,
674
840
  ...clone(patch),
675
841
  context: patch.context ? clone(patch.context) : current.context,
676
- adaptive_state: patch.adaptive_state ? clone(patch.adaptive_state) : current.adaptive_state,
842
+ adaptive_state: patch.adaptive_state
843
+ ? clone(patch.adaptive_state)
844
+ : current.adaptive_state,
677
845
  updated_at_ms: patch.updated_at_ms ?? Date.now(),
678
846
  };
679
847
  reflexionEntries.set(id, next);
@@ -703,7 +871,9 @@ export function createMemoryStore() {
703
871
  const current = adaptiveRules.get(key);
704
872
  adaptiveRules.set(key, {
705
873
  ...next,
706
- created_ms: current ? Math.min(current.created_ms, next.created_ms) : next.created_ms,
874
+ created_ms: current
875
+ ? Math.min(current.created_ms, next.created_ms)
876
+ : next.created_ms,
707
877
  });
708
878
  return clone(adaptiveRules.get(key));
709
879
  },
@@ -711,7 +881,11 @@ export function createMemoryStore() {
711
881
  findAdaptiveRule(projectSlug, pattern) {
712
882
  const identity = normalizeAdaptiveRuleIdentity(projectSlug, pattern);
713
883
  if (!identity) return null;
714
- return clone(adaptiveRules.get(getAdaptiveRuleKey(identity.project_slug, identity.pattern)) || null);
884
+ return clone(
885
+ adaptiveRules.get(
886
+ getAdaptiveRuleKey(identity.project_slug, identity.pattern),
887
+ ) || null,
888
+ );
715
889
  },
716
890
 
717
891
  updateRuleConfidence(projectSlug, pattern, confidence, options = {}) {
@@ -720,18 +894,23 @@ export function createMemoryStore() {
720
894
  const next = {
721
895
  ...current,
722
896
  confidence: clampConfidence(confidence, current.confidence),
723
- hit_count: current.hit_count + clampHitIncrement(options.hit_count_increment, 1),
897
+ hit_count:
898
+ current.hit_count + clampHitIncrement(options.hit_count_increment, 1),
724
899
  last_seen_ms: Math.max(
725
900
  current.last_seen_ms,
726
901
  coerceTimestamp(options.last_seen_ms, Date.now()),
727
902
  ),
728
903
  };
729
- adaptiveRules.set(getAdaptiveRuleKey(current.project_slug, current.pattern), next);
904
+ adaptiveRules.set(
905
+ getAdaptiveRuleKey(current.project_slug, current.pattern),
906
+ next,
907
+ );
730
908
  return clone(next);
731
909
  },
732
910
 
733
911
  pruneStaleRules(maxAge_ms = 30 * 24 * 3600 * 1000, minConfidence = 0.2) {
734
- const cutoff = Date.now() - clampRetentionMs(maxAge_ms, 30 * 24 * 3600 * 1000);
912
+ const cutoff =
913
+ Date.now() - clampRetentionMs(maxAge_ms, 30 * 24 * 3600 * 1000);
735
914
  let removed = 0;
736
915
  for (const [key, rule] of Array.from(adaptiveRules.entries())) {
737
916
  if (rule.last_seen_ms < cutoff && rule.confidence < minConfidence) {
@@ -741,8 +920,25 @@ export function createMemoryStore() {
741
920
  }
742
921
  return removed;
743
922
  },
923
+
924
+ listAdaptiveRules(projectSlug) {
925
+ const results = [];
926
+ for (const rule of adaptiveRules.values()) {
927
+ if (!projectSlug || rule.project_slug === projectSlug) {
928
+ results.push(clone(rule));
929
+ }
930
+ }
931
+ return results.sort((a, b) => b.confidence - a.confidence);
932
+ },
933
+
934
+ deleteAdaptiveRule(projectSlug, pattern) {
935
+ const identity = normalizeAdaptiveRuleIdentity(projectSlug, pattern);
936
+ if (!identity) return false;
937
+ return adaptiveRules.delete(
938
+ getAdaptiveRuleKey(identity.project_slug, identity.pattern),
939
+ );
940
+ },
744
941
  };
745
942
 
746
943
  return store;
747
944
  }
748
-