triflux 10.3.4 → 10.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/bin/tfx-doctor-tui.mjs +1 -1
- package/bin/tfx-doctor.mjs +6 -1
- package/bin/tfx-profile.mjs +1 -1
- package/bin/tfx-setup-tui.mjs +1 -1
- package/bin/tfx-setup.mjs +6 -1
- package/bin/triflux.mjs +2396 -1140
- package/hooks/agent-route-guard.mjs +12 -8
- package/hooks/cross-review-tracker.mjs +21 -8
- package/hooks/error-context.mjs +19 -7
- package/hooks/hook-adaptive-collector.mjs +18 -16
- package/hooks/hook-manager.mjs +93 -32
- package/hooks/hook-orchestrator.mjs +108 -24
- package/hooks/hook-registry.json +11 -0
- package/hooks/keyword-rules.json +6 -10
- package/hooks/lib/resolve-root.mjs +1 -1
- package/hooks/mcp-config-watcher.mjs +6 -2
- package/hooks/pipeline-stop.mjs +3 -6
- package/hooks/safety-guard.mjs +99 -28
- package/hooks/session-start-fast.mjs +143 -0
- package/hooks/subagent-verifier.mjs +5 -4
- package/hub/account-broker.mjs +256 -60
- package/hub/adaptive-diagnostic.mjs +75 -48
- package/hub/adaptive-inject.mjs +95 -57
- package/hub/adaptive-memory.mjs +156 -42
- package/hub/adaptive.mjs +60 -31
- package/hub/assign-callbacks.mjs +67 -30
- package/hub/bridge.mjs +0 -1
- package/hub/cli-adapter-base.mjs +200 -48
- package/hub/codex-adapter.mjs +76 -96
- package/hub/codex-compat.mjs +3 -3
- package/hub/codex-preflight.mjs +63 -37
- package/hub/delegator/contracts.mjs +19 -23
- package/hub/delegator/index.mjs +3 -3
- package/hub/delegator/service.mjs +88 -64
- package/hub/delegator/tool-definitions.mjs +5 -5
- package/hub/fullcycle.mjs +33 -17
- package/hub/gemini-adapter.mjs +69 -94
- package/hub/hitl.mjs +89 -30
- package/hub/intent.mjs +161 -38
- package/hub/lib/cache-guard.mjs +43 -17
- package/hub/lib/mcp-response-cache.mjs +66 -32
- package/hub/lib/memory-store.mjs +285 -111
- package/hub/lib/path-utils.mjs +35 -37
- package/hub/lib/process-utils.mjs +106 -37
- package/hub/lib/spawn-trace.mjs +527 -0
- package/hub/lib/ssh-command.mjs +34 -4
- package/hub/lib/ssh-retry.mjs +5 -1
- package/hub/lib/uuidv7.mjs +4 -3
- package/hub/memory-doctor.mjs +266 -106
- 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 +57 -48
- package/hub/promote-penalties.mjs +25 -7
- package/hub/quality/deslop.mjs +70 -49
- package/hub/research.mjs +32 -25
- package/hub/router.mjs +240 -107
- package/hub/routing/complexity.mjs +132 -29
- package/hub/routing/index.mjs +17 -12
- package/hub/routing/q-learning.mjs +76 -28
- package/hub/server.mjs +4 -4
- package/hub/session-fingerprint.mjs +126 -60
- package/hub/state.mjs +84 -43
- package/hub/store-adapter.mjs +59 -26
- package/hub/store.mjs +356 -153
- package/hub/team/agent-map.json +22 -7
- package/hub/team/ansi.mjs +186 -122
- package/hub/team/backend.mjs +28 -10
- package/hub/team/cli/commands/attach.mjs +29 -9
- package/hub/team/cli/commands/control.mjs +29 -8
- package/hub/team/cli/commands/debug.mjs +32 -11
- package/hub/team/cli/commands/focus.mjs +38 -11
- package/hub/team/cli/commands/interrupt.mjs +18 -6
- package/hub/team/cli/commands/kill.mjs +16 -5
- package/hub/team/cli/commands/list.mjs +11 -4
- package/hub/team/cli/commands/send.mjs +19 -6
- package/hub/team/cli/commands/start/index.mjs +154 -31
- package/hub/team/cli/commands/start/parse-args.mjs +38 -11
- package/hub/team/cli/commands/start/start-headless.mjs +112 -36
- package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
- package/hub/team/cli/commands/start/start-mux.mjs +70 -21
- package/hub/team/cli/commands/start/start-wt.mjs +29 -12
- package/hub/team/cli/commands/status.mjs +43 -14
- package/hub/team/cli/commands/stop.mjs +11 -4
- package/hub/team/cli/commands/task.mjs +8 -3
- package/hub/team/cli/commands/tasks.mjs +1 -1
- package/hub/team/cli/index.mjs +2 -2
- package/hub/team/cli/manifest.mjs +38 -8
- package/hub/team/cli/render.mjs +30 -8
- package/hub/team/cli/services/attach-fallback.mjs +31 -11
- package/hub/team/cli/services/hub-client.mjs +42 -14
- package/hub/team/cli/services/member-selector.mjs +11 -4
- package/hub/team/cli/services/native-control.mjs +48 -21
- package/hub/team/cli/services/runtime-mode.mjs +2 -1
- package/hub/team/cli/services/state-store.mjs +25 -8
- package/hub/team/cli/services/task-model.mjs +16 -6
- package/hub/team/conductor-mesh-bridge.mjs +24 -23
- package/hub/team/conductor.mjs +8 -4
- package/hub/team/dashboard-anchor.mjs +4 -5
- package/hub/team/dashboard-layout.mjs +3 -1
- package/hub/team/dashboard-open.mjs +41 -21
- package/hub/team/dashboard.mjs +76 -28
- package/hub/team/event-log.mjs +18 -10
- package/hub/team/handoff.mjs +31 -15
- package/hub/team/headless.mjs +2 -1
- package/hub/team/health-probe.mjs +69 -54
- package/hub/team/launcher-template.mjs +16 -13
- package/hub/team/native-supervisor.mjs +65 -21
- package/hub/team/native.mjs +74 -35
- package/hub/team/nativeProxy.mjs +184 -113
- package/hub/team/notify.mjs +119 -76
- package/hub/team/orchestrator.mjs +9 -4
- package/hub/team/pane.mjs +12 -7
- package/hub/team/process-cleanup.mjs +25 -16
- package/hub/team/psmux.mjs +491 -201
- package/hub/team/remote-probe.mjs +68 -52
- package/hub/team/remote-session.mjs +117 -59
- package/hub/team/remote-watcher.mjs +61 -33
- package/hub/team/routing.mjs +51 -25
- package/hub/team/runtime-strategy.mjs +3 -1
- package/hub/team/session.mjs +98 -34
- package/hub/team/staleState.mjs +72 -30
- package/hub/team/swarm-locks.mjs +15 -13
- package/hub/team/swarm-planner.mjs +32 -21
- package/hub/team/swarm-reconciler.mjs +48 -23
- package/hub/team/tui-lite.mjs +266 -68
- package/hub/team/tui-remote-adapter.mjs +14 -10
- package/hub/team/tui-viewer.mjs +99 -43
- package/hub/team/tui.mjs +708 -271
- package/hub/team/worktree-lifecycle.mjs +152 -58
- package/hub/team/wt-manager.mjs +24 -14
- package/hub/token-mode.mjs +71 -71
- package/hub/tray.mjs +66 -23
- package/hub/workers/claude-worker.mjs +162 -118
- package/hub/workers/codex-mcp.mjs +192 -141
- package/hub/workers/delegator-mcp.mjs +507 -333
- package/hub/workers/factory.mjs +8 -8
- package/hub/workers/gemini-worker.mjs +115 -84
- package/hub/workers/interface.mjs +6 -1
- package/hub/workers/worker-utils.mjs +21 -14
- package/hud/colors.mjs +27 -9
- package/hud/constants.mjs +162 -26
- package/hud/context-monitor.mjs +82 -41
- package/hud/hud-qos-status.mjs +129 -49
- package/hud/mission-board.mjs +6 -3
- package/hud/providers/claude.mjs +226 -115
- package/hud/providers/codex.mjs +62 -22
- package/hud/providers/gemini.mjs +168 -56
- package/hud/renderers.mjs +384 -119
- package/hud/terminal.mjs +101 -31
- package/hud/utils.mjs +78 -38
- package/mesh/index.mjs +11 -5
- package/mesh/mesh-budget.mjs +18 -9
- package/mesh/mesh-heartbeat.mjs +1 -1
- package/mesh/mesh-queue.mjs +3 -5
- package/mesh/mesh-router.mjs +5 -4
- package/package.json +2 -1
- package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
- package/scripts/__tests__/keyword-detector.test.mjs +77 -28
- package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
- package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
- package/scripts/__tests__/remote-spawn.test.mjs +10 -4
- package/scripts/__tests__/session-start-fast.test.mjs +36 -0
- package/scripts/__tests__/skill-template.test.mjs +98 -50
- package/scripts/__tests__/smoke.test.mjs +1 -1
- package/scripts/__tests__/spawn-trace.test.mjs +102 -0
- package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
- package/scripts/cache-doctor.mjs +11 -4
- package/scripts/cache-warmup.mjs +96 -37
- package/scripts/claudemd-sync.mjs +27 -17
- package/scripts/codex-gateway-preflight.mjs +52 -37
- package/scripts/codex-mcp-gateway-sync.mjs +59 -39
- package/scripts/completions/tfx.bash +47 -47
- package/scripts/completions/tfx.fish +44 -44
- package/scripts/completions/tfx.zsh +83 -83
- package/scripts/config-audit.mjs +232 -0
- package/scripts/convert-to-tmpl.mjs +54 -0
- package/scripts/cross-review-gate.mjs +35 -12
- package/scripts/cross-review-tracker.mjs +21 -8
- package/scripts/demo.mjs +35 -17
- package/scripts/doctor-diagnose.mjs +284 -0
- package/scripts/gen-skill-docs.mjs +7 -2
- package/scripts/gen-skill-manifest.mjs +2 -1
- package/scripts/headless-guard.mjs +86 -48
- package/scripts/hub-ensure.mjs +45 -26
- package/scripts/keyword-detector.mjs +41 -20
- package/scripts/keyword-rules-expander.mjs +47 -30
- package/scripts/lib/claudemd-scanner.mjs +6 -1
- package/scripts/lib/context.mjs +3 -3
- package/scripts/lib/cross-review-utils.mjs +6 -3
- package/scripts/lib/env-probe.mjs +47 -28
- 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/keyword-rules.mjs +43 -19
- package/scripts/lib/logger.mjs +24 -24
- package/scripts/lib/mcp-filter.mjs +377 -239
- package/scripts/lib/mcp-guard-engine.mjs +194 -79
- package/scripts/lib/mcp-manifest.mjs +23 -13
- package/scripts/lib/mcp-server-catalog.mjs +300 -63
- package/scripts/lib/psmux-info.mjs +11 -6
- package/scripts/lib/remote-spawn-transfer.mjs +44 -14
- package/scripts/lib/skill-template.mjs +30 -7
- package/scripts/mcp-check.mjs +58 -39
- package/scripts/mcp-gateway-config.mjs +83 -39
- package/scripts/mcp-gateway-ensure.mjs +43 -35
- package/scripts/mcp-gateway-integration-test.mjs +70 -58
- package/scripts/mcp-gateway-start.mjs +126 -60
- package/scripts/mcp-gateway-verify.mjs +24 -22
- package/scripts/mcp-safety-guard.mjs +44 -11
- package/scripts/notion-read.mjs +199 -84
- package/scripts/pack.mjs +94 -89
- package/scripts/preflight-cache.mjs +27 -10
- package/scripts/preinstall.mjs +42 -13
- package/scripts/remote-spawn.mjs +309 -94
- package/scripts/run.cjs +8 -5
- package/scripts/session-spawn-helper.mjs +130 -39
- package/scripts/session-stale-cleanup.mjs +123 -0
- package/scripts/setup.mjs +941 -492
- package/scripts/test-lock.mjs +20 -7
- package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
- package/scripts/tfx-batch-stats.mjs +32 -11
- package/scripts/tfx-gate-activate.mjs +11 -4
- package/scripts/tfx-route-post.mjs +87 -20
- package/scripts/tfx-route-worker.mjs +57 -51
- package/scripts/tfx-route.sh +41 -124
- package/scripts/tmp-cleanup.mjs +21 -7
- package/scripts/token-snapshot.mjs +204 -85
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
- package/skills/.omc/state/idle-notif-cooldown.json +3 -0
- package/skills/.omc/state/last-tool-error.json +7 -0
- package/skills/.omc/state/subagent-tracking.json +7 -0
- package/skills/_templates/base.md +1 -6
- package/skills/merge-worktree/SKILL.md.tmpl +144 -0
- package/skills/shared/telemetry-segment.md +6 -0
- package/skills/star-prompt/SKILL.md.tmpl +222 -0
- package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
- package/skills/tfx-analysis/skill.json +1 -6
- package/skills/tfx-auto/SKILL.md +1 -0
- package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
- package/skills/tfx-auto-codex/skill.json +1 -3
- package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
- package/skills/tfx-autopilot/skill.json +1 -5
- package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
- package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
- package/skills/tfx-autoroute/skill.json +1 -7
- package/skills/tfx-codex/SKILL.md +1 -0
- package/skills/tfx-codex/skill.json +1 -3
- package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
- package/skills/tfx-codex-swarm/evals/evals.json +1 -1
- package/skills/tfx-codex-swarm/skill.json +1 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
- package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
- package/skills/tfx-debate/SKILL.md.tmpl +192 -0
- package/skills/tfx-debate/skill.json +1 -7
- package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
- package/skills/tfx-deep-analysis/skill.json +1 -5
- package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
- package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
- package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
- package/skills/tfx-deep-qa/skill.json +1 -6
- package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
- package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
- package/skills/tfx-doctor/SKILL.md +21 -0
- package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
- package/skills/tfx-doctor/skill.json +1 -3
- package/skills/tfx-find/SKILL.md +1 -0
- package/skills/tfx-forge/SKILL.md.tmpl +187 -0
- package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
- package/skills/tfx-fullcycle/skill.json +1 -6
- package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
- package/skills/tfx-gemini/skill.json +1 -3
- package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
- package/skills/tfx-hooks/skill.json +1 -3
- package/skills/tfx-hub/SKILL.md.tmpl +212 -0
- package/skills/tfx-hub/skill.json +1 -3
- package/skills/tfx-index/SKILL.md +1 -0
- package/skills/tfx-index/skill.json +1 -6
- package/skills/tfx-interview/SKILL.md.tmpl +285 -0
- package/skills/tfx-multi/SKILL.md.tmpl +183 -0
- package/skills/tfx-multi/skill.json +1 -3
- package/skills/tfx-panel/SKILL.md.tmpl +189 -0
- package/skills/tfx-panel/skill.json +1 -7
- package/skills/tfx-persist/SKILL.md.tmpl +270 -0
- package/skills/tfx-persist/skill.json +1 -7
- package/skills/tfx-plan/SKILL.md +1 -0
- package/skills/tfx-plan/skill.json +1 -6
- package/skills/tfx-profile/SKILL.md.tmpl +239 -0
- package/skills/tfx-profile/skill.json +1 -3
- package/skills/tfx-prune/SKILL.md.tmpl +200 -0
- package/skills/tfx-prune/skill.json +1 -7
- package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
- package/skills/tfx-psmux-rules/skill.json +1 -4
- package/skills/tfx-qa/SKILL.md +1 -0
- package/skills/tfx-qa/skill.json +1 -6
- package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
- package/skills/tfx-ralph/skill.json +1 -4
- package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
- package/skills/tfx-remote-setup/skill.json +1 -3
- package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
- package/skills/tfx-remote-spawn/references/hosts.json +16 -0
- package/skills/tfx-remote-spawn/skill.json +1 -4
- package/skills/tfx-research/SKILL.md +1 -0
- package/skills/tfx-review/SKILL.md +1 -0
- package/skills/tfx-review/skill.json +1 -6
- package/skills/tfx-setup/SKILL.md.tmpl +504 -0
- package/skills/tfx-setup/skill.json +1 -3
- package/skills/tfx-swarm/SKILL.md +22 -0
- package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
- package/tui/codex-profile.mjs +88 -33
- package/tui/core.mjs +45 -15
- package/tui/doctor.mjs +75 -28
- package/tui/gemini-profile.mjs +74 -29
- package/tui/monitor-data.mjs +8 -4
- package/tui/monitor.mjs +71 -27
- package/tui/setup.mjs +133 -42
package/hub/store.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// hub/store.mjs — SQLite 감사 로그/메타데이터 저장소
|
|
2
2
|
// 실시간 배달 큐는 router/pipe가 담당하고, SQLite는 재생/감사 용도로만 유지한다.
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { uuidv7 } from
|
|
3
|
+
|
|
4
|
+
import { mkdirSync, readFileSync } from "node:fs";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { uuidv7 } from "./lib/uuidv7.mjs";
|
|
9
|
+
import { recalcConfidence } from "./reflexion.mjs";
|
|
9
10
|
|
|
10
11
|
export { uuidv7 };
|
|
11
12
|
|
|
@@ -14,7 +15,11 @@ const require = createRequire(import.meta.url);
|
|
|
14
15
|
|
|
15
16
|
function parseJson(str, fallback = null) {
|
|
16
17
|
if (str == null) return fallback;
|
|
17
|
-
try {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(str);
|
|
20
|
+
} catch {
|
|
21
|
+
return fallback;
|
|
22
|
+
}
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
function parseAgentRow(row) {
|
|
@@ -60,7 +65,7 @@ function parseReflexionRow(row) {
|
|
|
60
65
|
const { context_json, adaptive_state_json, ...rest } = row;
|
|
61
66
|
return {
|
|
62
67
|
...rest,
|
|
63
|
-
type: rest.type ||
|
|
68
|
+
type: rest.type || "reflexion",
|
|
64
69
|
context: parseJson(context_json, {}),
|
|
65
70
|
adaptive_state: parseJson(adaptive_state_json, {}),
|
|
66
71
|
};
|
|
@@ -81,13 +86,13 @@ function ensureColumn(db, tableName, columnName, definition) {
|
|
|
81
86
|
* @param {string} dbPath
|
|
82
87
|
*/
|
|
83
88
|
export async function importBetterSqlite3() {
|
|
84
|
-
const mod = await import(
|
|
89
|
+
const mod = await import("better-sqlite3");
|
|
85
90
|
return mod.default ?? mod;
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
function resolveBetterSqlite3(options = {}) {
|
|
89
94
|
if (options.DatabaseCtor) return options.DatabaseCtor;
|
|
90
|
-
const mod = require(
|
|
95
|
+
const mod = require("better-sqlite3");
|
|
91
96
|
return mod.default ?? mod;
|
|
92
97
|
}
|
|
93
98
|
|
|
@@ -96,18 +101,26 @@ export function createStore(dbPath, options = {}) {
|
|
|
96
101
|
const Database = resolveBetterSqlite3(options);
|
|
97
102
|
const db = new Database(dbPath);
|
|
98
103
|
|
|
99
|
-
db.pragma(
|
|
100
|
-
db.pragma(
|
|
101
|
-
db.pragma(
|
|
102
|
-
db.pragma(
|
|
103
|
-
db.pragma(
|
|
104
|
-
|
|
105
|
-
const schemaSQL = readFileSync(join(__dirname,
|
|
106
|
-
db.exec(
|
|
107
|
-
|
|
104
|
+
db.pragma("journal_mode = WAL");
|
|
105
|
+
db.pragma("synchronous = NORMAL");
|
|
106
|
+
db.pragma("foreign_keys = ON");
|
|
107
|
+
db.pragma("busy_timeout = 5000");
|
|
108
|
+
db.pragma("wal_autocheckpoint = 1000");
|
|
109
|
+
|
|
110
|
+
const schemaSQL = readFileSync(join(__dirname, "schema.sql"), "utf8");
|
|
111
|
+
db.exec(
|
|
112
|
+
"CREATE TABLE IF NOT EXISTS _meta (key TEXT PRIMARY KEY, value TEXT)",
|
|
113
|
+
);
|
|
114
|
+
const SCHEMA_VERSION = "4";
|
|
108
115
|
const curVer = (() => {
|
|
109
|
-
try {
|
|
110
|
-
|
|
116
|
+
try {
|
|
117
|
+
return db
|
|
118
|
+
.prepare("SELECT value FROM _meta WHERE key='schema_version'")
|
|
119
|
+
.pluck()
|
|
120
|
+
.get();
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
111
124
|
})();
|
|
112
125
|
// 마이그레이션 전략: 스키마 버전이 다르면 schema.sql을 재실행한다.
|
|
113
126
|
// schema.sql은 CREATE TABLE IF NOT EXISTS 패턴을 사용하므로 멱등하게 적용된다.
|
|
@@ -115,13 +128,27 @@ export function createStore(dbPath, options = {}) {
|
|
|
115
128
|
if (curVer !== SCHEMA_VERSION) {
|
|
116
129
|
if (curVer != null) {
|
|
117
130
|
// 이미 버전이 기록된 DB에서 버전 불일치가 발생한 경우 경고한다.
|
|
118
|
-
console.warn(
|
|
131
|
+
console.warn(
|
|
132
|
+
`[store] schema version mismatch: found=${curVer} expected=${SCHEMA_VERSION}. Applying schema.sql (idempotent).`,
|
|
133
|
+
);
|
|
119
134
|
}
|
|
120
135
|
db.exec(schemaSQL);
|
|
121
|
-
db.prepare(
|
|
136
|
+
db.prepare(
|
|
137
|
+
"INSERT OR REPLACE INTO _meta (key, value) VALUES ('schema_version', ?)",
|
|
138
|
+
).run(SCHEMA_VERSION);
|
|
122
139
|
}
|
|
123
|
-
ensureColumn(
|
|
124
|
-
|
|
140
|
+
ensureColumn(
|
|
141
|
+
db,
|
|
142
|
+
"reflexion_entries",
|
|
143
|
+
"type",
|
|
144
|
+
"TEXT NOT NULL DEFAULT 'reflexion'",
|
|
145
|
+
);
|
|
146
|
+
ensureColumn(
|
|
147
|
+
db,
|
|
148
|
+
"reflexion_entries",
|
|
149
|
+
"adaptive_state_json",
|
|
150
|
+
"TEXT NOT NULL DEFAULT '{}'",
|
|
151
|
+
);
|
|
125
152
|
|
|
126
153
|
const S = {
|
|
127
154
|
upsertAgent: db.prepare(`
|
|
@@ -136,23 +163,37 @@ export function createStore(dbPath, options = {}) {
|
|
|
136
163
|
lease_expires_ms=excluded.lease_expires_ms,
|
|
137
164
|
status=excluded.status,
|
|
138
165
|
metadata_json=excluded.metadata_json`),
|
|
139
|
-
getAgent: db.prepare(
|
|
140
|
-
setAgentTopics: db.prepare(
|
|
141
|
-
|
|
142
|
-
|
|
166
|
+
getAgent: db.prepare("SELECT * FROM agents WHERE agent_id = ?"),
|
|
167
|
+
setAgentTopics: db.prepare(
|
|
168
|
+
"UPDATE agents SET topics_json=?, last_seen_ms=? WHERE agent_id=?",
|
|
169
|
+
),
|
|
170
|
+
heartbeat: db.prepare(
|
|
171
|
+
"UPDATE agents SET last_seen_ms=?, lease_expires_ms=?, status='online' WHERE agent_id=?",
|
|
172
|
+
),
|
|
173
|
+
setAgentStatus: db.prepare("UPDATE agents SET status=? WHERE agent_id=?"),
|
|
143
174
|
onlineAgents: db.prepare("SELECT * FROM agents WHERE status != 'offline'"),
|
|
144
|
-
allAgents: db.prepare(
|
|
145
|
-
agentsByTopic: db.prepare(
|
|
146
|
-
|
|
147
|
-
|
|
175
|
+
allAgents: db.prepare("SELECT * FROM agents"),
|
|
176
|
+
agentsByTopic: db.prepare(
|
|
177
|
+
"SELECT a.* FROM agents a, json_each(a.topics_json) t WHERE t.value=? AND a.status != 'offline'",
|
|
178
|
+
),
|
|
179
|
+
markStale: db.prepare(
|
|
180
|
+
"UPDATE agents SET status='stale' WHERE status='online' AND lease_expires_ms < ?",
|
|
181
|
+
),
|
|
182
|
+
markOffline: db.prepare(
|
|
183
|
+
"UPDATE agents SET status='offline' WHERE status='stale' AND lease_expires_ms < ? - 300000",
|
|
184
|
+
),
|
|
148
185
|
|
|
149
186
|
insertAuditMessage: db.prepare(`
|
|
150
187
|
INSERT INTO messages (id, type, from_agent, to_agent, topic, priority, ttl_ms, created_at_ms, expires_at_ms, correlation_id, trace_id, payload_json, status)
|
|
151
188
|
VALUES (@id, @type, @from_agent, @to_agent, @topic, @priority, @ttl_ms, @created_at_ms, @expires_at_ms, @correlation_id, @trace_id, @payload_json, @status)`),
|
|
152
|
-
getMsg: db.prepare(
|
|
153
|
-
getResponse: db.prepare(
|
|
154
|
-
|
|
155
|
-
|
|
189
|
+
getMsg: db.prepare("SELECT * FROM messages WHERE id=?"),
|
|
190
|
+
getResponse: db.prepare(
|
|
191
|
+
"SELECT * FROM messages WHERE correlation_id=? AND type='response' ORDER BY created_at_ms DESC LIMIT 1",
|
|
192
|
+
),
|
|
193
|
+
getMsgsByTrace: db.prepare(
|
|
194
|
+
"SELECT * FROM messages WHERE trace_id=? ORDER BY created_at_ms",
|
|
195
|
+
),
|
|
196
|
+
setMsgStatus: db.prepare("UPDATE messages SET status=? WHERE id=?"),
|
|
156
197
|
recentAgentMessages: db.prepare(`
|
|
157
198
|
SELECT * FROM messages
|
|
158
199
|
WHERE to_agent=?
|
|
@@ -171,13 +212,21 @@ export function createStore(dbPath, options = {}) {
|
|
|
171
212
|
insertHR: db.prepare(`
|
|
172
213
|
INSERT INTO human_requests (request_id, requester_agent, kind, prompt, schema_json, state, deadline_ms, default_action, correlation_id, trace_id, response_json)
|
|
173
214
|
VALUES (@request_id, @requester_agent, @kind, @prompt, @schema_json, @state, @deadline_ms, @default_action, @correlation_id, @trace_id, @response_json)`),
|
|
174
|
-
getHR: db.prepare(
|
|
175
|
-
updateHR: db.prepare(
|
|
215
|
+
getHR: db.prepare("SELECT * FROM human_requests WHERE request_id=?"),
|
|
216
|
+
updateHR: db.prepare(
|
|
217
|
+
"UPDATE human_requests SET state=?, response_json=? WHERE request_id=?",
|
|
218
|
+
),
|
|
176
219
|
pendingHR: db.prepare("SELECT * FROM human_requests WHERE state='pending'"),
|
|
177
|
-
expireHR: db.prepare(
|
|
220
|
+
expireHR: db.prepare(
|
|
221
|
+
"UPDATE human_requests SET state='timed_out' WHERE state='pending' AND deadline_ms < ?",
|
|
222
|
+
),
|
|
178
223
|
|
|
179
|
-
insertDL: db.prepare(
|
|
180
|
-
|
|
224
|
+
insertDL: db.prepare(
|
|
225
|
+
"INSERT OR REPLACE INTO dead_letters (message_id, reason, failed_at_ms, last_error) VALUES (?,?,?,?)",
|
|
226
|
+
),
|
|
227
|
+
getDL: db.prepare(
|
|
228
|
+
"SELECT * FROM dead_letters ORDER BY failed_at_ms DESC LIMIT ?",
|
|
229
|
+
),
|
|
181
230
|
|
|
182
231
|
insertAssign: db.prepare(`
|
|
183
232
|
INSERT INTO assign_jobs (
|
|
@@ -191,7 +240,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
191
240
|
@trace_id, @correlation_id, @last_message_id, @result_json, @error_json,
|
|
192
241
|
@created_at_ms, @updated_at_ms, @started_at_ms, @completed_at_ms, @last_retry_at_ms
|
|
193
242
|
)`),
|
|
194
|
-
getAssign: db.prepare(
|
|
243
|
+
getAssign: db.prepare("SELECT * FROM assign_jobs WHERE job_id = ?"),
|
|
195
244
|
updateAssign: db.prepare(`
|
|
196
245
|
UPDATE assign_jobs SET
|
|
197
246
|
supervisor_agent=@supervisor_agent,
|
|
@@ -218,29 +267,61 @@ export function createStore(dbPath, options = {}) {
|
|
|
218
267
|
last_retry_at_ms=@last_retry_at_ms
|
|
219
268
|
WHERE job_id=@job_id`),
|
|
220
269
|
|
|
221
|
-
findExpired: db.prepare(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
270
|
+
findExpired: db.prepare(
|
|
271
|
+
"SELECT id FROM messages WHERE status='queued' AND expires_at_ms < ?",
|
|
272
|
+
),
|
|
273
|
+
urgentDepth: db.prepare(
|
|
274
|
+
"SELECT COUNT(*) as cnt FROM messages WHERE status='queued' AND priority >= 7",
|
|
275
|
+
),
|
|
276
|
+
normalDepth: db.prepare(
|
|
277
|
+
"SELECT COUNT(*) as cnt FROM messages WHERE status='queued' AND priority < 7",
|
|
278
|
+
),
|
|
279
|
+
onlineCount: db.prepare(
|
|
280
|
+
"SELECT COUNT(*) as cnt FROM agents WHERE status='online'",
|
|
281
|
+
),
|
|
282
|
+
msgCount: db.prepare("SELECT COUNT(*) as cnt FROM messages"),
|
|
283
|
+
dlqDepth: db.prepare("SELECT COUNT(*) as cnt FROM dead_letters"),
|
|
284
|
+
ackedRecent: db.prepare(
|
|
285
|
+
"SELECT COUNT(*) as cnt FROM messages WHERE status='acked' AND created_at_ms > ? - 300000",
|
|
286
|
+
),
|
|
287
|
+
assignCountByStatus: db.prepare(
|
|
288
|
+
"SELECT COUNT(*) as cnt FROM assign_jobs WHERE status = ?",
|
|
289
|
+
),
|
|
290
|
+
activeAssignCount: db.prepare(
|
|
291
|
+
"SELECT COUNT(*) as cnt FROM assign_jobs WHERE status IN ('queued','running')",
|
|
292
|
+
),
|
|
230
293
|
|
|
231
294
|
// reflexion
|
|
232
295
|
insertReflexion: db.prepare(`
|
|
233
296
|
INSERT INTO reflexion_entries (id, type, error_pattern, error_message, context_json, solution, solution_code, adaptive_state_json, confidence, hit_count, success_count, last_hit_ms, created_at_ms, updated_at_ms)
|
|
234
297
|
VALUES (@id, @type, @error_pattern, @error_message, @context_json, @solution, @solution_code, @adaptive_state_json, @confidence, @hit_count, @success_count, @last_hit_ms, @created_at_ms, @updated_at_ms)`),
|
|
235
|
-
getReflexionById: db.prepare(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
298
|
+
getReflexionById: db.prepare(
|
|
299
|
+
"SELECT * FROM reflexion_entries WHERE id = ?",
|
|
300
|
+
),
|
|
301
|
+
findReflexionExact: db.prepare(
|
|
302
|
+
"SELECT * FROM reflexion_entries WHERE error_pattern = ? ORDER BY confidence DESC",
|
|
303
|
+
),
|
|
304
|
+
findReflexionLike: db.prepare(
|
|
305
|
+
"SELECT * FROM reflexion_entries WHERE error_pattern LIKE ? ESCAPE '\\' ORDER BY confidence DESC LIMIT 10",
|
|
306
|
+
),
|
|
307
|
+
updateReflexionHitSuccess: db.prepare(
|
|
308
|
+
"UPDATE reflexion_entries SET hit_count = hit_count + 1, success_count = success_count + 1, last_hit_ms = ?, updated_at_ms = ? WHERE id = ?",
|
|
309
|
+
),
|
|
310
|
+
updateReflexionHitOnly: db.prepare(
|
|
311
|
+
"UPDATE reflexion_entries SET hit_count = hit_count + 1, last_hit_ms = ?, updated_at_ms = ? WHERE id = ?",
|
|
312
|
+
),
|
|
313
|
+
updateReflexionConfidence: db.prepare(
|
|
314
|
+
"UPDATE reflexion_entries SET confidence = ?, updated_at_ms = ? WHERE id = ?",
|
|
315
|
+
),
|
|
316
|
+
pruneReflexionEntries: db.prepare(
|
|
317
|
+
"DELETE FROM reflexion_entries WHERE updated_at_ms < ? AND confidence < ?",
|
|
318
|
+
),
|
|
319
|
+
listReflexionEntries: db.prepare(
|
|
320
|
+
"SELECT * FROM reflexion_entries ORDER BY confidence DESC, updated_at_ms DESC",
|
|
321
|
+
),
|
|
322
|
+
deleteReflexionEntry: db.prepare(
|
|
323
|
+
"DELETE FROM reflexion_entries WHERE id = ?",
|
|
324
|
+
),
|
|
244
325
|
};
|
|
245
326
|
|
|
246
327
|
const assignStatusListeners = new Set();
|
|
@@ -257,7 +338,9 @@ export function createStore(dbPath, options = {}) {
|
|
|
257
338
|
function notifyAssignStatusListeners(row) {
|
|
258
339
|
const event = buildAssignCallbackEvent(row);
|
|
259
340
|
for (const listener of Array.from(assignStatusListeners)) {
|
|
260
|
-
try {
|
|
341
|
+
try {
|
|
342
|
+
listener(event, row);
|
|
343
|
+
} catch {}
|
|
261
344
|
}
|
|
262
345
|
}
|
|
263
346
|
|
|
@@ -287,7 +370,15 @@ export function createStore(dbPath, options = {}) {
|
|
|
287
370
|
db.close();
|
|
288
371
|
},
|
|
289
372
|
|
|
290
|
-
registerAgent({
|
|
373
|
+
registerAgent({
|
|
374
|
+
agent_id,
|
|
375
|
+
cli,
|
|
376
|
+
pid,
|
|
377
|
+
capabilities = [],
|
|
378
|
+
topics = [],
|
|
379
|
+
heartbeat_ttl_ms = 30000,
|
|
380
|
+
metadata = {},
|
|
381
|
+
}) {
|
|
291
382
|
const now = Date.now();
|
|
292
383
|
const leaseExpires = now + heartbeat_ttl_ms;
|
|
293
384
|
S.upsertAgent.run({
|
|
@@ -298,10 +389,15 @@ export function createStore(dbPath, options = {}) {
|
|
|
298
389
|
topics_json: JSON.stringify(topics),
|
|
299
390
|
last_seen_ms: now,
|
|
300
391
|
lease_expires_ms: leaseExpires,
|
|
301
|
-
status:
|
|
392
|
+
status: "online",
|
|
302
393
|
metadata_json: JSON.stringify(metadata),
|
|
303
394
|
});
|
|
304
|
-
return {
|
|
395
|
+
return {
|
|
396
|
+
agent_id,
|
|
397
|
+
lease_id: uuidv7(),
|
|
398
|
+
lease_expires_ms: leaseExpires,
|
|
399
|
+
server_time_ms: now,
|
|
400
|
+
};
|
|
305
401
|
},
|
|
306
402
|
|
|
307
403
|
getAgent(id) {
|
|
@@ -311,12 +407,18 @@ export function createStore(dbPath, options = {}) {
|
|
|
311
407
|
refreshLease(agentId, ttlMs = 30000) {
|
|
312
408
|
const now = Date.now();
|
|
313
409
|
S.heartbeat.run(now, now + ttlMs, agentId);
|
|
314
|
-
return {
|
|
410
|
+
return {
|
|
411
|
+
agent_id: agentId,
|
|
412
|
+
lease_expires_ms: now + ttlMs,
|
|
413
|
+
server_time_ms: now,
|
|
414
|
+
};
|
|
315
415
|
},
|
|
316
416
|
|
|
317
417
|
updateAgentTopics(agentId, topics = []) {
|
|
318
418
|
const now = Date.now();
|
|
319
|
-
return
|
|
419
|
+
return (
|
|
420
|
+
S.setAgentTopics.run(JSON.stringify(topics), now, agentId).changes > 0
|
|
421
|
+
);
|
|
320
422
|
},
|
|
321
423
|
|
|
322
424
|
listOnlineAgents() {
|
|
@@ -343,7 +445,18 @@ export function createStore(dbPath, options = {}) {
|
|
|
343
445
|
return S.setAgentStatus.run(status, agentId).changes > 0;
|
|
344
446
|
},
|
|
345
447
|
|
|
346
|
-
auditLog({
|
|
448
|
+
auditLog({
|
|
449
|
+
type,
|
|
450
|
+
from,
|
|
451
|
+
to,
|
|
452
|
+
topic,
|
|
453
|
+
priority = 5,
|
|
454
|
+
ttl_ms = 300000,
|
|
455
|
+
payload = {},
|
|
456
|
+
trace_id,
|
|
457
|
+
correlation_id,
|
|
458
|
+
status = "queued",
|
|
459
|
+
}) {
|
|
347
460
|
const now = Date.now();
|
|
348
461
|
const row = {
|
|
349
462
|
id: uuidv7(),
|
|
@@ -385,14 +498,22 @@ export function createStore(dbPath, options = {}) {
|
|
|
385
498
|
return S.setMsgStatus.run(status, id).changes > 0;
|
|
386
499
|
},
|
|
387
500
|
|
|
388
|
-
getAuditMessagesForAgent(
|
|
501
|
+
getAuditMessagesForAgent(
|
|
502
|
+
agentId,
|
|
503
|
+
{ max_messages = 20, include_topics = null } = {},
|
|
504
|
+
) {
|
|
389
505
|
const limit = clampMaxMessages(max_messages);
|
|
390
|
-
const topics =
|
|
391
|
-
|
|
392
|
-
|
|
506
|
+
const topics =
|
|
507
|
+
Array.isArray(include_topics) && include_topics.length
|
|
508
|
+
? include_topics
|
|
509
|
+
: store.getAgent(agentId)?.topics || [];
|
|
393
510
|
|
|
394
511
|
const rows = topics.length
|
|
395
|
-
? S.recentAgentMessagesWithTopics.all(
|
|
512
|
+
? S.recentAgentMessagesWithTopics.all(
|
|
513
|
+
agentId,
|
|
514
|
+
JSON.stringify(topics),
|
|
515
|
+
limit,
|
|
516
|
+
)
|
|
396
517
|
: S.recentAgentMessages.all(agentId, limit);
|
|
397
518
|
|
|
398
519
|
return rows.map(parseMessageRow);
|
|
@@ -419,7 +540,16 @@ export function createStore(dbPath, options = {}) {
|
|
|
419
540
|
return 0;
|
|
420
541
|
},
|
|
421
542
|
|
|
422
|
-
insertHumanRequest({
|
|
543
|
+
insertHumanRequest({
|
|
544
|
+
requester_agent,
|
|
545
|
+
kind,
|
|
546
|
+
prompt,
|
|
547
|
+
requested_schema = {},
|
|
548
|
+
deadline_ms,
|
|
549
|
+
default_action,
|
|
550
|
+
correlation_id,
|
|
551
|
+
trace_id,
|
|
552
|
+
}) {
|
|
423
553
|
const requestId = uuidv7();
|
|
424
554
|
const now = Date.now();
|
|
425
555
|
const deadlineAt = now + deadline_ms;
|
|
@@ -429,14 +559,18 @@ export function createStore(dbPath, options = {}) {
|
|
|
429
559
|
kind,
|
|
430
560
|
prompt,
|
|
431
561
|
schema_json: JSON.stringify(requested_schema),
|
|
432
|
-
state:
|
|
562
|
+
state: "pending",
|
|
433
563
|
deadline_ms: deadlineAt,
|
|
434
564
|
default_action,
|
|
435
565
|
correlation_id: correlation_id || uuidv7(),
|
|
436
566
|
trace_id: trace_id || uuidv7(),
|
|
437
567
|
response_json: null,
|
|
438
568
|
});
|
|
439
|
-
return {
|
|
569
|
+
return {
|
|
570
|
+
request_id: requestId,
|
|
571
|
+
state: "pending",
|
|
572
|
+
deadline_ms: deadlineAt,
|
|
573
|
+
};
|
|
440
574
|
},
|
|
441
575
|
|
|
442
576
|
getHumanRequest(id) {
|
|
@@ -444,7 +578,10 @@ export function createStore(dbPath, options = {}) {
|
|
|
444
578
|
},
|
|
445
579
|
|
|
446
580
|
updateHumanRequest(id, state, resp = null) {
|
|
447
|
-
return
|
|
581
|
+
return (
|
|
582
|
+
S.updateHR.run(state, resp ? JSON.stringify(resp) : null, id).changes >
|
|
583
|
+
0
|
|
584
|
+
);
|
|
448
585
|
},
|
|
449
586
|
|
|
450
587
|
getPendingHumanRequests() {
|
|
@@ -457,7 +594,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
457
594
|
|
|
458
595
|
moveToDeadLetter(messageId, reason, lastError = null) {
|
|
459
596
|
db.transaction(() => {
|
|
460
|
-
S.setMsgStatus.run(
|
|
597
|
+
S.setMsgStatus.run("dead_letter", messageId);
|
|
461
598
|
S.insertDL.run(messageId, reason, Date.now(), lastError);
|
|
462
599
|
})();
|
|
463
600
|
return true;
|
|
@@ -471,10 +608,10 @@ export function createStore(dbPath, options = {}) {
|
|
|
471
608
|
job_id,
|
|
472
609
|
supervisor_agent,
|
|
473
610
|
worker_agent,
|
|
474
|
-
topic =
|
|
475
|
-
task =
|
|
611
|
+
topic = "assign.job",
|
|
612
|
+
task = "",
|
|
476
613
|
payload = {},
|
|
477
|
-
status =
|
|
614
|
+
status = "queued",
|
|
478
615
|
attempt = 1,
|
|
479
616
|
retry_count = 0,
|
|
480
617
|
max_retries = 0,
|
|
@@ -494,8 +631,8 @@ export function createStore(dbPath, options = {}) {
|
|
|
494
631
|
job_id: job_id || uuidv7(),
|
|
495
632
|
supervisor_agent,
|
|
496
633
|
worker_agent,
|
|
497
|
-
topic: String(topic ||
|
|
498
|
-
task: String(task ||
|
|
634
|
+
topic: String(topic || "assign.job"),
|
|
635
|
+
task: String(task || ""),
|
|
499
636
|
payload_json: JSON.stringify(payload || {}),
|
|
500
637
|
status,
|
|
501
638
|
attempt: Math.max(1, Number(attempt) || 1),
|
|
@@ -514,8 +651,10 @@ export function createStore(dbPath, options = {}) {
|
|
|
514
651
|
error_json: error == null ? null : JSON.stringify(error),
|
|
515
652
|
created_at_ms: now,
|
|
516
653
|
updated_at_ms: now,
|
|
517
|
-
started_at_ms: status ===
|
|
518
|
-
completed_at_ms: [
|
|
654
|
+
started_at_ms: status === "running" ? now : null,
|
|
655
|
+
completed_at_ms: ["succeeded", "failed", "timed_out"].includes(status)
|
|
656
|
+
? now
|
|
657
|
+
: null,
|
|
519
658
|
last_retry_at_ms: retry_count > 0 ? now : null,
|
|
520
659
|
};
|
|
521
660
|
S.insertAssign.run(row);
|
|
@@ -534,8 +673,13 @@ export function createStore(dbPath, options = {}) {
|
|
|
534
673
|
|
|
535
674
|
const now = Date.now();
|
|
536
675
|
const nextStatus = status || current.status;
|
|
537
|
-
const isTerminal = [
|
|
538
|
-
|
|
676
|
+
const isTerminal = ["succeeded", "failed", "timed_out"].includes(
|
|
677
|
+
nextStatus,
|
|
678
|
+
);
|
|
679
|
+
const nextTimeout = clampDuration(
|
|
680
|
+
patch.timeout_ms ?? current.timeout_ms,
|
|
681
|
+
current.timeout_ms,
|
|
682
|
+
);
|
|
539
683
|
const nextRow = {
|
|
540
684
|
job_id: current.job_id,
|
|
541
685
|
supervisor_agent: patch.supervisor_agent ?? current.supervisor_agent,
|
|
@@ -544,39 +688,69 @@ export function createStore(dbPath, options = {}) {
|
|
|
544
688
|
task: patch.task ?? current.task,
|
|
545
689
|
payload_json: JSON.stringify(patch.payload ?? current.payload ?? {}),
|
|
546
690
|
status: nextStatus,
|
|
547
|
-
attempt: Math.max(
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
691
|
+
attempt: Math.max(
|
|
692
|
+
1,
|
|
693
|
+
Number(patch.attempt ?? current.attempt) || current.attempt || 1,
|
|
694
|
+
),
|
|
695
|
+
retry_count: Math.max(
|
|
696
|
+
0,
|
|
697
|
+
Number(patch.retry_count ?? current.retry_count) || 0,
|
|
698
|
+
),
|
|
699
|
+
max_retries: Math.max(
|
|
700
|
+
0,
|
|
701
|
+
Number(patch.max_retries ?? current.max_retries) || 0,
|
|
702
|
+
),
|
|
703
|
+
priority: clampPriority(
|
|
704
|
+
patch.priority ?? current.priority,
|
|
705
|
+
current.priority || 5,
|
|
706
|
+
),
|
|
707
|
+
ttl_ms: clampDuration(
|
|
708
|
+
patch.ttl_ms ?? current.ttl_ms,
|
|
709
|
+
current.ttl_ms || nextTimeout,
|
|
710
|
+
),
|
|
552
711
|
timeout_ms: nextTimeout,
|
|
553
712
|
deadline_ms: (() => {
|
|
554
|
-
if (Object.hasOwn(patch,
|
|
555
|
-
return patch.deadline_ms == null
|
|
713
|
+
if (Object.hasOwn(patch, "deadline_ms")) {
|
|
714
|
+
return patch.deadline_ms == null
|
|
715
|
+
? null
|
|
716
|
+
: Math.trunc(Number(patch.deadline_ms));
|
|
556
717
|
}
|
|
557
718
|
if (isTerminal) return null;
|
|
558
|
-
if (nextStatus ===
|
|
719
|
+
if (nextStatus === "running" && !current.deadline_ms)
|
|
720
|
+
return now + nextTimeout;
|
|
559
721
|
return current.deadline_ms;
|
|
560
722
|
})(),
|
|
561
723
|
trace_id: patch.trace_id ?? current.trace_id,
|
|
562
724
|
correlation_id: patch.correlation_id ?? current.correlation_id,
|
|
563
|
-
last_message_id: Object.hasOwn(patch,
|
|
725
|
+
last_message_id: Object.hasOwn(patch, "last_message_id")
|
|
564
726
|
? patch.last_message_id
|
|
565
727
|
: current.last_message_id,
|
|
566
|
-
result_json: Object.hasOwn(patch,
|
|
567
|
-
?
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
728
|
+
result_json: Object.hasOwn(patch, "result")
|
|
729
|
+
? patch.result == null
|
|
730
|
+
? null
|
|
731
|
+
: JSON.stringify(patch.result)
|
|
732
|
+
: current.result == null
|
|
733
|
+
? null
|
|
734
|
+
: JSON.stringify(current.result),
|
|
735
|
+
error_json: Object.hasOwn(patch, "error")
|
|
736
|
+
? patch.error == null
|
|
737
|
+
? null
|
|
738
|
+
: JSON.stringify(patch.error)
|
|
739
|
+
: current.error == null
|
|
740
|
+
? null
|
|
741
|
+
: JSON.stringify(current.error),
|
|
572
742
|
updated_at_ms: now,
|
|
573
|
-
started_at_ms: Object.hasOwn(patch,
|
|
743
|
+
started_at_ms: Object.hasOwn(patch, "started_at_ms")
|
|
574
744
|
? patch.started_at_ms
|
|
575
|
-
:
|
|
576
|
-
|
|
745
|
+
: nextStatus === "running"
|
|
746
|
+
? current.started_at_ms || now
|
|
747
|
+
: current.started_at_ms,
|
|
748
|
+
completed_at_ms: Object.hasOwn(patch, "completed_at_ms")
|
|
577
749
|
? patch.completed_at_ms
|
|
578
|
-
:
|
|
579
|
-
|
|
750
|
+
: isTerminal
|
|
751
|
+
? current.completed_at_ms || now
|
|
752
|
+
: current.completed_at_ms,
|
|
753
|
+
last_retry_at_ms: Object.hasOwn(patch, "last_retry_at_ms")
|
|
580
754
|
? patch.last_retry_at_ms
|
|
581
755
|
: current.last_retry_at_ms,
|
|
582
756
|
};
|
|
@@ -602,32 +776,35 @@ export function createStore(dbPath, options = {}) {
|
|
|
602
776
|
const values = [];
|
|
603
777
|
|
|
604
778
|
if (supervisor_agent) {
|
|
605
|
-
clauses.push(
|
|
779
|
+
clauses.push("supervisor_agent = ?");
|
|
606
780
|
values.push(supervisor_agent);
|
|
607
781
|
}
|
|
608
782
|
if (worker_agent) {
|
|
609
|
-
clauses.push(
|
|
783
|
+
clauses.push("worker_agent = ?");
|
|
610
784
|
values.push(worker_agent);
|
|
611
785
|
}
|
|
612
786
|
if (trace_id) {
|
|
613
|
-
clauses.push(
|
|
787
|
+
clauses.push("trace_id = ?");
|
|
614
788
|
values.push(trace_id);
|
|
615
789
|
}
|
|
616
790
|
if (correlation_id) {
|
|
617
|
-
clauses.push(
|
|
791
|
+
clauses.push("correlation_id = ?");
|
|
618
792
|
values.push(correlation_id);
|
|
619
793
|
}
|
|
620
794
|
|
|
621
|
-
const statusList =
|
|
622
|
-
|
|
623
|
-
|
|
795
|
+
const statusList =
|
|
796
|
+
Array.isArray(statuses) && statuses.length
|
|
797
|
+
? statuses
|
|
798
|
+
: status
|
|
799
|
+
? [status]
|
|
800
|
+
: [];
|
|
624
801
|
if (statusList.length) {
|
|
625
|
-
clauses.push(`status IN (${statusList.map(() =>
|
|
802
|
+
clauses.push(`status IN (${statusList.map(() => "?").join(",")})`);
|
|
626
803
|
values.push(...statusList);
|
|
627
804
|
}
|
|
628
805
|
|
|
629
806
|
if (Number.isFinite(Number(active_before_ms))) {
|
|
630
|
-
clauses.push(
|
|
807
|
+
clauses.push("deadline_ms IS NOT NULL AND deadline_ms <= ?");
|
|
631
808
|
values.push(Math.trunc(Number(active_before_ms)));
|
|
632
809
|
}
|
|
633
810
|
|
|
@@ -637,21 +814,33 @@ export function createStore(dbPath, options = {}) {
|
|
|
637
814
|
// 이 함수는 hot path(heartbeat/poll)가 아닌 관리/조회 경로에서만 호출되므로 허용한다.
|
|
638
815
|
const sql = `
|
|
639
816
|
SELECT * FROM assign_jobs
|
|
640
|
-
${clauses.length ? `WHERE ${clauses.join(
|
|
817
|
+
${clauses.length ? `WHERE ${clauses.join(" AND ")}` : ""}
|
|
641
818
|
ORDER BY updated_at_ms DESC
|
|
642
819
|
LIMIT ?`;
|
|
643
820
|
values.push(clampMaxMessages(limit, 50));
|
|
644
|
-
return db
|
|
821
|
+
return db
|
|
822
|
+
.prepare(sql)
|
|
823
|
+
.all(...values)
|
|
824
|
+
.map(parseAssignRow);
|
|
645
825
|
},
|
|
646
826
|
|
|
647
827
|
retryAssign(jobId, patch = {}) {
|
|
648
828
|
const current = store.getAssign(jobId);
|
|
649
829
|
if (!current) return null;
|
|
650
830
|
|
|
651
|
-
const nextRetryCount = Math.max(
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
831
|
+
const nextRetryCount = Math.max(
|
|
832
|
+
0,
|
|
833
|
+
Number(patch.retry_count ?? current.retry_count + 1) || 0,
|
|
834
|
+
);
|
|
835
|
+
const nextAttempt = Math.max(
|
|
836
|
+
current.attempt + 1,
|
|
837
|
+
Number(patch.attempt ?? current.attempt + 1) || 1,
|
|
838
|
+
);
|
|
839
|
+
const nextTimeout = clampDuration(
|
|
840
|
+
patch.timeout_ms ?? current.timeout_ms,
|
|
841
|
+
current.timeout_ms,
|
|
842
|
+
);
|
|
843
|
+
return store.updateAssignStatus(jobId, "queued", {
|
|
655
844
|
retry_count: nextRetryCount,
|
|
656
845
|
attempt: nextAttempt,
|
|
657
846
|
timeout_ms: nextTimeout,
|
|
@@ -661,7 +850,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
661
850
|
started_at_ms: null,
|
|
662
851
|
last_retry_at_ms: Date.now(),
|
|
663
852
|
result: patch.result ?? null,
|
|
664
|
-
error: Object.hasOwn(patch,
|
|
853
|
+
error: Object.hasOwn(patch, "error") ? patch.error : current.error,
|
|
665
854
|
last_message_id: null,
|
|
666
855
|
});
|
|
667
856
|
},
|
|
@@ -671,8 +860,8 @@ export function createStore(dbPath, options = {}) {
|
|
|
671
860
|
return db.transaction(() => {
|
|
672
861
|
const expired = S.findExpired.all(now);
|
|
673
862
|
for (const { id } of expired) {
|
|
674
|
-
S.setMsgStatus.run(
|
|
675
|
-
S.insertDL.run(id,
|
|
863
|
+
S.setMsgStatus.run("dead_letter", id);
|
|
864
|
+
S.insertDL.run(id, "ttl_expired", now, null);
|
|
676
865
|
}
|
|
677
866
|
const humanRequests = S.expireHR.run(now).changes;
|
|
678
867
|
return { messages: expired.length, human_requests: humanRequests };
|
|
@@ -688,7 +877,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
688
877
|
},
|
|
689
878
|
|
|
690
879
|
onAssignStatusChange(listener) {
|
|
691
|
-
if (typeof listener !==
|
|
880
|
+
if (typeof listener !== "function") {
|
|
692
881
|
return () => {};
|
|
693
882
|
}
|
|
694
883
|
assignStatusListeners.add(listener);
|
|
@@ -718,17 +907,17 @@ export function createStore(dbPath, options = {}) {
|
|
|
718
907
|
online_agents: S.onlineCount.get().cnt,
|
|
719
908
|
total_messages: S.msgCount.get().cnt,
|
|
720
909
|
dlq: S.dlqDepth.get().cnt,
|
|
721
|
-
assign_queued: S.assignCountByStatus.get(
|
|
722
|
-
assign_running: S.assignCountByStatus.get(
|
|
723
|
-
assign_failed: S.assignCountByStatus.get(
|
|
724
|
-
assign_timed_out: S.assignCountByStatus.get(
|
|
910
|
+
assign_queued: S.assignCountByStatus.get("queued").cnt,
|
|
911
|
+
assign_running: S.assignCountByStatus.get("running").cnt,
|
|
912
|
+
assign_failed: S.assignCountByStatus.get("failed").cnt,
|
|
913
|
+
assign_timed_out: S.assignCountByStatus.get("timed_out").cnt,
|
|
725
914
|
};
|
|
726
915
|
},
|
|
727
916
|
|
|
728
917
|
// --- Reflexion CRUD ---
|
|
729
918
|
|
|
730
919
|
addReflexion({
|
|
731
|
-
type =
|
|
920
|
+
type = "reflexion",
|
|
732
921
|
error_pattern,
|
|
733
922
|
error_message,
|
|
734
923
|
context = {},
|
|
@@ -768,14 +957,16 @@ export function createStore(dbPath, options = {}) {
|
|
|
768
957
|
},
|
|
769
958
|
|
|
770
959
|
findReflexion(errorPattern, context = {}) {
|
|
771
|
-
const ctxKeys = Object.keys(context).filter(k => context[k] != null);
|
|
772
|
-
const ctxWhere = ctxKeys
|
|
773
|
-
|
|
960
|
+
const ctxKeys = Object.keys(context).filter((k) => context[k] != null);
|
|
961
|
+
const ctxWhere = ctxKeys
|
|
962
|
+
.map((k) => ` AND json_extract(context_json, '$.${k}') = ?`)
|
|
963
|
+
.join("");
|
|
964
|
+
const ctxVals = ctxKeys.map((k) => context[k]);
|
|
774
965
|
|
|
775
966
|
if (ctxKeys.length === 0) {
|
|
776
967
|
let rows = S.findReflexionExact.all(errorPattern);
|
|
777
968
|
if (rows.length) return rows.map(parseReflexionRow);
|
|
778
|
-
const escaped = errorPattern.replace(/[%_\\]/g,
|
|
969
|
+
const escaped = errorPattern.replace(/[%_\\]/g, "\\$&");
|
|
779
970
|
rows = S.findReflexionLike.all(`%${escaped.slice(0, 100)}%`);
|
|
780
971
|
return rows.map(parseReflexionRow);
|
|
781
972
|
}
|
|
@@ -784,7 +975,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
784
975
|
let rows = db.prepare(exactSql).all(errorPattern, ...ctxVals);
|
|
785
976
|
if (rows.length) return rows.map(parseReflexionRow);
|
|
786
977
|
|
|
787
|
-
const escaped = errorPattern.replace(/[%_\\]/g,
|
|
978
|
+
const escaped = errorPattern.replace(/[%_\\]/g, "\\$&");
|
|
788
979
|
const likeSql = `SELECT * FROM reflexion_entries WHERE error_pattern LIKE ? ESCAPE '\\'${ctxWhere} ORDER BY confidence DESC LIMIT 10`;
|
|
789
980
|
rows = db.prepare(likeSql).all(`%${escaped.slice(0, 100)}%`, ...ctxVals);
|
|
790
981
|
return rows.map(parseReflexionRow);
|
|
@@ -800,18 +991,30 @@ export function createStore(dbPath, options = {}) {
|
|
|
800
991
|
const entry = store.getReflexion(id);
|
|
801
992
|
if (entry && entry.hit_count > 0) {
|
|
802
993
|
const conf = recalcConfidence(entry);
|
|
803
|
-
S.updateReflexionConfidence.run(
|
|
994
|
+
S.updateReflexionConfidence.run(
|
|
995
|
+
Math.max(0, Math.min(1, conf)),
|
|
996
|
+
now,
|
|
997
|
+
id,
|
|
998
|
+
);
|
|
804
999
|
}
|
|
805
1000
|
return store.getReflexion(id);
|
|
806
1001
|
},
|
|
807
1002
|
|
|
808
1003
|
listReflexion(filters = {}) {
|
|
809
|
-
const {
|
|
810
|
-
|
|
1004
|
+
const {
|
|
1005
|
+
type,
|
|
1006
|
+
minConfidence = Number.NEGATIVE_INFINITY,
|
|
1007
|
+
projectSlug,
|
|
1008
|
+
} = filters;
|
|
1009
|
+
return S.listReflexionEntries
|
|
1010
|
+
.all()
|
|
811
1011
|
.map(parseReflexionRow)
|
|
812
1012
|
.filter((entry) => !type || entry.type === type)
|
|
813
1013
|
.filter((entry) => entry.confidence >= minConfidence)
|
|
814
|
-
.filter(
|
|
1014
|
+
.filter(
|
|
1015
|
+
(entry) =>
|
|
1016
|
+
!projectSlug || entry.adaptive_state?.project_slug === projectSlug,
|
|
1017
|
+
);
|
|
815
1018
|
},
|
|
816
1019
|
|
|
817
1020
|
patchReflexion(id, patch = {}) {
|
|
@@ -825,20 +1028,20 @@ export function createStore(dbPath, options = {}) {
|
|
|
825
1028
|
updated_at_ms: patch.updated_at_ms ?? Date.now(),
|
|
826
1029
|
};
|
|
827
1030
|
const sets = [
|
|
828
|
-
[
|
|
829
|
-
[
|
|
830
|
-
[
|
|
831
|
-
[
|
|
832
|
-
[
|
|
833
|
-
[
|
|
834
|
-
[
|
|
835
|
-
[
|
|
836
|
-
[
|
|
837
|
-
[
|
|
838
|
-
[
|
|
839
|
-
[
|
|
1031
|
+
["type", next.type],
|
|
1032
|
+
["error_pattern", next.error_pattern],
|
|
1033
|
+
["error_message", next.error_message],
|
|
1034
|
+
["context_json", JSON.stringify(next.context ?? {})],
|
|
1035
|
+
["solution", next.solution],
|
|
1036
|
+
["solution_code", next.solution_code ?? null],
|
|
1037
|
+
["adaptive_state_json", JSON.stringify(next.adaptive_state ?? {})],
|
|
1038
|
+
["confidence", next.confidence],
|
|
1039
|
+
["hit_count", next.hit_count],
|
|
1040
|
+
["success_count", next.success_count],
|
|
1041
|
+
["last_hit_ms", next.last_hit_ms],
|
|
1042
|
+
["updated_at_ms", next.updated_at_ms],
|
|
840
1043
|
];
|
|
841
|
-
const sql = `UPDATE reflexion_entries SET ${sets.map(([key]) => `${key} = ?`).join(
|
|
1044
|
+
const sql = `UPDATE reflexion_entries SET ${sets.map(([key]) => `${key} = ?`).join(", ")} WHERE id = ?`;
|
|
842
1045
|
db.prepare(sql).run(...sets.map(([, value]) => value), id);
|
|
843
1046
|
return store.getReflexion(id);
|
|
844
1047
|
},
|