@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.
- package/hooks/agent-route-guard.mjs +14 -10
- package/hooks/cross-review-tracker.mjs +21 -8
- package/hooks/error-context.mjs +47 -7
- package/hooks/hook-adaptive-collector.mjs +18 -16
- package/hooks/hook-manager.mjs +93 -32
- package/hooks/hook-orchestrator.mjs +279 -33
- package/hooks/hook-registry.json +104 -7
- package/hooks/hooks.json +49 -1
- package/hooks/keyword-rules.json +183 -23
- package/hooks/lib/resolve-root.mjs +1 -1
- package/hooks/mcp-config-watcher.mjs +6 -2
- package/hooks/permission-safe-allow.mjs +233 -0
- package/hooks/pipeline-stop.mjs +369 -22
- package/hooks/post-tool-tips.mjs +306 -0
- package/hooks/pre-compact-snapshot.mjs +181 -0
- package/hooks/safety-guard.mjs +308 -18
- package/hooks/session-end-cleanup.mjs +345 -0
- package/hooks/session-start-fast.mjs +214 -0
- package/hooks/subagent-tracker.mjs +267 -0
- package/hooks/subagent-verifier.mjs +9 -4
- package/hub/account-broker.mjs +1036 -66
- package/hub/adaptive-diagnostic.mjs +75 -48
- package/hub/adaptive-inject.mjs +96 -58
- package/hub/adaptive-memory.mjs +156 -42
- package/hub/adaptive.mjs +60 -31
- package/hub/assign-callbacks.mjs +163 -126
- package/hub/bridge.mjs +832 -291
- package/hub/cli-adapter-base.mjs +321 -97
- package/hub/codex-adapter.mjs +179 -93
- package/hub/codex-compat.mjs +4 -3
- package/hub/codex-preflight.mjs +113 -37
- package/hub/delegator/contracts.mjs +19 -23
- package/hub/delegator/index.mjs +14 -14
- package/hub/delegator/service.mjs +88 -64
- package/hub/delegator/tool-definitions.mjs +35 -35
- package/hub/dynamic-routing-engine.mjs +511 -0
- package/hub/fullcycle.mjs +34 -17
- package/hub/gemini-adapter.mjs +77 -95
- package/hub/hitl.mjs +202 -143
- package/hub/intent.mjs +171 -39
- package/hub/lib/bash-path.mjs +73 -0
- package/hub/lib/cache-guard.mjs +43 -17
- package/hub/lib/env-detect.mjs +145 -0
- package/hub/lib/hosts-compat.mjs +319 -0
- package/hub/lib/mcp-response-cache.mjs +239 -0
- package/hub/lib/memory-store.mjs +303 -107
- package/hub/lib/path-utils.mjs +165 -0
- package/hub/lib/phase-manager.mjs +493 -0
- package/hub/lib/process-utils.mjs +797 -104
- package/hub/lib/prompt-tmp.mjs +33 -0
- package/hub/lib/risk-tier.mjs +53 -0
- package/hub/lib/spawn-trace.mjs +561 -0
- package/hub/lib/ssh-command.mjs +44 -24
- package/hub/lib/ssh-retry.mjs +5 -1
- package/hub/lib/staged-file-detect.mjs +43 -0
- package/hub/lib/state-snapshot.mjs +293 -0
- package/hub/lib/tfx-route-args.mjs +309 -0
- package/hub/lib/timeline-adapter.mjs +190 -0
- package/hub/lib/trace-recorder.mjs +153 -0
- package/hub/lib/uuidv7.mjs +4 -3
- package/hub/memory-doctor.mjs +266 -106
- package/hub/middleware/quota-middleware.mjs +70 -0
- package/hub/middleware/request-logger.mjs +61 -34
- package/hub/paths.mjs +9 -9
- package/hub/pipeline/gates/confidence.mjs +34 -15
- package/hub/pipeline/gates/consensus.mjs +27 -15
- package/hub/pipeline/gates/index.mjs +7 -3
- package/hub/pipeline/gates/selfcheck.mjs +57 -19
- package/hub/pipeline/index.mjs +77 -42
- package/hub/pipeline/state.mjs +10 -10
- package/hub/pipeline/transitions.mjs +40 -23
- package/hub/platform.mjs +65 -48
- package/hub/quality/deslop.mjs +70 -49
- package/hub/reflexion.mjs +116 -58
- package/hub/research.mjs +34 -25
- package/hub/router.mjs +941 -791
- package/hub/routing/complexity.mjs +132 -29
- package/hub/routing/index.mjs +17 -12
- package/hub/routing/q-learning.mjs +76 -28
- package/hub/routing-snapshot.mjs +261 -0
- package/hub/session-fingerprint.mjs +129 -63
- package/hub/state.mjs +86 -44
- package/hub/team/retry-state-machine.mjs +267 -0
- package/hub/token-mode.mjs +76 -72
- package/hub/workers/worker-utils.mjs +22 -16
- package/hud/colors.mjs +27 -9
- package/hud/constants.mjs +184 -27
- package/hud/context-monitor.mjs +133 -44
- package/hud/hud-qos-status.mjs +153 -50
- package/hud/mission-board.mjs +56 -0
- package/hud/providers/claude.mjs +398 -114
- package/hud/providers/codex.mjs +96 -21
- package/hud/providers/gemini.mjs +184 -51
- package/hud/renderers.mjs +413 -123
- package/hud/terminal.mjs +101 -31
- package/hud/utils.mjs +81 -39
- package/mesh/index.mjs +69 -0
- package/mesh/mesh-budget.mjs +137 -0
- package/mesh/mesh-heartbeat.mjs +100 -0
- package/mesh/mesh-protocol.mjs +96 -0
- package/mesh/mesh-queue.mjs +163 -0
- package/mesh/mesh-registry.mjs +78 -0
- package/mesh/mesh-router.mjs +77 -0
- package/package.json +5 -1
- package/scripts/lib/agent-json.mjs +27 -0
- package/scripts/lib/async.mjs +174 -0
- package/scripts/lib/claudemd-scanner.mjs +6 -1
- package/scripts/lib/cli-agy.mjs +62 -0
- package/scripts/lib/cli-claude.mjs +78 -0
- package/scripts/lib/cli-codex.mjs +199 -0
- package/scripts/lib/cli-gemini.mjs +67 -0
- package/scripts/lib/codex-recovery.sh +50 -0
- package/scripts/lib/context.mjs +3 -3
- package/scripts/lib/cross-review-utils.mjs +13 -5
- package/scripts/lib/doctor-env-checks.mjs +121 -0
- package/scripts/lib/dynamic-route-cli.mjs +107 -0
- package/scripts/lib/env-probe.mjs +58 -30
- package/scripts/lib/env.mjs +14 -0
- package/scripts/lib/gemini-profiles.mjs +44 -10
- package/scripts/lib/handoff.mjs +33 -17
- package/scripts/lib/hook-utils.mjs +8 -6
- package/scripts/lib/hub.mjs +48 -0
- package/scripts/lib/keyword-rules.mjs +48 -19
- package/scripts/lib/logger.mjs +24 -24
- package/scripts/lib/mcp-filter.mjs +393 -249
- package/scripts/lib/mcp-gateway-health-check.mjs +159 -0
- package/scripts/lib/mcp-gateway-servers.mjs +70 -0
- package/scripts/lib/mcp-gateway-wrapper-check.mjs +35 -0
- package/scripts/lib/mcp-guard-engine.mjs +1282 -116
- package/scripts/lib/mcp-health.mjs +773 -0
- package/scripts/lib/mcp-manifest.mjs +24 -14
- package/scripts/lib/mcp-server-catalog.mjs +355 -118
- package/scripts/lib/pid.mjs +63 -0
- package/scripts/lib/process-utils.mjs +26 -0
- package/scripts/lib/psmux-info.mjs +72 -12
- package/scripts/lib/quota.mjs +47 -0
- package/scripts/lib/remote-spawn-transfer.mjs +44 -14
- package/scripts/lib/skill-state.mjs +220 -0
- package/scripts/lib/skill-template.mjs +36 -11
- package/scripts/lib/team.mjs +75 -0
- package/scripts/lib/timeout.mjs +66 -0
- package/scripts/lib/tmp.mjs +26 -0
- package/scripts/lib/toml.mjs +113 -0
package/hub/lib/memory-store.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// hub/lib/memory-store.mjs — In-memory store (SQLite-free fallback for @triflux/core)
|
|
2
|
-
|
|
3
|
-
import { recalcConfidence } from
|
|
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(
|
|
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 {
|
|
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(
|
|
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:
|
|
145
|
+
type: "memory",
|
|
132
146
|
db: null,
|
|
133
147
|
uuidv7,
|
|
134
148
|
|
|
135
149
|
close() {},
|
|
136
150
|
|
|
137
|
-
registerAgent({
|
|
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:
|
|
171
|
+
status: "online",
|
|
150
172
|
metadata: clone(metadata),
|
|
151
173
|
};
|
|
152
174
|
agents.set(agent_id, next);
|
|
153
|
-
return {
|
|
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)
|
|
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 =
|
|
167
|
-
return {
|
|
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 !==
|
|
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(
|
|
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 ===
|
|
200
|
-
agent.status =
|
|
240
|
+
if (agent.status === "online" && agent.lease_expires_ms < now) {
|
|
241
|
+
agent.status = "stale";
|
|
201
242
|
stale += 1;
|
|
202
|
-
} else if (
|
|
203
|
-
agent.status
|
|
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({
|
|
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
|
|
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(
|
|
323
|
+
getAuditMessagesForAgent(
|
|
324
|
+
agentId,
|
|
325
|
+
{ max_messages = 20, include_topics = null } = {},
|
|
326
|
+
) {
|
|
264
327
|
const limit = clampMaxMessages(max_messages);
|
|
265
|
-
const topics =
|
|
266
|
-
|
|
267
|
-
|
|
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(
|
|
271
|
-
message
|
|
272
|
-
|
|
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, {
|
|
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({
|
|
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:
|
|
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 {
|
|
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 ===
|
|
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 ===
|
|
338
|
-
request.state =
|
|
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 =
|
|
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 =
|
|
369
|
-
task =
|
|
450
|
+
topic = "assign.job",
|
|
451
|
+
task = "",
|
|
370
452
|
payload = {},
|
|
371
|
-
status =
|
|
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 ||
|
|
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 ===
|
|
412
|
-
completed_at_ms: [
|
|
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 = [
|
|
432
|
-
|
|
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(
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
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,
|
|
449
|
-
return patch.deadline_ms == null
|
|
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 ===
|
|
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,
|
|
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,
|
|
461
|
-
?
|
|
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,
|
|
464
|
-
?
|
|
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,
|
|
578
|
+
started_at_ms: Object.hasOwn(patch, "started_at_ms")
|
|
468
579
|
? patch.started_at_ms
|
|
469
|
-
:
|
|
470
|
-
|
|
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
|
-
:
|
|
473
|
-
|
|
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 =
|
|
495
|
-
|
|
496
|
-
|
|
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(
|
|
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(
|
|
622
|
+
.filter(
|
|
623
|
+
(job) => !correlation_id || job.correlation_id === correlation_id,
|
|
624
|
+
)
|
|
502
625
|
.filter((job) => !statusList.length || statusList.includes(job.status))
|
|
503
|
-
.filter(
|
|
504
|
-
|
|
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(
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
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,
|
|
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 ===
|
|
536
|
-
message.status =
|
|
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:
|
|
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 {
|
|
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 !==
|
|
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 !==
|
|
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
|
-
|
|
573
|
-
|
|
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(
|
|
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) =>
|
|
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) =>
|
|
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(
|
|
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(
|
|
593
|
-
assign_running: countByStatus(
|
|
594
|
-
assign_failed: countByStatus(
|
|
595
|
-
assign_timed_out: countByStatus(
|
|
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 =
|
|
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(
|
|
642
|
-
|
|
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 {
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
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:
|
|
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(
|
|
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 =
|
|
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
|
-
|