@askexenow/exe-os 0.9.66 → 0.9.67
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/deploy/stack-manifests/v0.9.json +54 -5
- package/dist/bin/age-ontology-load.js +61 -0
- package/dist/bin/agentic-ontology-backfill.js +4708 -0
- package/dist/bin/agentic-reflection-backfill.js +4144 -0
- package/dist/bin/{exe-link.js → agentic-semantic-label.js} +1523 -2275
- package/dist/bin/backfill-conversations.js +506 -20
- package/dist/bin/backfill-responses.js +506 -20
- package/dist/bin/backfill-vectors.js +233 -20
- package/dist/bin/bulk-sync-postgres.js +4876 -0
- package/dist/bin/cleanup-stale-review-tasks.js +507 -21
- package/dist/bin/cli.js +2450 -1530
- package/dist/bin/exe-assign.js +506 -20
- package/dist/bin/exe-boot.js +410 -60
- package/dist/bin/exe-cloud.js +795 -105
- package/dist/bin/exe-dispatch.js +516 -22
- package/dist/bin/exe-doctor.js +587 -30
- package/dist/bin/exe-export-behaviors.js +518 -24
- package/dist/bin/exe-forget.js +507 -21
- package/dist/bin/exe-gateway.js +571 -25
- package/dist/bin/exe-heartbeat.js +518 -24
- package/dist/bin/exe-kill.js +507 -21
- package/dist/bin/exe-launch-agent.js +2312 -1069
- package/dist/bin/exe-new-employee.js +197 -165
- package/dist/bin/exe-pending-messages.js +507 -21
- package/dist/bin/exe-pending-notifications.js +507 -21
- package/dist/bin/exe-pending-reviews.js +507 -21
- package/dist/bin/exe-rename.js +507 -21
- package/dist/bin/exe-review.js +507 -21
- package/dist/bin/exe-search.js +518 -24
- package/dist/bin/exe-session-cleanup.js +516 -22
- package/dist/bin/exe-settings.js +4 -0
- package/dist/bin/exe-start-codex.js +682 -143
- package/dist/bin/exe-start-opencode.js +627 -79
- package/dist/bin/exe-status.js +507 -21
- package/dist/bin/exe-team.js +507 -21
- package/dist/bin/git-sweep.js +516 -22
- package/dist/bin/graph-backfill.js +558 -21
- package/dist/bin/graph-export.js +507 -21
- package/dist/bin/graph-layer-benchmark.js +109 -0
- package/dist/bin/install.js +305 -288
- package/dist/bin/intercom-check.js +516 -22
- package/dist/bin/postgres-agentic-reflection-backfill.js +187 -0
- package/dist/bin/postgres-agentic-semantic-backfill.js +237 -0
- package/dist/bin/scan-tasks.js +516 -22
- package/dist/bin/setup.js +412 -62
- package/dist/bin/shard-migrate.js +506 -20
- package/dist/gateway/index.js +569 -23
- package/dist/hooks/bug-report-worker.js +519 -25
- package/dist/hooks/codex-stop-task-finalizer.js +516 -22
- package/dist/hooks/commit-complete.js +516 -22
- package/dist/hooks/error-recall.js +518 -24
- package/dist/hooks/ingest.js +516 -22
- package/dist/hooks/instructions-loaded.js +507 -21
- package/dist/hooks/notification.js +507 -21
- package/dist/hooks/post-compact.js +507 -21
- package/dist/hooks/post-tool-combined.js +519 -25
- package/dist/hooks/pre-compact.js +516 -22
- package/dist/hooks/pre-tool-use.js +507 -21
- package/dist/hooks/prompt-submit.js +519 -25
- package/dist/hooks/session-end.js +516 -22
- package/dist/hooks/session-start.js +520 -26
- package/dist/hooks/stop.js +517 -23
- package/dist/hooks/subagent-stop.js +507 -21
- package/dist/hooks/summary-worker.js +411 -61
- package/dist/index.js +569 -23
- package/dist/lib/cloud-sync.js +391 -53
- package/dist/lib/config.js +13 -1
- package/dist/lib/consolidation.js +1 -1
- package/dist/lib/database.js +124 -0
- package/dist/lib/db.js +124 -0
- package/dist/lib/device-registry.js +124 -0
- package/dist/lib/embedder.js +13 -1
- package/dist/lib/exe-daemon.js +2184 -561
- package/dist/lib/hybrid-search.js +518 -24
- package/dist/lib/identity.js +3 -0
- package/dist/lib/keychain.js +178 -22
- package/dist/lib/messaging.js +3 -0
- package/dist/lib/reminders.js +3 -0
- package/dist/lib/schedules.js +233 -20
- package/dist/lib/skill-learning.js +16 -1
- package/dist/lib/store.js +506 -20
- package/dist/lib/tasks.js +16 -1
- package/dist/lib/tmux-routing.js +16 -1
- package/dist/lib/token-spend.js +3 -0
- package/dist/mcp/server.js +1757 -428
- package/dist/mcp/tools/complete-reminder.js +3 -0
- package/dist/mcp/tools/create-reminder.js +3 -0
- package/dist/mcp/tools/create-task.js +16 -1
- package/dist/mcp/tools/deactivate-behavior.js +3 -0
- package/dist/mcp/tools/list-reminders.js +3 -0
- package/dist/mcp/tools/list-tasks.js +3 -0
- package/dist/mcp/tools/send-message.js +3 -0
- package/dist/mcp/tools/update-task.js +16 -1
- package/dist/runtime/index.js +516 -22
- package/dist/tui/App.js +594 -29
- package/package.json +8 -5
- package/src/commands/exe/cloud.md +6 -10
- package/stack.release.json +3 -3
- package/src/commands/exe/link.md +0 -18
package/dist/mcp/server.js
CHANGED
|
@@ -192,6 +192,10 @@ async function loadConfig() {
|
|
|
192
192
|
if (config2.dbPath.startsWith("~")) {
|
|
193
193
|
config2.dbPath = config2.dbPath.replace(/^~/, os.homedir());
|
|
194
194
|
}
|
|
195
|
+
const envDbPath = path.join(dir, "memories.db");
|
|
196
|
+
if (process.env.EXE_OS_DIR && config2.dbPath !== envDbPath && !existsSync2(config2.dbPath) && existsSync2(envDbPath)) {
|
|
197
|
+
config2.dbPath = envDbPath;
|
|
198
|
+
}
|
|
195
199
|
return config2;
|
|
196
200
|
} catch {
|
|
197
201
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
@@ -212,7 +216,15 @@ function loadConfigSync() {
|
|
|
212
216
|
normalizeSessionLifecycle(migratedCfg);
|
|
213
217
|
normalizeAutoUpdate(migratedCfg);
|
|
214
218
|
normalizeOrchestration(migratedCfg);
|
|
215
|
-
|
|
219
|
+
const config2 = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
220
|
+
if (config2.dbPath.startsWith("~")) {
|
|
221
|
+
config2.dbPath = config2.dbPath.replace(/^~/, os.homedir());
|
|
222
|
+
}
|
|
223
|
+
const envDbPath = path.join(dir, "memories.db");
|
|
224
|
+
if (process.env.EXE_OS_DIR && config2.dbPath !== envDbPath && !existsSync2(config2.dbPath) && existsSync2(envDbPath)) {
|
|
225
|
+
config2.dbPath = envDbPath;
|
|
226
|
+
}
|
|
227
|
+
return config2;
|
|
216
228
|
} catch {
|
|
217
229
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
218
230
|
}
|
|
@@ -695,15 +707,15 @@ function isDaemonTooYoung() {
|
|
|
695
707
|
}
|
|
696
708
|
}
|
|
697
709
|
async function retryThenRestart(doRequest, label) {
|
|
698
|
-
const
|
|
699
|
-
if (!
|
|
710
|
+
const result2 = await doRequest();
|
|
711
|
+
if (!result2.error) {
|
|
700
712
|
_consecutiveFailures = 0;
|
|
701
|
-
return
|
|
713
|
+
return result2;
|
|
702
714
|
}
|
|
703
715
|
_consecutiveFailures++;
|
|
704
716
|
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
705
717
|
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
706
|
-
process.stderr.write(`[exed-client] ${label} failed (${
|
|
718
|
+
process.stderr.write(`[exed-client] ${label} failed (${result2.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
707
719
|
`);
|
|
708
720
|
await new Promise((r) => setTimeout(r, delayMs));
|
|
709
721
|
if (!_connected) {
|
|
@@ -719,7 +731,7 @@ async function retryThenRestart(doRequest, label) {
|
|
|
719
731
|
if (isDaemonTooYoung()) {
|
|
720
732
|
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
721
733
|
`);
|
|
722
|
-
return { error:
|
|
734
|
+
return { error: result2.error };
|
|
723
735
|
}
|
|
724
736
|
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
725
737
|
`);
|
|
@@ -755,20 +767,20 @@ async function embedViaClient(text3, priority = "high") {
|
|
|
755
767
|
if (!_connected) return null;
|
|
756
768
|
}
|
|
757
769
|
}
|
|
758
|
-
const
|
|
770
|
+
const result2 = await retryThenRestart(
|
|
759
771
|
() => sendRequest([text3], priority),
|
|
760
772
|
"Embed"
|
|
761
773
|
);
|
|
762
|
-
return !
|
|
774
|
+
return !result2.error && result2.vectors?.[0] ? result2.vectors[0] : null;
|
|
763
775
|
}
|
|
764
776
|
async function embedBatchViaClient(texts, priority = "high") {
|
|
765
777
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
766
778
|
_requestCount++;
|
|
767
|
-
const
|
|
779
|
+
const result2 = await retryThenRestart(
|
|
768
780
|
() => sendRequest(texts, priority),
|
|
769
781
|
"Batch embed"
|
|
770
782
|
);
|
|
771
|
-
return !
|
|
783
|
+
return !result2.error && result2.vectors ? result2.vectors : null;
|
|
772
784
|
}
|
|
773
785
|
function disconnectClient() {
|
|
774
786
|
if (_socket) {
|
|
@@ -861,10 +873,10 @@ async function disposeEmbedder() {
|
|
|
861
873
|
async function embedDirect(text3) {
|
|
862
874
|
const llamaCpp = await import("node-llama-cpp");
|
|
863
875
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
864
|
-
const { existsSync:
|
|
865
|
-
const
|
|
866
|
-
const modelPath =
|
|
867
|
-
if (!
|
|
876
|
+
const { existsSync: existsSync39 } = await import("fs");
|
|
877
|
+
const path50 = await import("path");
|
|
878
|
+
const modelPath = path50.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
879
|
+
if (!existsSync39(modelPath)) {
|
|
868
880
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
869
881
|
}
|
|
870
882
|
const llama = await llamaCpp.getLlama();
|
|
@@ -2214,6 +2226,9 @@ function getClient() {
|
|
|
2214
2226
|
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
2215
2227
|
return _daemonClient;
|
|
2216
2228
|
}
|
|
2229
|
+
if (!_resilientClient) {
|
|
2230
|
+
return _adapterClient;
|
|
2231
|
+
}
|
|
2217
2232
|
return _resilientClient;
|
|
2218
2233
|
}
|
|
2219
2234
|
async function initDaemonClient() {
|
|
@@ -3246,6 +3261,127 @@ async function ensureSchema() {
|
|
|
3246
3261
|
VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
|
|
3247
3262
|
END;
|
|
3248
3263
|
`);
|
|
3264
|
+
await client.executeMultiple(`
|
|
3265
|
+
CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
3266
|
+
id TEXT PRIMARY KEY,
|
|
3267
|
+
agent_id TEXT NOT NULL,
|
|
3268
|
+
project_name TEXT,
|
|
3269
|
+
started_at TEXT NOT NULL,
|
|
3270
|
+
last_event_at TEXT NOT NULL,
|
|
3271
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
3272
|
+
properties TEXT DEFAULT '{}'
|
|
3273
|
+
);
|
|
3274
|
+
|
|
3275
|
+
CREATE INDEX IF NOT EXISTS idx_agent_sessions_agent_time
|
|
3276
|
+
ON agent_sessions(agent_id, started_at);
|
|
3277
|
+
|
|
3278
|
+
CREATE TABLE IF NOT EXISTS agent_goals (
|
|
3279
|
+
id TEXT PRIMARY KEY,
|
|
3280
|
+
statement TEXT NOT NULL,
|
|
3281
|
+
owner_agent_id TEXT,
|
|
3282
|
+
project_name TEXT,
|
|
3283
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
3284
|
+
priority INTEGER NOT NULL DEFAULT 5,
|
|
3285
|
+
success_criteria TEXT,
|
|
3286
|
+
parent_goal_id TEXT,
|
|
3287
|
+
due_at TEXT,
|
|
3288
|
+
achieved_at TEXT,
|
|
3289
|
+
supersedes_id TEXT,
|
|
3290
|
+
created_at TEXT NOT NULL,
|
|
3291
|
+
updated_at TEXT NOT NULL,
|
|
3292
|
+
source_memory_id TEXT
|
|
3293
|
+
);
|
|
3294
|
+
|
|
3295
|
+
CREATE INDEX IF NOT EXISTS idx_agent_goals_project_status
|
|
3296
|
+
ON agent_goals(project_name, status, priority);
|
|
3297
|
+
|
|
3298
|
+
CREATE TABLE IF NOT EXISTS agent_events (
|
|
3299
|
+
id TEXT PRIMARY KEY,
|
|
3300
|
+
event_type TEXT NOT NULL,
|
|
3301
|
+
occurred_at TEXT NOT NULL,
|
|
3302
|
+
sequence_index INTEGER NOT NULL,
|
|
3303
|
+
actor_agent_id TEXT,
|
|
3304
|
+
agent_role TEXT,
|
|
3305
|
+
project_name TEXT,
|
|
3306
|
+
session_id TEXT,
|
|
3307
|
+
task_id TEXT,
|
|
3308
|
+
goal_id TEXT,
|
|
3309
|
+
parent_event_id TEXT,
|
|
3310
|
+
intention TEXT,
|
|
3311
|
+
outcome TEXT,
|
|
3312
|
+
evidence_memory_id TEXT,
|
|
3313
|
+
impact TEXT,
|
|
3314
|
+
payload TEXT DEFAULT '{}',
|
|
3315
|
+
created_at TEXT NOT NULL
|
|
3316
|
+
);
|
|
3317
|
+
|
|
3318
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_time
|
|
3319
|
+
ON agent_events(occurred_at, sequence_index);
|
|
3320
|
+
|
|
3321
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_session_seq
|
|
3322
|
+
ON agent_events(session_id, sequence_index);
|
|
3323
|
+
|
|
3324
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_goal_time
|
|
3325
|
+
ON agent_events(goal_id, occurred_at);
|
|
3326
|
+
|
|
3327
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_memory
|
|
3328
|
+
ON agent_events(evidence_memory_id);
|
|
3329
|
+
|
|
3330
|
+
CREATE TABLE IF NOT EXISTS agent_goal_links (
|
|
3331
|
+
id TEXT PRIMARY KEY,
|
|
3332
|
+
goal_id TEXT NOT NULL,
|
|
3333
|
+
link_type TEXT NOT NULL,
|
|
3334
|
+
target_id TEXT NOT NULL,
|
|
3335
|
+
target_type TEXT NOT NULL,
|
|
3336
|
+
created_at TEXT NOT NULL
|
|
3337
|
+
);
|
|
3338
|
+
|
|
3339
|
+
CREATE INDEX IF NOT EXISTS idx_agent_goal_links_goal
|
|
3340
|
+
ON agent_goal_links(goal_id, target_type);
|
|
3341
|
+
|
|
3342
|
+
CREATE TABLE IF NOT EXISTS agent_semantic_labels (
|
|
3343
|
+
id TEXT PRIMARY KEY,
|
|
3344
|
+
source_memory_id TEXT NOT NULL,
|
|
3345
|
+
event_id TEXT,
|
|
3346
|
+
labeler TEXT NOT NULL,
|
|
3347
|
+
schema_version INTEGER NOT NULL DEFAULT 1,
|
|
3348
|
+
confidence REAL NOT NULL DEFAULT 0,
|
|
3349
|
+
labels TEXT NOT NULL,
|
|
3350
|
+
created_at TEXT NOT NULL,
|
|
3351
|
+
updated_at TEXT NOT NULL
|
|
3352
|
+
);
|
|
3353
|
+
|
|
3354
|
+
CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_memory
|
|
3355
|
+
ON agent_semantic_labels(source_memory_id, labeler);
|
|
3356
|
+
|
|
3357
|
+
CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_event
|
|
3358
|
+
ON agent_semantic_labels(event_id);
|
|
3359
|
+
|
|
3360
|
+
CREATE TABLE IF NOT EXISTS agent_reflection_checkpoints (
|
|
3361
|
+
id TEXT PRIMARY KEY,
|
|
3362
|
+
project_name TEXT,
|
|
3363
|
+
session_id TEXT,
|
|
3364
|
+
window_start_at TEXT NOT NULL,
|
|
3365
|
+
window_end_at TEXT NOT NULL,
|
|
3366
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
3367
|
+
goal_count INTEGER NOT NULL DEFAULT 0,
|
|
3368
|
+
success_count INTEGER NOT NULL DEFAULT 0,
|
|
3369
|
+
failure_count INTEGER NOT NULL DEFAULT 0,
|
|
3370
|
+
risk_count INTEGER NOT NULL DEFAULT 0,
|
|
3371
|
+
summary TEXT NOT NULL,
|
|
3372
|
+
learnings TEXT NOT NULL DEFAULT '[]',
|
|
3373
|
+
next_actions TEXT NOT NULL DEFAULT '[]',
|
|
3374
|
+
evidence_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
3375
|
+
confidence REAL NOT NULL DEFAULT 0,
|
|
3376
|
+
created_at TEXT NOT NULL
|
|
3377
|
+
);
|
|
3378
|
+
|
|
3379
|
+
CREATE INDEX IF NOT EXISTS idx_agent_reflection_project_time
|
|
3380
|
+
ON agent_reflection_checkpoints(project_name, window_end_at);
|
|
3381
|
+
|
|
3382
|
+
CREATE INDEX IF NOT EXISTS idx_agent_reflection_session_time
|
|
3383
|
+
ON agent_reflection_checkpoints(session_id, window_end_at);
|
|
3384
|
+
`);
|
|
3249
3385
|
try {
|
|
3250
3386
|
await client.execute({
|
|
3251
3387
|
sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
|
|
@@ -3398,12 +3534,13 @@ var keychain_exports = {};
|
|
|
3398
3534
|
__export(keychain_exports, {
|
|
3399
3535
|
deleteMasterKey: () => deleteMasterKey,
|
|
3400
3536
|
exportMnemonic: () => exportMnemonic,
|
|
3537
|
+
getKeyStorageInfo: () => getKeyStorageInfo,
|
|
3401
3538
|
getMasterKey: () => getMasterKey,
|
|
3402
3539
|
importMnemonic: () => importMnemonic,
|
|
3403
3540
|
setMasterKey: () => setMasterKey
|
|
3404
3541
|
});
|
|
3405
3542
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3406
|
-
import { existsSync as existsSync7 } from "fs";
|
|
3543
|
+
import { existsSync as existsSync7, statSync as statSync2 } from "fs";
|
|
3407
3544
|
import { execSync as execSync2 } from "child_process";
|
|
3408
3545
|
import path7 from "path";
|
|
3409
3546
|
import os5 from "os";
|
|
@@ -3413,29 +3550,65 @@ function getKeyDir() {
|
|
|
3413
3550
|
function getKeyPath() {
|
|
3414
3551
|
return path7.join(getKeyDir(), "master.key");
|
|
3415
3552
|
}
|
|
3416
|
-
function
|
|
3553
|
+
function nativeKeychainAllowed() {
|
|
3554
|
+
return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
|
|
3555
|
+
}
|
|
3556
|
+
function linuxSecretAvailable() {
|
|
3557
|
+
if (!nativeKeychainAllowed()) return false;
|
|
3558
|
+
if (process.platform !== "linux") return false;
|
|
3559
|
+
if (linuxSecretAvailability !== null) return linuxSecretAvailability;
|
|
3560
|
+
try {
|
|
3561
|
+
execSync2("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
|
|
3562
|
+
} catch {
|
|
3563
|
+
linuxSecretAvailability = false;
|
|
3564
|
+
return false;
|
|
3565
|
+
}
|
|
3566
|
+
try {
|
|
3567
|
+
execSync2("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
|
|
3568
|
+
linuxSecretAvailability = true;
|
|
3569
|
+
} catch {
|
|
3570
|
+
linuxSecretAvailability = false;
|
|
3571
|
+
}
|
|
3572
|
+
return linuxSecretAvailability;
|
|
3573
|
+
}
|
|
3574
|
+
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3575
|
+
if (process.platform !== "linux") return false;
|
|
3576
|
+
try {
|
|
3577
|
+
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3578
|
+
const st = statSync2(keyPath);
|
|
3579
|
+
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
3580
|
+
if (uid === 0) return true;
|
|
3581
|
+
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3582
|
+
return Boolean(exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep));
|
|
3583
|
+
} catch {
|
|
3584
|
+
return false;
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
function macKeychainGet(service = SERVICE) {
|
|
3588
|
+
if (!nativeKeychainAllowed()) return null;
|
|
3417
3589
|
if (process.platform !== "darwin") return null;
|
|
3418
3590
|
try {
|
|
3419
3591
|
return execSync2(
|
|
3420
|
-
`security find-generic-password -s "${
|
|
3592
|
+
`security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
|
|
3421
3593
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
3422
3594
|
).trim();
|
|
3423
3595
|
} catch {
|
|
3424
3596
|
return null;
|
|
3425
3597
|
}
|
|
3426
3598
|
}
|
|
3427
|
-
function macKeychainSet(value) {
|
|
3599
|
+
function macKeychainSet(value, service = SERVICE) {
|
|
3600
|
+
if (!nativeKeychainAllowed()) return false;
|
|
3428
3601
|
if (process.platform !== "darwin") return false;
|
|
3429
3602
|
try {
|
|
3430
3603
|
try {
|
|
3431
3604
|
execSync2(
|
|
3432
|
-
`security delete-generic-password -s "${
|
|
3605
|
+
`security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3433
3606
|
{ timeout: 5e3 }
|
|
3434
3607
|
);
|
|
3435
3608
|
} catch {
|
|
3436
3609
|
}
|
|
3437
3610
|
execSync2(
|
|
3438
|
-
`security add-generic-password -s "${
|
|
3611
|
+
`security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
|
|
3439
3612
|
{ timeout: 5e3 }
|
|
3440
3613
|
);
|
|
3441
3614
|
return true;
|
|
@@ -3443,11 +3616,12 @@ function macKeychainSet(value) {
|
|
|
3443
3616
|
return false;
|
|
3444
3617
|
}
|
|
3445
3618
|
}
|
|
3446
|
-
function macKeychainDelete() {
|
|
3619
|
+
function macKeychainDelete(service = SERVICE) {
|
|
3620
|
+
if (!nativeKeychainAllowed()) return false;
|
|
3447
3621
|
if (process.platform !== "darwin") return false;
|
|
3448
3622
|
try {
|
|
3449
3623
|
execSync2(
|
|
3450
|
-
`security delete-generic-password -s "${
|
|
3624
|
+
`security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3451
3625
|
{ timeout: 5e3 }
|
|
3452
3626
|
);
|
|
3453
3627
|
return true;
|
|
@@ -3455,22 +3629,22 @@ function macKeychainDelete() {
|
|
|
3455
3629
|
return false;
|
|
3456
3630
|
}
|
|
3457
3631
|
}
|
|
3458
|
-
function linuxSecretGet() {
|
|
3459
|
-
if (
|
|
3632
|
+
function linuxSecretGet(service = SERVICE) {
|
|
3633
|
+
if (!linuxSecretAvailable()) return null;
|
|
3460
3634
|
try {
|
|
3461
3635
|
return execSync2(
|
|
3462
|
-
`secret-tool lookup service "${
|
|
3636
|
+
`secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3463
3637
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
3464
3638
|
).trim();
|
|
3465
3639
|
} catch {
|
|
3466
3640
|
return null;
|
|
3467
3641
|
}
|
|
3468
3642
|
}
|
|
3469
|
-
function linuxSecretSet(value) {
|
|
3470
|
-
if (
|
|
3643
|
+
function linuxSecretSet(value, service = SERVICE) {
|
|
3644
|
+
if (!linuxSecretAvailable()) return false;
|
|
3471
3645
|
try {
|
|
3472
3646
|
execSync2(
|
|
3473
|
-
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${
|
|
3647
|
+
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3474
3648
|
{ timeout: 5e3 }
|
|
3475
3649
|
);
|
|
3476
3650
|
return true;
|
|
@@ -3478,11 +3652,12 @@ function linuxSecretSet(value) {
|
|
|
3478
3652
|
return false;
|
|
3479
3653
|
}
|
|
3480
3654
|
}
|
|
3481
|
-
function linuxSecretDelete() {
|
|
3655
|
+
function linuxSecretDelete(service = SERVICE) {
|
|
3656
|
+
if (!nativeKeychainAllowed()) return false;
|
|
3482
3657
|
if (process.platform !== "linux") return false;
|
|
3483
3658
|
try {
|
|
3484
3659
|
execSync2(
|
|
3485
|
-
`secret-tool clear service "${
|
|
3660
|
+
`secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3486
3661
|
{ timeout: 5e3 }
|
|
3487
3662
|
);
|
|
3488
3663
|
return true;
|
|
@@ -3491,6 +3666,7 @@ function linuxSecretDelete() {
|
|
|
3491
3666
|
}
|
|
3492
3667
|
}
|
|
3493
3668
|
async function tryKeytar() {
|
|
3669
|
+
if (!nativeKeychainAllowed()) return null;
|
|
3494
3670
|
try {
|
|
3495
3671
|
return await import("keytar");
|
|
3496
3672
|
} catch {
|
|
@@ -3499,7 +3675,7 @@ async function tryKeytar() {
|
|
|
3499
3675
|
}
|
|
3500
3676
|
function deriveMachineKey() {
|
|
3501
3677
|
try {
|
|
3502
|
-
const
|
|
3678
|
+
const crypto20 = __require("crypto");
|
|
3503
3679
|
const material = [
|
|
3504
3680
|
os5.hostname(),
|
|
3505
3681
|
os5.userInfo().username,
|
|
@@ -3508,23 +3684,23 @@ function deriveMachineKey() {
|
|
|
3508
3684
|
// Machine ID on Linux (stable across reboots)
|
|
3509
3685
|
process.platform === "linux" ? readMachineId() : ""
|
|
3510
3686
|
].join("|");
|
|
3511
|
-
return
|
|
3687
|
+
return crypto20.createHash("sha256").update(material).digest();
|
|
3512
3688
|
} catch {
|
|
3513
3689
|
return null;
|
|
3514
3690
|
}
|
|
3515
3691
|
}
|
|
3516
3692
|
function readMachineId() {
|
|
3517
3693
|
try {
|
|
3518
|
-
const { readFileSync:
|
|
3519
|
-
return
|
|
3694
|
+
const { readFileSync: readFileSync32 } = __require("fs");
|
|
3695
|
+
return readFileSync32("/etc/machine-id", "utf-8").trim();
|
|
3520
3696
|
} catch {
|
|
3521
3697
|
return "";
|
|
3522
3698
|
}
|
|
3523
3699
|
}
|
|
3524
3700
|
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3525
|
-
const
|
|
3526
|
-
const iv =
|
|
3527
|
-
const cipher =
|
|
3701
|
+
const crypto20 = __require("crypto");
|
|
3702
|
+
const iv = crypto20.randomBytes(12);
|
|
3703
|
+
const cipher = crypto20.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3528
3704
|
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3529
3705
|
encrypted += cipher.final("base64");
|
|
3530
3706
|
const authTag = cipher.getAuthTag().toString("base64");
|
|
@@ -3533,13 +3709,13 @@ function encryptWithMachineKey(plaintext, machineKey) {
|
|
|
3533
3709
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3534
3710
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3535
3711
|
try {
|
|
3536
|
-
const
|
|
3712
|
+
const crypto20 = __require("crypto");
|
|
3537
3713
|
const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
|
|
3538
3714
|
if (parts.length !== 3) return null;
|
|
3539
3715
|
const [ivB64, tagB64, cipherB64] = parts;
|
|
3540
3716
|
const iv = Buffer.from(ivB64, "base64");
|
|
3541
3717
|
const authTag = Buffer.from(tagB64, "base64");
|
|
3542
|
-
const decipher =
|
|
3718
|
+
const decipher = crypto20.createDecipheriv("aes-256-gcm", machineKey, iv);
|
|
3543
3719
|
decipher.setAuthTag(authTag);
|
|
3544
3720
|
let decrypted = decipher.update(cipherB64, "base64", "utf-8");
|
|
3545
3721
|
decrypted += decipher.final("utf-8");
|
|
@@ -3564,7 +3740,19 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
3564
3740
|
return "plaintext";
|
|
3565
3741
|
}
|
|
3566
3742
|
async function getMasterKey() {
|
|
3567
|
-
|
|
3743
|
+
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3744
|
+
if (!nativeValue) {
|
|
3745
|
+
const legacyValue = macKeychainGet(LEGACY_SERVICE) ?? linuxSecretGet(LEGACY_SERVICE);
|
|
3746
|
+
if (legacyValue) {
|
|
3747
|
+
const migrated = macKeychainSet(legacyValue) || linuxSecretSet(legacyValue);
|
|
3748
|
+
if (migrated) {
|
|
3749
|
+
macKeychainDelete(LEGACY_SERVICE);
|
|
3750
|
+
linuxSecretDelete(LEGACY_SERVICE);
|
|
3751
|
+
process.stderr.write("[keychain] Migrated keychain service from exe-mem to exe-os.\n");
|
|
3752
|
+
}
|
|
3753
|
+
nativeValue = legacyValue;
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3568
3756
|
if (nativeValue) {
|
|
3569
3757
|
return Buffer.from(nativeValue, "base64");
|
|
3570
3758
|
}
|
|
@@ -3572,12 +3760,17 @@ async function getMasterKey() {
|
|
|
3572
3760
|
if (keytar) {
|
|
3573
3761
|
try {
|
|
3574
3762
|
const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
3575
|
-
|
|
3576
|
-
|
|
3763
|
+
const legacyKeytarValue = keytarValue ?? await keytar.getPassword(LEGACY_SERVICE, ACCOUNT);
|
|
3764
|
+
if (legacyKeytarValue) {
|
|
3765
|
+
const migrated = macKeychainSet(legacyKeytarValue) || linuxSecretSet(legacyKeytarValue);
|
|
3577
3766
|
if (migrated) {
|
|
3578
3767
|
process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
|
|
3768
|
+
try {
|
|
3769
|
+
await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
|
|
3770
|
+
} catch {
|
|
3771
|
+
}
|
|
3579
3772
|
}
|
|
3580
|
-
return Buffer.from(
|
|
3773
|
+
return Buffer.from(legacyKeytarValue, "base64");
|
|
3581
3774
|
}
|
|
3582
3775
|
} catch {
|
|
3583
3776
|
}
|
|
@@ -3602,7 +3795,7 @@ async function getMasterKey() {
|
|
|
3602
3795
|
const decrypted = decryptWithMachineKey(content, machineKey);
|
|
3603
3796
|
if (!decrypted) {
|
|
3604
3797
|
process.stderr.write(
|
|
3605
|
-
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os
|
|
3798
|
+
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase during setup: exe-os setup\n"
|
|
3606
3799
|
);
|
|
3607
3800
|
return null;
|
|
3608
3801
|
}
|
|
@@ -3611,6 +3804,9 @@ async function getMasterKey() {
|
|
|
3611
3804
|
b64Value = content;
|
|
3612
3805
|
}
|
|
3613
3806
|
const key = Buffer.from(b64Value, "base64");
|
|
3807
|
+
if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
3808
|
+
return key;
|
|
3809
|
+
}
|
|
3614
3810
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3615
3811
|
if (migrated) {
|
|
3616
3812
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
@@ -3638,6 +3834,97 @@ async function getMasterKey() {
|
|
|
3638
3834
|
return null;
|
|
3639
3835
|
}
|
|
3640
3836
|
}
|
|
3837
|
+
async function getKeyStorageInfo() {
|
|
3838
|
+
if (macKeychainGet()) {
|
|
3839
|
+
return {
|
|
3840
|
+
kind: "macos-keychain",
|
|
3841
|
+
secure: true,
|
|
3842
|
+
note: "stored in macOS Keychain via built-in security CLI"
|
|
3843
|
+
};
|
|
3844
|
+
}
|
|
3845
|
+
if (macKeychainGet(LEGACY_SERVICE)) {
|
|
3846
|
+
return {
|
|
3847
|
+
kind: "macos-keychain",
|
|
3848
|
+
secure: true,
|
|
3849
|
+
note: "stored in legacy macOS Keychain service exe-mem; next key read migrates it to exe-os"
|
|
3850
|
+
};
|
|
3851
|
+
}
|
|
3852
|
+
if (linuxSecretGet()) {
|
|
3853
|
+
return {
|
|
3854
|
+
kind: "linux-secret-service",
|
|
3855
|
+
secure: true,
|
|
3856
|
+
note: "stored in Linux Secret Service via secret-tool"
|
|
3857
|
+
};
|
|
3858
|
+
}
|
|
3859
|
+
if (linuxSecretGet(LEGACY_SERVICE)) {
|
|
3860
|
+
return {
|
|
3861
|
+
kind: "linux-secret-service",
|
|
3862
|
+
secure: true,
|
|
3863
|
+
note: "stored in legacy Linux Secret Service service exe-mem; next key read migrates it to exe-os"
|
|
3864
|
+
};
|
|
3865
|
+
}
|
|
3866
|
+
const keytar = await tryKeytar();
|
|
3867
|
+
if (keytar) {
|
|
3868
|
+
try {
|
|
3869
|
+
if (await keytar.getPassword(SERVICE, ACCOUNT)) {
|
|
3870
|
+
return {
|
|
3871
|
+
kind: "legacy-keytar",
|
|
3872
|
+
secure: true,
|
|
3873
|
+
note: "stored in legacy keytar backend; will migrate to native keychain when possible"
|
|
3874
|
+
};
|
|
3875
|
+
}
|
|
3876
|
+
if (await keytar.getPassword(LEGACY_SERVICE, ACCOUNT)) {
|
|
3877
|
+
return {
|
|
3878
|
+
kind: "legacy-keytar",
|
|
3879
|
+
secure: true,
|
|
3880
|
+
note: "stored in legacy keytar service exe-mem; will migrate to native exe-os keychain when possible"
|
|
3881
|
+
};
|
|
3882
|
+
}
|
|
3883
|
+
} catch {
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
const keyPath = getKeyPath();
|
|
3887
|
+
if (!existsSync7(keyPath)) {
|
|
3888
|
+
return {
|
|
3889
|
+
kind: "missing",
|
|
3890
|
+
secure: false,
|
|
3891
|
+
path: keyPath,
|
|
3892
|
+
note: "no key found in OS keychain, legacy keytar, or file fallback"
|
|
3893
|
+
};
|
|
3894
|
+
}
|
|
3895
|
+
try {
|
|
3896
|
+
const content = (await readFile3(keyPath, "utf-8")).trim();
|
|
3897
|
+
if (content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3898
|
+
return {
|
|
3899
|
+
kind: "encrypted-file",
|
|
3900
|
+
secure: true,
|
|
3901
|
+
path: keyPath,
|
|
3902
|
+
note: "stored in machine-bound encrypted file fallback"
|
|
3903
|
+
};
|
|
3904
|
+
}
|
|
3905
|
+
if (isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
3906
|
+
return {
|
|
3907
|
+
kind: "server-secret-file",
|
|
3908
|
+
secure: true,
|
|
3909
|
+
path: keyPath,
|
|
3910
|
+
note: "stored as root-only trusted server secret file"
|
|
3911
|
+
};
|
|
3912
|
+
}
|
|
3913
|
+
return {
|
|
3914
|
+
kind: "plaintext-file",
|
|
3915
|
+
secure: false,
|
|
3916
|
+
path: keyPath,
|
|
3917
|
+
note: "stored in legacy plaintext file; reading it will migrate or encrypt it"
|
|
3918
|
+
};
|
|
3919
|
+
} catch {
|
|
3920
|
+
return {
|
|
3921
|
+
kind: "missing",
|
|
3922
|
+
secure: false,
|
|
3923
|
+
path: keyPath,
|
|
3924
|
+
note: "key file exists but could not be read"
|
|
3925
|
+
};
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3641
3928
|
async function setMasterKey(key) {
|
|
3642
3929
|
const b64 = key.toString("base64");
|
|
3643
3930
|
if (macKeychainSet(b64) || linuxSecretSet(b64)) {
|
|
@@ -3663,10 +3950,13 @@ async function setMasterKey(key) {
|
|
|
3663
3950
|
async function deleteMasterKey() {
|
|
3664
3951
|
macKeychainDelete();
|
|
3665
3952
|
linuxSecretDelete();
|
|
3953
|
+
macKeychainDelete(LEGACY_SERVICE);
|
|
3954
|
+
linuxSecretDelete(LEGACY_SERVICE);
|
|
3666
3955
|
const keytar = await tryKeytar();
|
|
3667
3956
|
if (keytar) {
|
|
3668
3957
|
try {
|
|
3669
3958
|
await keytar.deletePassword(SERVICE, ACCOUNT);
|
|
3959
|
+
await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
|
|
3670
3960
|
} catch {
|
|
3671
3961
|
}
|
|
3672
3962
|
}
|
|
@@ -3704,12 +3994,14 @@ async function importMnemonic(mnemonic) {
|
|
|
3704
3994
|
const entropy = mnemonicToEntropy(trimmed);
|
|
3705
3995
|
return Buffer.from(entropy, "hex");
|
|
3706
3996
|
}
|
|
3707
|
-
var SERVICE, ACCOUNT, ENCRYPTED_PREFIX;
|
|
3997
|
+
var SERVICE, LEGACY_SERVICE, ACCOUNT, linuxSecretAvailability, ENCRYPTED_PREFIX;
|
|
3708
3998
|
var init_keychain = __esm({
|
|
3709
3999
|
"src/lib/keychain.ts"() {
|
|
3710
4000
|
"use strict";
|
|
3711
|
-
SERVICE = "exe-
|
|
4001
|
+
SERVICE = "exe-os";
|
|
4002
|
+
LEGACY_SERVICE = "exe-mem";
|
|
3712
4003
|
ACCOUNT = "master-key";
|
|
4004
|
+
linuxSecretAvailability = null;
|
|
3713
4005
|
ENCRYPTED_PREFIX = "enc:";
|
|
3714
4006
|
}
|
|
3715
4007
|
});
|
|
@@ -3859,8 +4151,8 @@ async function findScopedDuplicate(input) {
|
|
|
3859
4151
|
args.push(input.excludeId);
|
|
3860
4152
|
}
|
|
3861
4153
|
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3862
|
-
const
|
|
3863
|
-
return
|
|
4154
|
+
const result2 = await client.execute({ sql, args });
|
|
4155
|
+
return result2.rows[0]?.id ? String(result2.rows[0].id) : null;
|
|
3864
4156
|
}
|
|
3865
4157
|
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3866
4158
|
try {
|
|
@@ -3979,7 +4271,7 @@ __export(shard_manager_exports, {
|
|
|
3979
4271
|
shardExists: () => shardExists
|
|
3980
4272
|
});
|
|
3981
4273
|
import path8 from "path";
|
|
3982
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync3, statSync as
|
|
4274
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync3, statSync as statSync3 } from "fs";
|
|
3983
4275
|
import { createClient as createClient2 } from "@libsql/client";
|
|
3984
4276
|
function initShardManager(encryptionKey) {
|
|
3985
4277
|
_encryptionKey = encryptionKey;
|
|
@@ -4043,7 +4335,7 @@ async function auditShardHealth(options = {}) {
|
|
|
4043
4335
|
const shards = [];
|
|
4044
4336
|
for (const name of names) {
|
|
4045
4337
|
const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
|
|
4046
|
-
const stat =
|
|
4338
|
+
const stat = statSync3(dbPath);
|
|
4047
4339
|
const item = {
|
|
4048
4340
|
name,
|
|
4049
4341
|
path: dbPath,
|
|
@@ -4296,7 +4588,7 @@ async function getReadyShardClient(projectName) {
|
|
|
4296
4588
|
_shardLastAccess.delete(safeName);
|
|
4297
4589
|
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
4298
4590
|
if (existsSync8(dbPath)) {
|
|
4299
|
-
const stat =
|
|
4591
|
+
const stat = statSync3(dbPath);
|
|
4300
4592
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4301
4593
|
const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
|
|
4302
4594
|
renameSync3(dbPath, archivedPath);
|
|
@@ -4585,11 +4877,11 @@ __export(global_procedures_exports, {
|
|
|
4585
4877
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4586
4878
|
async function loadGlobalProcedures() {
|
|
4587
4879
|
const client = getClient();
|
|
4588
|
-
const
|
|
4880
|
+
const result2 = await client.execute({
|
|
4589
4881
|
sql: "SELECT * FROM company_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
|
|
4590
4882
|
args: []
|
|
4591
4883
|
});
|
|
4592
|
-
const allRows =
|
|
4884
|
+
const allRows = result2.rows;
|
|
4593
4885
|
const customerOnly = allRows.filter((p) => !PLATFORM_PROCEDURE_TITLES.has(p.title));
|
|
4594
4886
|
if (customerOnly.length > 0) {
|
|
4595
4887
|
_customerCache = customerOnly.map((p) => `### ${p.title}
|
|
@@ -4625,12 +4917,12 @@ async function storeGlobalProcedure(input) {
|
|
|
4625
4917
|
async function deactivateGlobalProcedure(id) {
|
|
4626
4918
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4627
4919
|
const client = getClient();
|
|
4628
|
-
const
|
|
4920
|
+
const result2 = await client.execute({
|
|
4629
4921
|
sql: "UPDATE company_procedures SET active = 0, updated_at = ? WHERE id = ?",
|
|
4630
4922
|
args: [now, id]
|
|
4631
4923
|
});
|
|
4632
4924
|
await loadGlobalProcedures();
|
|
4633
|
-
return
|
|
4925
|
+
return result2.rowsAffected > 0;
|
|
4634
4926
|
}
|
|
4635
4927
|
var _customerCache, _cacheLoaded, _platformCache;
|
|
4636
4928
|
var init_global_procedures = __esm({
|
|
@@ -4774,8 +5066,8 @@ async function searchMemoryCards(queryText, agentId, options) {
|
|
|
4774
5066
|
}
|
|
4775
5067
|
sql += ` ORDER BY rank LIMIT ?`;
|
|
4776
5068
|
args.push(limit);
|
|
4777
|
-
const
|
|
4778
|
-
return
|
|
5069
|
+
const result2 = await getClient().execute({ sql, args });
|
|
5070
|
+
return result2.rows.map((row) => ({
|
|
4779
5071
|
id: `card:${String(row.id)}`,
|
|
4780
5072
|
agent_id: String(row.agent_id),
|
|
4781
5073
|
agent_role: "memory_card",
|
|
@@ -4803,6 +5095,274 @@ var init_memory_cards = __esm({
|
|
|
4803
5095
|
}
|
|
4804
5096
|
});
|
|
4805
5097
|
|
|
5098
|
+
// src/lib/agentic-ontology.ts
|
|
5099
|
+
var agentic_ontology_exports = {};
|
|
5100
|
+
__export(agentic_ontology_exports, {
|
|
5101
|
+
clean: () => clean,
|
|
5102
|
+
extractGoalCandidates: () => extractGoalCandidates,
|
|
5103
|
+
inferIntention: () => inferIntention,
|
|
5104
|
+
inferOntologyEventType: () => inferOntologyEventType,
|
|
5105
|
+
inferOutcome: () => inferOutcome,
|
|
5106
|
+
inferSemanticLabel: () => inferSemanticLabel,
|
|
5107
|
+
insertOntologyForBatch: () => insertOntologyForBatch,
|
|
5108
|
+
insertOntologyForMemory: () => insertOntologyForMemory,
|
|
5109
|
+
ontologyPayload: () => ontologyPayload,
|
|
5110
|
+
stableId: () => stableId2
|
|
5111
|
+
});
|
|
5112
|
+
import { createHash as createHash3 } from "crypto";
|
|
5113
|
+
function stableId2(...parts) {
|
|
5114
|
+
return createHash3("sha256").update(parts.map((p) => String(p ?? "")).join("::")).digest("hex").slice(0, 32);
|
|
5115
|
+
}
|
|
5116
|
+
function clean(text3, max = 240) {
|
|
5117
|
+
return text3.replace(/\u0000/g, "").replace(/```[\s\S]*?```/g, " ").replace(/\s+/g, " ").trim().slice(0, max);
|
|
5118
|
+
}
|
|
5119
|
+
function inferOntologyEventType(row) {
|
|
5120
|
+
const lower = row.raw_text.toLowerCase();
|
|
5121
|
+
if (row.has_error) return "error";
|
|
5122
|
+
if (/\b(done|complete|completed|fixed|resolved|shipped|deployed|pushed|published)\b/.test(lower)) return "milestone";
|
|
5123
|
+
if (/\b(blocked|failed|error|bug|regression|broken)\b/.test(lower)) return "problem";
|
|
5124
|
+
if (/\b(decided|decision|adr|we chose|approved|rejected)\b/.test(lower)) return "decision";
|
|
5125
|
+
if (/\b(goal|need to|we need|want to|trying to|objective)\b/.test(lower)) return "goal_signal";
|
|
5126
|
+
if (["Bash", "Read", "Edit", "Write", "Grep", "Glob"].includes(row.tool_name)) return "tool_action";
|
|
5127
|
+
if (row.tool_name.startsWith("memory_card")) return "memory_card";
|
|
5128
|
+
return "memory_observation";
|
|
5129
|
+
}
|
|
5130
|
+
function inferIntention(row) {
|
|
5131
|
+
if (row.intent) return clean(row.intent, 220);
|
|
5132
|
+
const text3 = clean(row.raw_text, 1e3);
|
|
5133
|
+
const patterns = [
|
|
5134
|
+
/(?:we need to|need to|let'?s|i want to|we should|goal is to|objective is to|trying to)\s+([^.!?\n]{8,220})/i,
|
|
5135
|
+
/(?:so that|in order to)\s+([^.!?\n]{8,220})/i,
|
|
5136
|
+
/(?:task|plan):\s*([^.!?\n]{8,220})/i
|
|
5137
|
+
];
|
|
5138
|
+
for (const p of patterns) {
|
|
5139
|
+
const m = text3.match(p);
|
|
5140
|
+
if (m?.[1]) return clean(m[1], 220);
|
|
5141
|
+
}
|
|
5142
|
+
if (["Bash", "Read", "Edit", "Write", "Grep", "Glob"].includes(row.tool_name)) {
|
|
5143
|
+
return `${row.tool_name} during ${row.project_name}`;
|
|
5144
|
+
}
|
|
5145
|
+
return null;
|
|
5146
|
+
}
|
|
5147
|
+
function inferOutcome(row) {
|
|
5148
|
+
if (row.outcome) return clean(row.outcome, 220);
|
|
5149
|
+
if (row.has_error) return "error";
|
|
5150
|
+
const lower = row.raw_text.toLowerCase();
|
|
5151
|
+
if (/\b(done|complete|completed|fixed|resolved|shipped|deployed|pushed|published|passed)\b/.test(lower)) return "success_signal";
|
|
5152
|
+
if (/\b(blocked|failed|error|regression|broken|not working|could not)\b/.test(lower)) return "failure_signal";
|
|
5153
|
+
if (/\b(warning|risk|concern|caveat)\b/.test(lower)) return "risk_signal";
|
|
5154
|
+
return null;
|
|
5155
|
+
}
|
|
5156
|
+
function extractGoalCandidates(row) {
|
|
5157
|
+
const text3 = clean(row.raw_text, 1600);
|
|
5158
|
+
const patterns = [
|
|
5159
|
+
/(?:we need to|need to|i want to|we should|goal is to|objective is to|trying to|let'?s)\s+([^.!?\n]{12,220})/gi,
|
|
5160
|
+
/(?:success means|success criteria|so that)\s+([^.!?\n]{12,220})/gi
|
|
5161
|
+
];
|
|
5162
|
+
const out = [];
|
|
5163
|
+
for (const pattern of patterns) {
|
|
5164
|
+
for (const m of text3.matchAll(pattern)) {
|
|
5165
|
+
const candidate = clean(m[1] ?? "", 220);
|
|
5166
|
+
if (candidate.length >= 12 && !out.some((x) => x.toLowerCase() === candidate.toLowerCase())) out.push(candidate);
|
|
5167
|
+
if (out.length >= 3) return out;
|
|
5168
|
+
}
|
|
5169
|
+
}
|
|
5170
|
+
return out;
|
|
5171
|
+
}
|
|
5172
|
+
function uniq(values, max = 6) {
|
|
5173
|
+
const out = [];
|
|
5174
|
+
for (const value of values.map((v) => clean(v, 220)).filter(Boolean)) {
|
|
5175
|
+
if (!out.some((x) => x.toLowerCase() === value.toLowerCase())) out.push(value);
|
|
5176
|
+
if (out.length >= max) break;
|
|
5177
|
+
}
|
|
5178
|
+
return out;
|
|
5179
|
+
}
|
|
5180
|
+
function extractMatches(text3, patterns, max = 5) {
|
|
5181
|
+
const out = [];
|
|
5182
|
+
for (const pattern of patterns) {
|
|
5183
|
+
for (const match of text3.matchAll(pattern)) {
|
|
5184
|
+
const value = match[1] ?? match[0];
|
|
5185
|
+
if (value) out.push(value);
|
|
5186
|
+
if (out.length >= max) return uniq(out, max);
|
|
5187
|
+
}
|
|
5188
|
+
}
|
|
5189
|
+
return uniq(out, max);
|
|
5190
|
+
}
|
|
5191
|
+
function inferSemanticLabel(row) {
|
|
5192
|
+
const text3 = clean(row.raw_text, 2400);
|
|
5193
|
+
const eventType = inferOntologyEventType(row);
|
|
5194
|
+
const intention = inferIntention(row);
|
|
5195
|
+
const outcome = inferOutcome(row);
|
|
5196
|
+
const goals = extractGoalCandidates(row);
|
|
5197
|
+
const milestones = extractMatches(text3, [
|
|
5198
|
+
/\b(?:completed|finished|fixed|resolved|shipped|deployed|published|pushed|passed)\b([^.!?\n]{0,180})/gi,
|
|
5199
|
+
/(?:milestone|done):\s*([^.!?\n]{8,220})/gi
|
|
5200
|
+
]);
|
|
5201
|
+
const problems = extractMatches(text3, [
|
|
5202
|
+
/\b(?:blocked by|failed because|bug|regression|broken|not working|error)\b([^.!?\n]{0,180})/gi,
|
|
5203
|
+
/(?:problem|issue|risk):\s*([^.!?\n]{8,220})/gi
|
|
5204
|
+
]);
|
|
5205
|
+
const decisions = extractMatches(text3, [
|
|
5206
|
+
/(?:decided|decision|adr|we chose|approved|rejected)\s+([^.!?\n]{8,220})/gi
|
|
5207
|
+
]);
|
|
5208
|
+
const temporalAnchors = extractMatches(text3, [
|
|
5209
|
+
/\b(\d{4}-\d{2}-\d{2}(?:[T ][0-9:.+-Z]+)?)\b/g,
|
|
5210
|
+
/\b(today|yesterday|tomorrow|this week|next week|last week|morning|afternoon|tonight)\b/gi
|
|
5211
|
+
], 8);
|
|
5212
|
+
const nextActions = extractMatches(text3, [
|
|
5213
|
+
/(?:next|todo|follow[- ]?up|remaining|need to)\s*:?\s*([^.!?\n]{8,220})/gi
|
|
5214
|
+
]);
|
|
5215
|
+
const actors = uniq([
|
|
5216
|
+
row.agent_id,
|
|
5217
|
+
...extractMatches(text3, [/\b(?:agent|employee|owner|assignee)[:= ]+([a-zA-Z][a-zA-Z0-9_-]{1,40})/gi], 5)
|
|
5218
|
+
], 6);
|
|
5219
|
+
const successSignals = milestones.length ? milestones : outcome === "success_signal" ? [clean(text3, 180)] : [];
|
|
5220
|
+
const failureSignals = problems.length ? problems : outcome === "failure_signal" || row.has_error ? [clean(text3, 180)] : [];
|
|
5221
|
+
const impact = successSignals.length && failureSignals.length ? "mixed" : failureSignals.length ? "negative" : successSignals.length ? "positive" : "neutral";
|
|
5222
|
+
const signalCount = goals.length + milestones.length + problems.length + decisions.length + nextActions.length;
|
|
5223
|
+
return {
|
|
5224
|
+
labeler: "deterministic",
|
|
5225
|
+
schemaVersion: 1,
|
|
5226
|
+
eventType,
|
|
5227
|
+
intention,
|
|
5228
|
+
outcome,
|
|
5229
|
+
impact,
|
|
5230
|
+
confidence: Math.min(0.95, 0.45 + signalCount * 0.08 + (intention ? 0.1 : 0) + (outcome ? 0.1 : 0)),
|
|
5231
|
+
goals,
|
|
5232
|
+
milestones,
|
|
5233
|
+
problems,
|
|
5234
|
+
decisions,
|
|
5235
|
+
actors,
|
|
5236
|
+
temporalAnchors,
|
|
5237
|
+
successSignals,
|
|
5238
|
+
failureSignals,
|
|
5239
|
+
nextActions,
|
|
5240
|
+
summary: clean(text3, 280)
|
|
5241
|
+
};
|
|
5242
|
+
}
|
|
5243
|
+
function ontologyPayload(row) {
|
|
5244
|
+
const semantic = inferSemanticLabel(row);
|
|
5245
|
+
return {
|
|
5246
|
+
tool_name: row.tool_name,
|
|
5247
|
+
memory_version: row.version ?? null,
|
|
5248
|
+
domain: row.domain ?? null,
|
|
5249
|
+
trajectory: row.trajectory ? safeJson(row.trajectory) : null,
|
|
5250
|
+
semantic
|
|
5251
|
+
};
|
|
5252
|
+
}
|
|
5253
|
+
function safeJson(value) {
|
|
5254
|
+
try {
|
|
5255
|
+
return JSON.parse(value);
|
|
5256
|
+
} catch {
|
|
5257
|
+
return value.slice(0, 1e3);
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
5260
|
+
async function resolveClient(client) {
|
|
5261
|
+
if (client) return client;
|
|
5262
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
5263
|
+
return getClient2();
|
|
5264
|
+
}
|
|
5265
|
+
async function insertOntologyForMemory(row, client) {
|
|
5266
|
+
const db = await resolveClient(client);
|
|
5267
|
+
const occurredAt = row.timestamp;
|
|
5268
|
+
const sequence = Number(row.version ?? 0) || Math.floor(new Date(occurredAt).getTime() / 1e3);
|
|
5269
|
+
const eventType = inferOntologyEventType(row);
|
|
5270
|
+
const intention = inferIntention(row);
|
|
5271
|
+
const outcome = inferOutcome(row);
|
|
5272
|
+
const eventId = stableId2("event", row.id);
|
|
5273
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5274
|
+
await db.execute({
|
|
5275
|
+
sql: `INSERT INTO agent_sessions (id, agent_id, project_name, started_at, last_event_at, event_count, properties)
|
|
5276
|
+
VALUES (?, ?, ?, ?, ?, 1, ?)
|
|
5277
|
+
ON CONFLICT(id) DO UPDATE SET last_event_at = MAX(last_event_at, excluded.last_event_at),
|
|
5278
|
+
event_count = event_count + 1`,
|
|
5279
|
+
args: [row.session_id, row.agent_id, row.project_name, occurredAt, occurredAt, JSON.stringify({ agent_role: row.agent_role })]
|
|
5280
|
+
});
|
|
5281
|
+
await db.execute({
|
|
5282
|
+
sql: `INSERT OR IGNORE INTO agent_events
|
|
5283
|
+
(id, event_type, occurred_at, sequence_index, actor_agent_id, agent_role, project_name,
|
|
5284
|
+
session_id, task_id, goal_id, parent_event_id, intention, outcome, evidence_memory_id,
|
|
5285
|
+
impact, payload, created_at)
|
|
5286
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?, ?, ?, ?, ?, ?)`,
|
|
5287
|
+
args: [
|
|
5288
|
+
eventId,
|
|
5289
|
+
eventType,
|
|
5290
|
+
occurredAt,
|
|
5291
|
+
sequence,
|
|
5292
|
+
row.agent_id,
|
|
5293
|
+
row.agent_role,
|
|
5294
|
+
row.project_name,
|
|
5295
|
+
row.session_id,
|
|
5296
|
+
row.task_id ?? null,
|
|
5297
|
+
intention,
|
|
5298
|
+
outcome,
|
|
5299
|
+
row.id,
|
|
5300
|
+
row.has_error ? "negative" : outcome === "success_signal" ? "positive" : "neutral",
|
|
5301
|
+
JSON.stringify(ontologyPayload(row)),
|
|
5302
|
+
now
|
|
5303
|
+
]
|
|
5304
|
+
});
|
|
5305
|
+
const semantic = inferSemanticLabel(row);
|
|
5306
|
+
await db.execute({
|
|
5307
|
+
sql: `INSERT INTO agent_semantic_labels
|
|
5308
|
+
(id, source_memory_id, event_id, labeler, schema_version, confidence, labels, created_at, updated_at)
|
|
5309
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5310
|
+
ON CONFLICT(id) DO UPDATE SET confidence = excluded.confidence,
|
|
5311
|
+
labels = excluded.labels, updated_at = excluded.updated_at`,
|
|
5312
|
+
args: [
|
|
5313
|
+
stableId2("semantic", row.id, semantic.labeler, semantic.schemaVersion),
|
|
5314
|
+
row.id,
|
|
5315
|
+
eventId,
|
|
5316
|
+
semantic.labeler,
|
|
5317
|
+
semantic.schemaVersion,
|
|
5318
|
+
semantic.confidence,
|
|
5319
|
+
JSON.stringify(semantic),
|
|
5320
|
+
now,
|
|
5321
|
+
now
|
|
5322
|
+
]
|
|
5323
|
+
});
|
|
5324
|
+
for (const statement of extractGoalCandidates(row)) {
|
|
5325
|
+
const goalId = stableId2("goal", row.project_name, statement.toLowerCase());
|
|
5326
|
+
await db.execute({
|
|
5327
|
+
sql: `INSERT INTO agent_goals
|
|
5328
|
+
(id, statement, owner_agent_id, project_name, status, priority, success_criteria,
|
|
5329
|
+
parent_goal_id, due_at, achieved_at, supersedes_id, created_at, updated_at, source_memory_id)
|
|
5330
|
+
VALUES (?, ?, ?, ?, 'open', 5, NULL, NULL, NULL, NULL, NULL, ?, ?, ?)
|
|
5331
|
+
ON CONFLICT(id) DO UPDATE SET updated_at = excluded.updated_at`,
|
|
5332
|
+
args: [goalId, statement, row.agent_id, row.project_name, now, now, row.id]
|
|
5333
|
+
});
|
|
5334
|
+
await db.execute({
|
|
5335
|
+
sql: `INSERT OR IGNORE INTO agent_goal_links
|
|
5336
|
+
(id, goal_id, link_type, target_id, target_type, created_at)
|
|
5337
|
+
VALUES (?, ?, 'evidence', ?, 'memory', ?)`,
|
|
5338
|
+
args: [stableId2("goal_link", goalId, row.id, "memory"), goalId, row.id, now]
|
|
5339
|
+
});
|
|
5340
|
+
await db.execute({
|
|
5341
|
+
sql: `INSERT OR IGNORE INTO agent_goal_links
|
|
5342
|
+
(id, goal_id, link_type, target_id, target_type, created_at)
|
|
5343
|
+
VALUES (?, ?, 'event', ?, 'event', ?)`,
|
|
5344
|
+
args: [stableId2("goal_link", goalId, eventId, "event"), goalId, eventId, now]
|
|
5345
|
+
});
|
|
5346
|
+
}
|
|
5347
|
+
}
|
|
5348
|
+
async function insertOntologyForBatch(rows, client) {
|
|
5349
|
+
const db = await resolveClient(client);
|
|
5350
|
+
let count = 0;
|
|
5351
|
+
for (const row of rows) {
|
|
5352
|
+
try {
|
|
5353
|
+
await insertOntologyForMemory(row, db);
|
|
5354
|
+
count++;
|
|
5355
|
+
} catch {
|
|
5356
|
+
}
|
|
5357
|
+
}
|
|
5358
|
+
return count;
|
|
5359
|
+
}
|
|
5360
|
+
var init_agentic_ontology = __esm({
|
|
5361
|
+
"src/lib/agentic-ontology.ts"() {
|
|
5362
|
+
"use strict";
|
|
5363
|
+
}
|
|
5364
|
+
});
|
|
5365
|
+
|
|
4806
5366
|
// src/lib/store.ts
|
|
4807
5367
|
var store_exports = {};
|
|
4808
5368
|
__export(store_exports, {
|
|
@@ -5146,6 +5706,11 @@ async function flushBatch() {
|
|
|
5146
5706
|
await insertMemoryCardsForBatch2(batch);
|
|
5147
5707
|
} catch {
|
|
5148
5708
|
}
|
|
5709
|
+
try {
|
|
5710
|
+
const { insertOntologyForBatch: insertOntologyForBatch2 } = await Promise.resolve().then(() => (init_agentic_ontology(), agentic_ontology_exports));
|
|
5711
|
+
await insertOntologyForBatch2(batch);
|
|
5712
|
+
} catch {
|
|
5713
|
+
}
|
|
5149
5714
|
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
5150
5715
|
_pendingRecords.splice(0, batch.length);
|
|
5151
5716
|
try {
|
|
@@ -5279,8 +5844,8 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
5279
5844
|
args.push(vectorToBlob(queryVector));
|
|
5280
5845
|
sql += ` LIMIT ?`;
|
|
5281
5846
|
args.push(limit);
|
|
5282
|
-
const
|
|
5283
|
-
return
|
|
5847
|
+
const result2 = await client.execute({ sql, args });
|
|
5848
|
+
return result2.rows.map((row) => ({
|
|
5284
5849
|
id: row.id,
|
|
5285
5850
|
agent_id: row.agent_id,
|
|
5286
5851
|
agent_role: row.agent_role,
|
|
@@ -5314,14 +5879,14 @@ async function attachDocumentMetadata(records) {
|
|
|
5314
5879
|
try {
|
|
5315
5880
|
const client = getClient();
|
|
5316
5881
|
const placeholders = docIds.map(() => "?").join(",");
|
|
5317
|
-
const
|
|
5882
|
+
const result2 = await client.execute({
|
|
5318
5883
|
sql: `SELECT id, filename, mime, source_type, uploaded_at
|
|
5319
5884
|
FROM documents
|
|
5320
5885
|
WHERE id IN (${placeholders})`,
|
|
5321
5886
|
args: docIds
|
|
5322
5887
|
});
|
|
5323
5888
|
const byId = /* @__PURE__ */ new Map();
|
|
5324
|
-
for (const row of
|
|
5889
|
+
for (const row of result2.rows) {
|
|
5325
5890
|
const id = row.id;
|
|
5326
5891
|
byId.set(id, {
|
|
5327
5892
|
document_id: id,
|
|
@@ -5344,19 +5909,19 @@ async function flushTier3(agentId, options) {
|
|
|
5344
5909
|
const maxAge = options?.maxAgeHours ?? 72;
|
|
5345
5910
|
const cutoff = new Date(Date.now() - maxAge * 36e5).toISOString();
|
|
5346
5911
|
if (options?.dryRun) {
|
|
5347
|
-
const
|
|
5912
|
+
const result3 = await client.execute({
|
|
5348
5913
|
sql: `SELECT COUNT(*) as cnt FROM memories
|
|
5349
5914
|
WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
|
|
5350
5915
|
args: [agentId, cutoff]
|
|
5351
5916
|
});
|
|
5352
|
-
return { archived: Number(
|
|
5917
|
+
return { archived: Number(result3.rows[0]?.cnt ?? 0) };
|
|
5353
5918
|
}
|
|
5354
|
-
const
|
|
5919
|
+
const result2 = await client.execute({
|
|
5355
5920
|
sql: `UPDATE memories SET status = 'archived'
|
|
5356
5921
|
WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
|
|
5357
5922
|
args: [agentId, cutoff]
|
|
5358
5923
|
});
|
|
5359
|
-
return { archived:
|
|
5924
|
+
return { archived: result2.rowsAffected };
|
|
5360
5925
|
}
|
|
5361
5926
|
async function disposeStore() {
|
|
5362
5927
|
if (_flushTimer !== null) {
|
|
@@ -5391,11 +5956,11 @@ function reserveVersions(count) {
|
|
|
5391
5956
|
async function getMemoryCardinality(agentId) {
|
|
5392
5957
|
try {
|
|
5393
5958
|
const client = getClient();
|
|
5394
|
-
const
|
|
5959
|
+
const result2 = await client.execute({
|
|
5395
5960
|
sql: `SELECT COUNT(*) as cnt FROM memories WHERE agent_id = ? AND COALESCE(status, 'active') = 'active'`,
|
|
5396
5961
|
args: [agentId]
|
|
5397
5962
|
});
|
|
5398
|
-
return Number(
|
|
5963
|
+
return Number(result2.rows[0]?.cnt) || 0;
|
|
5399
5964
|
} catch {
|
|
5400
5965
|
return 0;
|
|
5401
5966
|
}
|
|
@@ -5696,7 +6261,7 @@ __export(file_grep_exports, {
|
|
|
5696
6261
|
grepProjectFiles: () => grepProjectFiles
|
|
5697
6262
|
});
|
|
5698
6263
|
import { execSync as execSync4 } from "child_process";
|
|
5699
|
-
import { readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as
|
|
6264
|
+
import { readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync4, existsSync as existsSync10 } from "fs";
|
|
5700
6265
|
import path11 from "path";
|
|
5701
6266
|
import crypto2 from "crypto";
|
|
5702
6267
|
function hasRipgrep() {
|
|
@@ -5813,7 +6378,7 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
|
|
|
5813
6378
|
for (const filePath of files.slice(0, MAX_FILES)) {
|
|
5814
6379
|
const absPath = path11.join(projectRoot, filePath);
|
|
5815
6380
|
try {
|
|
5816
|
-
const stat =
|
|
6381
|
+
const stat = statSync4(absPath);
|
|
5817
6382
|
if (stat.size > MAX_FILE_SIZE) continue;
|
|
5818
6383
|
const content = readFileSync6(absPath, "utf8");
|
|
5819
6384
|
const lines = content.split("\n");
|
|
@@ -5930,9 +6495,9 @@ __export(graph_query_exports, {
|
|
|
5930
6495
|
async function getEntityByName(client, name, type) {
|
|
5931
6496
|
const sql = type ? `SELECT * FROM entities WHERE LOWER(name) = LOWER(?) AND type = ? LIMIT 1` : `SELECT * FROM entities WHERE LOWER(name) = LOWER(?) LIMIT 1`;
|
|
5932
6497
|
const args = type ? [name, type] : [name];
|
|
5933
|
-
const
|
|
5934
|
-
if (
|
|
5935
|
-
const row =
|
|
6498
|
+
const result2 = await client.execute({ sql, args });
|
|
6499
|
+
if (result2.rows.length === 0) return null;
|
|
6500
|
+
const row = result2.rows[0];
|
|
5936
6501
|
return {
|
|
5937
6502
|
id: String(row.id),
|
|
5938
6503
|
name: String(row.name),
|
|
@@ -5943,11 +6508,11 @@ async function getEntityByName(client, name, type) {
|
|
|
5943
6508
|
};
|
|
5944
6509
|
}
|
|
5945
6510
|
async function searchEntities(client, query, limit = 10) {
|
|
5946
|
-
const
|
|
6511
|
+
const result2 = await client.execute({
|
|
5947
6512
|
sql: `SELECT * FROM entities WHERE LOWER(name) LIKE ? ORDER BY last_seen DESC LIMIT ?`,
|
|
5948
6513
|
args: [`%${query.toLowerCase()}%`, limit]
|
|
5949
6514
|
});
|
|
5950
|
-
return
|
|
6515
|
+
return result2.rows.map((row) => ({
|
|
5951
6516
|
id: String(row.id),
|
|
5952
6517
|
name: String(row.name),
|
|
5953
6518
|
type: String(row.type),
|
|
@@ -5987,8 +6552,8 @@ async function getRelationships(client, entityId2, options) {
|
|
|
5987
6552
|
args.push(options.type);
|
|
5988
6553
|
}
|
|
5989
6554
|
sql += ` ORDER BY r.weight DESC, r.timestamp DESC`;
|
|
5990
|
-
const
|
|
5991
|
-
return
|
|
6555
|
+
const result2 = await client.execute({ sql, args });
|
|
6556
|
+
return result2.rows.map((row) => ({
|
|
5992
6557
|
id: String(row.id),
|
|
5993
6558
|
sourceEntityId: String(row.source_entity_id),
|
|
5994
6559
|
targetEntityId: String(row.target_entity_id),
|
|
@@ -6003,7 +6568,7 @@ async function getRelationships(client, entityId2, options) {
|
|
|
6003
6568
|
}));
|
|
6004
6569
|
}
|
|
6005
6570
|
async function traverseChain(client, startEntityId, relationshipType, maxDepth = 3) {
|
|
6006
|
-
const
|
|
6571
|
+
const result2 = await client.execute({
|
|
6007
6572
|
sql: `WITH RECURSIVE chain(entity_id, depth, rel_type) AS (
|
|
6008
6573
|
SELECT ?, 0, ''
|
|
6009
6574
|
UNION ALL
|
|
@@ -6018,7 +6583,7 @@ async function traverseChain(client, startEntityId, relationshipType, maxDepth =
|
|
|
6018
6583
|
ORDER BY chain.depth ASC`,
|
|
6019
6584
|
args: [startEntityId, relationshipType, maxDepth]
|
|
6020
6585
|
});
|
|
6021
|
-
return
|
|
6586
|
+
return result2.rows.map((row) => ({
|
|
6022
6587
|
entity: {
|
|
6023
6588
|
id: String(row.id),
|
|
6024
6589
|
name: String(row.name),
|
|
@@ -6032,7 +6597,7 @@ async function traverseChain(client, startEntityId, relationshipType, maxDepth =
|
|
|
6032
6597
|
}));
|
|
6033
6598
|
}
|
|
6034
6599
|
async function getEntityNeighbors(client, entityId2, maxHops = 2) {
|
|
6035
|
-
const
|
|
6600
|
+
const result2 = await client.execute({
|
|
6036
6601
|
sql: `WITH RECURSIVE neighborhood(entity_id, depth, rel_type, rel_confidence) AS (
|
|
6037
6602
|
SELECT ?, 0, '', 1.0
|
|
6038
6603
|
UNION ALL
|
|
@@ -6054,7 +6619,7 @@ async function getEntityNeighbors(client, entityId2, maxHops = 2) {
|
|
|
6054
6619
|
LIMIT 50`,
|
|
6055
6620
|
args: [entityId2, maxHops]
|
|
6056
6621
|
});
|
|
6057
|
-
return
|
|
6622
|
+
return result2.rows.map((row) => ({
|
|
6058
6623
|
entity: {
|
|
6059
6624
|
id: String(row.id),
|
|
6060
6625
|
name: String(row.name),
|
|
@@ -6091,7 +6656,7 @@ async function getEntityTimeline(client, entityId2, since) {
|
|
|
6091
6656
|
sinceClause = " AND r.timestamp >= ?";
|
|
6092
6657
|
args.push(since.toISOString());
|
|
6093
6658
|
}
|
|
6094
|
-
const
|
|
6659
|
+
const result2 = await client.execute({
|
|
6095
6660
|
sql: `SELECT r.*, s.name as source_name, t.name as target_name
|
|
6096
6661
|
FROM relationships r
|
|
6097
6662
|
JOIN entities s ON r.source_entity_id = s.id
|
|
@@ -6100,7 +6665,7 @@ async function getEntityTimeline(client, entityId2, since) {
|
|
|
6100
6665
|
ORDER BY r.timestamp DESC`,
|
|
6101
6666
|
args
|
|
6102
6667
|
});
|
|
6103
|
-
return
|
|
6668
|
+
return result2.rows.map((row) => ({
|
|
6104
6669
|
relationship: {
|
|
6105
6670
|
id: String(row.id),
|
|
6106
6671
|
sourceEntityId: String(row.source_entity_id),
|
|
@@ -6134,7 +6699,7 @@ async function getRelationshipFrequency(client, entityId2, options) {
|
|
|
6134
6699
|
default:
|
|
6135
6700
|
bucketExpr = `strftime('%Y-%m-%d', r.timestamp)`;
|
|
6136
6701
|
}
|
|
6137
|
-
const
|
|
6702
|
+
const result2 = await client.execute({
|
|
6138
6703
|
sql: `SELECT ${bucketExpr} as bucket, COUNT(*) as cnt, r.type as rel_type
|
|
6139
6704
|
FROM relationships r
|
|
6140
6705
|
WHERE (r.source_entity_id = ? OR r.target_entity_id = ?)${typeClause}
|
|
@@ -6142,7 +6707,7 @@ async function getRelationshipFrequency(client, entityId2, options) {
|
|
|
6142
6707
|
ORDER BY bucket DESC`,
|
|
6143
6708
|
args
|
|
6144
6709
|
});
|
|
6145
|
-
return
|
|
6710
|
+
return result2.rows.map((row) => ({
|
|
6146
6711
|
bucket: String(row.bucket),
|
|
6147
6712
|
count: Number(row.cnt),
|
|
6148
6713
|
relationshipType: options?.type ? options.type : String(row.rel_type)
|
|
@@ -6155,7 +6720,7 @@ async function getConversationPartners(client, contactEntityId, since) {
|
|
|
6155
6720
|
sinceClause = " AND r.timestamp >= ?";
|
|
6156
6721
|
args.push(since.toISOString());
|
|
6157
6722
|
}
|
|
6158
|
-
const
|
|
6723
|
+
const result2 = await client.execute({
|
|
6159
6724
|
sql: `SELECT
|
|
6160
6725
|
CASE
|
|
6161
6726
|
WHEN r.source_entity_id = ? THEN r.target_entity_id
|
|
@@ -6170,7 +6735,7 @@ async function getConversationPartners(client, contactEntityId, since) {
|
|
|
6170
6735
|
args: [contactEntityId, ...args]
|
|
6171
6736
|
});
|
|
6172
6737
|
const partners = [];
|
|
6173
|
-
for (const row of
|
|
6738
|
+
for (const row of result2.rows) {
|
|
6174
6739
|
const partnerId = String(row.partner_id);
|
|
6175
6740
|
const entityResult = await client.execute({
|
|
6176
6741
|
sql: "SELECT * FROM entities WHERE id = ?",
|
|
@@ -6195,7 +6760,7 @@ async function getConversationPartners(client, contactEntityId, since) {
|
|
|
6195
6760
|
}
|
|
6196
6761
|
async function getHotEntities(client, since, limit = 10) {
|
|
6197
6762
|
const sinceISO = since.toISOString();
|
|
6198
|
-
const
|
|
6763
|
+
const result2 = await client.execute({
|
|
6199
6764
|
sql: `SELECT entity_id, COUNT(*) as rel_count
|
|
6200
6765
|
FROM (
|
|
6201
6766
|
SELECT source_entity_id as entity_id FROM relationships WHERE timestamp >= ?
|
|
@@ -6208,7 +6773,7 @@ async function getHotEntities(client, since, limit = 10) {
|
|
|
6208
6773
|
args: [sinceISO, sinceISO, limit]
|
|
6209
6774
|
});
|
|
6210
6775
|
const hotEntities = [];
|
|
6211
|
-
for (const row of
|
|
6776
|
+
for (const row of result2.rows) {
|
|
6212
6777
|
const eid = String(row.entity_id);
|
|
6213
6778
|
const entityResult = await client.execute({
|
|
6214
6779
|
sql: "SELECT * FROM entities WHERE id = ?",
|
|
@@ -6255,7 +6820,7 @@ async function matchEntities(query, client) {
|
|
|
6255
6820
|
if (words.length === 0) return [];
|
|
6256
6821
|
try {
|
|
6257
6822
|
const matchExpr = words.map((w) => `${w}*`).join(" OR ");
|
|
6258
|
-
const
|
|
6823
|
+
const result2 = await client.execute({
|
|
6259
6824
|
sql: `SELECT e.id, e.name FROM entities e
|
|
6260
6825
|
JOIN entities_fts fts ON e.rowid = fts.rowid
|
|
6261
6826
|
WHERE entities_fts MATCH ?
|
|
@@ -6263,8 +6828,8 @@ async function matchEntities(query, client) {
|
|
|
6263
6828
|
LIMIT ?`,
|
|
6264
6829
|
args: [matchExpr, MAX_ENTITY_MATCHES]
|
|
6265
6830
|
});
|
|
6266
|
-
if (
|
|
6267
|
-
return
|
|
6831
|
+
if (result2.rows.length > 0) {
|
|
6832
|
+
return result2.rows.map((row) => ({
|
|
6268
6833
|
entityId: String(row.id),
|
|
6269
6834
|
name: String(row.name)
|
|
6270
6835
|
}));
|
|
@@ -6274,13 +6839,13 @@ async function matchEntities(query, client) {
|
|
|
6274
6839
|
const conditions = words.map(() => `LOWER(name) LIKE ?`);
|
|
6275
6840
|
const args = words.map((w) => `%${w}%`);
|
|
6276
6841
|
try {
|
|
6277
|
-
const
|
|
6842
|
+
const result2 = await client.execute({
|
|
6278
6843
|
sql: `SELECT id, name FROM entities
|
|
6279
6844
|
WHERE ${conditions.join(" OR ")}
|
|
6280
6845
|
LIMIT ?`,
|
|
6281
6846
|
args: [...args, MAX_ENTITY_MATCHES]
|
|
6282
6847
|
});
|
|
6283
|
-
return
|
|
6848
|
+
return result2.rows.map((row) => ({
|
|
6284
6849
|
entityId: String(row.id),
|
|
6285
6850
|
name: String(row.name)
|
|
6286
6851
|
}));
|
|
@@ -6293,12 +6858,12 @@ async function getLinkedMemories(entityIds, client) {
|
|
|
6293
6858
|
if (entityIds.length === 0) return linked;
|
|
6294
6859
|
const placeholders = entityIds.map(() => "?").join(",");
|
|
6295
6860
|
try {
|
|
6296
|
-
const
|
|
6861
|
+
const result2 = await client.execute({
|
|
6297
6862
|
sql: `SELECT entity_id, memory_id FROM entity_memories
|
|
6298
6863
|
WHERE entity_id IN (${placeholders})`,
|
|
6299
6864
|
args: entityIds
|
|
6300
6865
|
});
|
|
6301
|
-
for (const row of
|
|
6866
|
+
for (const row of result2.rows) {
|
|
6302
6867
|
const entityId2 = String(row.entity_id);
|
|
6303
6868
|
const memoryId = String(row.memory_id);
|
|
6304
6869
|
const entry = linked.get(entityId2) ?? {
|
|
@@ -6371,13 +6936,13 @@ async function applyHyperedgeBoost(entities, client, boostMap, resultIds) {
|
|
|
6371
6936
|
const entityIds = entities.map((e) => e.entityId);
|
|
6372
6937
|
const placeholders = entityIds.map(() => "?").join(",");
|
|
6373
6938
|
try {
|
|
6374
|
-
const
|
|
6939
|
+
const result2 = await client.execute({
|
|
6375
6940
|
sql: `SELECT hyperedge_id, entity_id FROM hyperedge_nodes
|
|
6376
6941
|
WHERE entity_id IN (${placeholders})`,
|
|
6377
6942
|
args: entityIds
|
|
6378
6943
|
});
|
|
6379
6944
|
const hyperedgeEntities = /* @__PURE__ */ new Map();
|
|
6380
|
-
for (const row of
|
|
6945
|
+
for (const row of result2.rows) {
|
|
6381
6946
|
const hid = String(row.hyperedge_id);
|
|
6382
6947
|
const eid = String(row.entity_id);
|
|
6383
6948
|
const set = hyperedgeEntities.get(hid) ?? /* @__PURE__ */ new Set();
|
|
@@ -6702,10 +7267,10 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
6702
7267
|
};
|
|
6703
7268
|
try {
|
|
6704
7269
|
const fs = await import("fs");
|
|
6705
|
-
const
|
|
7270
|
+
const path50 = await import("path");
|
|
6706
7271
|
const os21 = await import("os");
|
|
6707
|
-
const logPath =
|
|
6708
|
-
fs.mkdirSync(
|
|
7272
|
+
const logPath = path50.join(os21.homedir(), ".exe-os", "search-quality.jsonl");
|
|
7273
|
+
fs.mkdirSync(path50.dirname(logPath), { recursive: true });
|
|
6709
7274
|
fs.appendFileSync(logPath, JSON.stringify(logEntry) + "\n");
|
|
6710
7275
|
} catch {
|
|
6711
7276
|
}
|
|
@@ -6754,8 +7319,8 @@ async function estimateCardinality(agentId, options) {
|
|
|
6754
7319
|
}
|
|
6755
7320
|
sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
|
|
6756
7321
|
try {
|
|
6757
|
-
const
|
|
6758
|
-
return Number(
|
|
7322
|
+
const result2 = await client.execute({ sql, args });
|
|
7323
|
+
return Number(result2.rows[0]?.cnt) || 0;
|
|
6759
7324
|
} catch {
|
|
6760
7325
|
return 0;
|
|
6761
7326
|
}
|
|
@@ -6886,8 +7451,8 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
6886
7451
|
sql = appendMemoryTypeFilter(sql, args, "m.memory_type", options);
|
|
6887
7452
|
sql += ` ORDER BY rank LIMIT ?`;
|
|
6888
7453
|
args.push(limit);
|
|
6889
|
-
const
|
|
6890
|
-
return
|
|
7454
|
+
const result2 = await client.execute({ sql, args });
|
|
7455
|
+
return result2.rows.map((row) => ({
|
|
6891
7456
|
id: row.id,
|
|
6892
7457
|
agent_id: row.agent_id,
|
|
6893
7458
|
agent_role: row.agent_role,
|
|
@@ -7002,8 +7567,8 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
7002
7567
|
}
|
|
7003
7568
|
sql += ` ORDER BY timestamp DESC LIMIT ?`;
|
|
7004
7569
|
args.push(limit);
|
|
7005
|
-
const
|
|
7006
|
-
const recentResults =
|
|
7570
|
+
const result2 = await client.execute({ sql, args });
|
|
7571
|
+
const recentResults = result2.rows.map((row) => rowToMemoryRecord(row));
|
|
7007
7572
|
if (sessionBoundaryMemories.length > 0) {
|
|
7008
7573
|
const seenIds = new Set(recentResults.map((r) => r.id));
|
|
7009
7574
|
for (const bm of sessionBoundaryMemories) {
|
|
@@ -7086,9 +7651,9 @@ async function trajectoryBypass(queryText, agentId, options, limit) {
|
|
|
7086
7651
|
}
|
|
7087
7652
|
sql += ` ORDER BY timestamp DESC LIMIT ?`;
|
|
7088
7653
|
args.push(limit);
|
|
7089
|
-
const
|
|
7090
|
-
if (
|
|
7091
|
-
return
|
|
7654
|
+
const result2 = await client.execute({ sql, args });
|
|
7655
|
+
if (result2.rows.length < 3) return null;
|
|
7656
|
+
return result2.rows.map((row) => ({
|
|
7092
7657
|
id: row.id,
|
|
7093
7658
|
agent_id: row.agent_id,
|
|
7094
7659
|
agent_role: row.agent_role,
|
|
@@ -7658,8 +8223,8 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
7658
8223
|
}
|
|
7659
8224
|
function getCacheAgeMs() {
|
|
7660
8225
|
try {
|
|
7661
|
-
const { statSync:
|
|
7662
|
-
const s =
|
|
8226
|
+
const { statSync: statSync9 } = __require("fs");
|
|
8227
|
+
const s = statSync9(CACHE_PATH);
|
|
7663
8228
|
return Date.now() - s.mtimeMs;
|
|
7664
8229
|
} catch {
|
|
7665
8230
|
return Infinity;
|
|
@@ -7914,10 +8479,10 @@ function freeLicense() {
|
|
|
7914
8479
|
async function countActiveMemories() {
|
|
7915
8480
|
if (!isInitialized()) return 0;
|
|
7916
8481
|
const client = getClient();
|
|
7917
|
-
const
|
|
8482
|
+
const result2 = await client.execute(
|
|
7918
8483
|
"SELECT COUNT(*) as cnt FROM memories WHERE status = 'active' OR status IS NULL"
|
|
7919
8484
|
);
|
|
7920
|
-
const row =
|
|
8485
|
+
const row = result2.rows[0];
|
|
7921
8486
|
return Number(row?.cnt ?? 0);
|
|
7922
8487
|
}
|
|
7923
8488
|
async function assertMemoryLimit() {
|
|
@@ -7997,8 +8562,8 @@ __export(wiki_client_exports, {
|
|
|
7997
8562
|
listDocuments: () => listDocuments,
|
|
7998
8563
|
listWorkspaces: () => listWorkspaces
|
|
7999
8564
|
});
|
|
8000
|
-
async function wikiFetch(config2,
|
|
8001
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
8565
|
+
async function wikiFetch(config2, path50, method = "GET", body) {
|
|
8566
|
+
const url = `${config2.baseUrl}/api/v1${path50}`;
|
|
8002
8567
|
const headers = {
|
|
8003
8568
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
8004
8569
|
"Content-Type": "application/json"
|
|
@@ -8031,7 +8596,7 @@ async function wikiFetch(config2, path49, method = "GET", body) {
|
|
|
8031
8596
|
}
|
|
8032
8597
|
}
|
|
8033
8598
|
if (!response.ok) {
|
|
8034
|
-
throw new Error(`Wiki API ${method} ${
|
|
8599
|
+
throw new Error(`Wiki API ${method} ${path50}: ${response.status} ${response.statusText}`);
|
|
8035
8600
|
}
|
|
8036
8601
|
return response.json();
|
|
8037
8602
|
} finally {
|
|
@@ -8145,7 +8710,7 @@ __export(consolidation_exports, {
|
|
|
8145
8710
|
});
|
|
8146
8711
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
8147
8712
|
async function selectUnconsolidated(client, limit = 200) {
|
|
8148
|
-
const
|
|
8713
|
+
const result2 = await client.execute({
|
|
8149
8714
|
sql: `SELECT id, agent_id, project_name, tool_name, raw_text, timestamp
|
|
8150
8715
|
FROM memories
|
|
8151
8716
|
WHERE consolidated = 0
|
|
@@ -8153,7 +8718,7 @@ async function selectUnconsolidated(client, limit = 200) {
|
|
|
8153
8718
|
LIMIT ?`,
|
|
8154
8719
|
args: [limit]
|
|
8155
8720
|
});
|
|
8156
|
-
return
|
|
8721
|
+
return result2.rows.map((row) => ({
|
|
8157
8722
|
id: row.id,
|
|
8158
8723
|
agent_id: row.agent_id,
|
|
8159
8724
|
project_name: row.project_name,
|
|
@@ -8393,10 +8958,10 @@ async function runConsolidation(client, options) {
|
|
|
8393
8958
|
if (dedupCount > 0) clustersProcessed++;
|
|
8394
8959
|
continue;
|
|
8395
8960
|
}
|
|
8396
|
-
const
|
|
8961
|
+
const result2 = await storeConsolidation(client, cluster, synthesis, options.embedFn);
|
|
8397
8962
|
if (isCoordinator && options.wikiConfig) {
|
|
8398
8963
|
await pushToWiki(
|
|
8399
|
-
{ ...
|
|
8964
|
+
{ ...result2, projectName: cluster.projectName },
|
|
8400
8965
|
options.wikiConfig
|
|
8401
8966
|
).catch((err) => {
|
|
8402
8967
|
process.stderr.write(
|
|
@@ -8406,7 +8971,7 @@ async function runConsolidation(client, options) {
|
|
|
8406
8971
|
});
|
|
8407
8972
|
}
|
|
8408
8973
|
if (isCoordinator) {
|
|
8409
|
-
const sourceIds =
|
|
8974
|
+
const sourceIds = result2.sourceIds;
|
|
8410
8975
|
if (sourceIds.length > 0) {
|
|
8411
8976
|
const placeholders = sourceIds.map(() => "?").join(",");
|
|
8412
8977
|
await client.execute({
|
|
@@ -8476,26 +9041,26 @@ function cosineSimilarity(a, b) {
|
|
|
8476
9041
|
return denom === 0 ? 0 : dot / denom;
|
|
8477
9042
|
}
|
|
8478
9043
|
async function isUserIdle(client, idleMinutes = 30) {
|
|
8479
|
-
const
|
|
9044
|
+
const result2 = await client.execute({
|
|
8480
9045
|
sql: `SELECT MAX(timestamp) as last_activity
|
|
8481
9046
|
FROM memories
|
|
8482
9047
|
WHERE tool_name != 'consolidation'
|
|
8483
9048
|
AND timestamp >= datetime('now', '-1 day')`,
|
|
8484
9049
|
args: []
|
|
8485
9050
|
});
|
|
8486
|
-
const lastActivity =
|
|
9051
|
+
const lastActivity = result2.rows[0]?.last_activity;
|
|
8487
9052
|
if (!lastActivity) return true;
|
|
8488
9053
|
const lastMs = new Date(lastActivity).getTime();
|
|
8489
9054
|
const now = Date.now();
|
|
8490
9055
|
return now - lastMs >= idleMinutes * 60 * 1e3;
|
|
8491
9056
|
}
|
|
8492
9057
|
async function countUnconsolidated(client) {
|
|
8493
|
-
const
|
|
9058
|
+
const result2 = await client.execute({
|
|
8494
9059
|
sql: `SELECT COUNT(*) as cnt FROM memories
|
|
8495
9060
|
WHERE consolidated = 0`,
|
|
8496
9061
|
args: []
|
|
8497
9062
|
});
|
|
8498
|
-
return Number(
|
|
9063
|
+
return Number(result2.rows[0]?.cnt ?? 0);
|
|
8499
9064
|
}
|
|
8500
9065
|
var ROLE_PROMPTS, DEFAULT_ROLE_PROMPT, WIKI_FETCH_TIMEOUT_MS;
|
|
8501
9066
|
var init_consolidation = __esm({
|
|
@@ -8634,12 +9199,12 @@ var init_tmux_transport = __esm({
|
|
|
8634
9199
|
}
|
|
8635
9200
|
isPaneInCopyMode(target) {
|
|
8636
9201
|
try {
|
|
8637
|
-
const
|
|
9202
|
+
const result2 = execFileSync(
|
|
8638
9203
|
"tmux",
|
|
8639
9204
|
["display-message", "-p", "-t", target, "#{pane_in_mode}"],
|
|
8640
9205
|
{ ...QUIET, timeout: 3e3 }
|
|
8641
9206
|
).trim();
|
|
8642
|
-
return
|
|
9207
|
+
return result2 === "1";
|
|
8643
9208
|
} catch {
|
|
8644
9209
|
return false;
|
|
8645
9210
|
}
|
|
@@ -8981,11 +9546,11 @@ async function recordSessionKill(input) {
|
|
|
8981
9546
|
async function countKillsSince(sinceISO) {
|
|
8982
9547
|
try {
|
|
8983
9548
|
const client = getClient();
|
|
8984
|
-
const
|
|
9549
|
+
const result2 = await client.execute({
|
|
8985
9550
|
sql: `SELECT COUNT(*) AS n FROM session_kills WHERE killed_at >= ?`,
|
|
8986
9551
|
args: [sinceISO]
|
|
8987
9552
|
});
|
|
8988
|
-
const row =
|
|
9553
|
+
const row = result2.rows[0];
|
|
8989
9554
|
return row ? Number(row.n) : 0;
|
|
8990
9555
|
} catch {
|
|
8991
9556
|
return 0;
|
|
@@ -8994,13 +9559,13 @@ async function countKillsSince(sinceISO) {
|
|
|
8994
9559
|
async function sumTokensSavedSince(sinceISO) {
|
|
8995
9560
|
try {
|
|
8996
9561
|
const client = getClient();
|
|
8997
|
-
const
|
|
9562
|
+
const result2 = await client.execute({
|
|
8998
9563
|
sql: `SELECT COALESCE(SUM(estimated_tokens_saved), 0) AS total
|
|
8999
9564
|
FROM session_kills
|
|
9000
9565
|
WHERE killed_at >= ?`,
|
|
9001
9566
|
args: [sinceISO]
|
|
9002
9567
|
});
|
|
9003
|
-
const row =
|
|
9568
|
+
const row = result2.rows[0];
|
|
9004
9569
|
return row ? Number(row.total) : 0;
|
|
9005
9570
|
} catch {
|
|
9006
9571
|
return 0;
|
|
@@ -9092,13 +9657,13 @@ function _resetLastRelaunchCache() {
|
|
|
9092
9657
|
async function lastResumeCreatedAtMs(agentId) {
|
|
9093
9658
|
const client = getClient();
|
|
9094
9659
|
const cmScope = sessionScopeFilter(null);
|
|
9095
|
-
const
|
|
9660
|
+
const result2 = await client.execute({
|
|
9096
9661
|
sql: `SELECT MAX(created_at) AS last_created_at
|
|
9097
9662
|
FROM tasks
|
|
9098
9663
|
WHERE assigned_to = ? AND title LIKE ?${cmScope.sql}`,
|
|
9099
9664
|
args: [agentId, `${RESUME_TITLE_PREFIX} %`, ...cmScope.args]
|
|
9100
9665
|
});
|
|
9101
|
-
const raw =
|
|
9666
|
+
const raw = result2.rows[0]?.last_created_at;
|
|
9102
9667
|
if (raw === null || raw === void 0) return null;
|
|
9103
9668
|
const parsed = Date.parse(String(raw));
|
|
9104
9669
|
return Number.isNaN(parsed) ? null : parsed;
|
|
@@ -9335,37 +9900,37 @@ async function countPendingReviews(sessionScope) {
|
|
|
9335
9900
|
const scope = strictSessionScopeFilter(
|
|
9336
9901
|
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
9337
9902
|
);
|
|
9338
|
-
const
|
|
9903
|
+
const result2 = await client.execute({
|
|
9339
9904
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
9340
9905
|
WHERE status = 'needs_review'${scope.sql}`,
|
|
9341
9906
|
args: [...scope.args]
|
|
9342
9907
|
});
|
|
9343
|
-
return Number(
|
|
9908
|
+
return Number(result2.rows[0]?.cnt) || 0;
|
|
9344
9909
|
}
|
|
9345
9910
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
9346
9911
|
const client = getClient();
|
|
9347
9912
|
const scope = strictSessionScopeFilter(
|
|
9348
9913
|
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
9349
9914
|
);
|
|
9350
|
-
const
|
|
9915
|
+
const result2 = await client.execute({
|
|
9351
9916
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
9352
9917
|
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
9353
9918
|
args: [sinceIso, ...scope.args]
|
|
9354
9919
|
});
|
|
9355
|
-
return Number(
|
|
9920
|
+
return Number(result2.rows[0]?.cnt) || 0;
|
|
9356
9921
|
}
|
|
9357
9922
|
async function listPendingReviews(limit, sessionScope) {
|
|
9358
9923
|
const client = getClient();
|
|
9359
9924
|
const scope = strictSessionScopeFilter(
|
|
9360
9925
|
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
9361
9926
|
);
|
|
9362
|
-
const
|
|
9927
|
+
const result2 = await client.execute({
|
|
9363
9928
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
9364
9929
|
WHERE status = 'needs_review'${scope.sql}
|
|
9365
9930
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
9366
9931
|
args: [...scope.args, limit]
|
|
9367
9932
|
});
|
|
9368
|
-
return
|
|
9933
|
+
return result2.rows;
|
|
9369
9934
|
}
|
|
9370
9935
|
async function cleanupOrphanedReviews() {
|
|
9371
9936
|
const client = getClient();
|
|
@@ -9450,7 +10015,7 @@ function getReviewChecklist(role, agent, taskSlug) {
|
|
|
9450
10015
|
]
|
|
9451
10016
|
};
|
|
9452
10017
|
}
|
|
9453
|
-
async function createReviewForCompletedTask(row,
|
|
10018
|
+
async function createReviewForCompletedTask(row, result2, _baseDir, now) {
|
|
9454
10019
|
const taskFile = String(row.task_file);
|
|
9455
10020
|
const employees = await loadEmployees();
|
|
9456
10021
|
const coordinatorName = getCoordinatorName(employees);
|
|
@@ -9495,7 +10060,7 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
|
9495
10060
|
"- **Needs work:** re-open with notes"
|
|
9496
10061
|
].join("\n");
|
|
9497
10062
|
const originalTaskId = String(row.id);
|
|
9498
|
-
const updatedResult = (
|
|
10063
|
+
const updatedResult = (result2 ?? "No result summary provided") + reviewNotes;
|
|
9499
10064
|
await client.execute({
|
|
9500
10065
|
sql: `UPDATE tasks SET result = ?, status = 'needs_review', updated_at = ?
|
|
9501
10066
|
WHERE id = ?`,
|
|
@@ -9517,7 +10082,7 @@ async function createReviewForCompletedTask(row, result, _baseDir, now) {
|
|
|
9517
10082
|
taskFile
|
|
9518
10083
|
});
|
|
9519
10084
|
const originalPriority = String(row.priority).toLowerCase();
|
|
9520
|
-
const autoApprove = originalPriority === "p2" &&
|
|
10085
|
+
const autoApprove = originalPriority === "p2" && result2?.toLowerCase().includes("tests pass");
|
|
9521
10086
|
if (!autoApprove) {
|
|
9522
10087
|
try {
|
|
9523
10088
|
const key = getSessionKey();
|
|
@@ -9546,11 +10111,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
9546
10111
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9547
10112
|
const parentId = row.parent_task_id ? String(row.parent_task_id) : null;
|
|
9548
10113
|
if (parentId) {
|
|
9549
|
-
const
|
|
10114
|
+
const result2 = await client.execute({
|
|
9550
10115
|
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE id = ? AND status = 'needs_review'",
|
|
9551
10116
|
args: [now, parentId]
|
|
9552
10117
|
});
|
|
9553
|
-
if (
|
|
10118
|
+
if (result2.rowsAffected > 0) {
|
|
9554
10119
|
process.stderr.write(
|
|
9555
10120
|
`[review-cleanup] Cascaded original task to done via parent_task_id: ${parentId}
|
|
9556
10121
|
`
|
|
@@ -9564,11 +10129,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
9564
10129
|
const agent = parts[1];
|
|
9565
10130
|
const slug = parts.slice(2).join("-");
|
|
9566
10131
|
const legacyTaskFile = `exe/${agent}/${slug}.md`;
|
|
9567
|
-
const
|
|
10132
|
+
const result2 = await client.execute({
|
|
9568
10133
|
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
|
|
9569
10134
|
args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
|
|
9570
10135
|
});
|
|
9571
|
-
if (
|
|
10136
|
+
if (result2.rowsAffected > 0) {
|
|
9572
10137
|
process.stderr.write(
|
|
9573
10138
|
`[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
|
|
9574
10139
|
`
|
|
@@ -10018,8 +10583,8 @@ function notifyParentExe(sessionKey) {
|
|
|
10018
10583
|
}
|
|
10019
10584
|
process.stderr.write(`[intercom] notifyParentExe \u2192 ${target}
|
|
10020
10585
|
`);
|
|
10021
|
-
const
|
|
10022
|
-
if (
|
|
10586
|
+
const result2 = sendIntercom(target);
|
|
10587
|
+
if (result2 === "failed") {
|
|
10023
10588
|
const rootExe = resolveExeSession();
|
|
10024
10589
|
if (rootExe && rootExe !== target) {
|
|
10025
10590
|
process.stderr.write(`[intercom] notifyParentExe: dispatcher ${target} dead, falling back to root coordinator session ${rootExe}
|
|
@@ -10109,19 +10674,19 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
10109
10674
|
}
|
|
10110
10675
|
const sessionName = employeeSessionName(employeeName, exeSession, effectiveInstance);
|
|
10111
10676
|
if (isEmployeeAlive(sessionName)) {
|
|
10112
|
-
const
|
|
10113
|
-
if (
|
|
10677
|
+
const result3 = sendIntercom(sessionName);
|
|
10678
|
+
if (result3 === "acknowledged" || result3 === "skipped_exe" || result3 === "debounced" || result3 === "queued") {
|
|
10114
10679
|
return { status: "intercom_sent", sessionName };
|
|
10115
10680
|
}
|
|
10116
|
-
if (
|
|
10681
|
+
if (result3 === "delivered") {
|
|
10117
10682
|
return { status: "intercom_unprocessed", sessionName };
|
|
10118
10683
|
}
|
|
10119
10684
|
return { status: "failed", sessionName, error: "intercom delivery failed" };
|
|
10120
10685
|
}
|
|
10121
10686
|
const spawnOpts = { ...opts, instance: effectiveInstance };
|
|
10122
|
-
const
|
|
10123
|
-
if (
|
|
10124
|
-
return { status: "failed", sessionName, error:
|
|
10687
|
+
const result2 = spawnEmployee(employeeName, exeSession, projectDir, spawnOpts);
|
|
10688
|
+
if (result2.error) {
|
|
10689
|
+
return { status: "failed", sessionName, error: result2.error };
|
|
10125
10690
|
}
|
|
10126
10691
|
return { status: "spawned", sessionName };
|
|
10127
10692
|
}
|
|
@@ -10621,11 +11186,11 @@ async function writeCheckpoint(input) {
|
|
|
10621
11186
|
blocked_by_ids: blockedByIds,
|
|
10622
11187
|
last_checkpoint_at: now
|
|
10623
11188
|
};
|
|
10624
|
-
const
|
|
11189
|
+
const result2 = await client.execute({
|
|
10625
11190
|
sql: `UPDATE tasks SET checkpoint = ?, checkpoint_count = checkpoint_count + 1, updated_at = ? WHERE id = ?`,
|
|
10626
11191
|
args: [JSON.stringify(checkpoint), now, taskId]
|
|
10627
11192
|
});
|
|
10628
|
-
if (
|
|
11193
|
+
if (result2.rowsAffected === 0) {
|
|
10629
11194
|
throw new Error(`Checkpoint write failed: task ${taskId} not found`);
|
|
10630
11195
|
}
|
|
10631
11196
|
const countResult = await client.execute({
|
|
@@ -10676,22 +11241,22 @@ function checkLaneAffinity(title, context, assigneeName) {
|
|
|
10676
11241
|
}
|
|
10677
11242
|
async function resolveTask(client, identifier, scopeSession) {
|
|
10678
11243
|
const scope = sessionScopeFilter(scopeSession);
|
|
10679
|
-
let
|
|
11244
|
+
let result2 = await client.execute({
|
|
10680
11245
|
sql: `SELECT * FROM tasks WHERE id = ?${scope.sql}`,
|
|
10681
11246
|
args: [identifier, ...scope.args]
|
|
10682
11247
|
});
|
|
10683
|
-
if (
|
|
10684
|
-
|
|
11248
|
+
if (result2.rows.length === 1) return result2.rows[0];
|
|
11249
|
+
result2 = await client.execute({
|
|
10685
11250
|
sql: `SELECT * FROM tasks WHERE task_file LIKE ?${scope.sql}`,
|
|
10686
11251
|
args: [`%${identifier}%`, ...scope.args]
|
|
10687
11252
|
});
|
|
10688
|
-
if (
|
|
10689
|
-
if (
|
|
10690
|
-
const exact =
|
|
11253
|
+
if (result2.rows.length === 1) return result2.rows[0];
|
|
11254
|
+
if (result2.rows.length > 1) {
|
|
11255
|
+
const exact = result2.rows.filter(
|
|
10691
11256
|
(r) => String(r.task_file).endsWith(`/${identifier}.md`)
|
|
10692
11257
|
);
|
|
10693
11258
|
if (exact.length === 1) return exact[0];
|
|
10694
|
-
const candidates = exact.length > 1 ? exact :
|
|
11259
|
+
const candidates = exact.length > 1 ? exact : result2.rows;
|
|
10695
11260
|
const active = candidates.filter(
|
|
10696
11261
|
(r) => !["done", "cancelled"].includes(String(r.status))
|
|
10697
11262
|
);
|
|
@@ -10701,17 +11266,17 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
10701
11266
|
`Multiple tasks match "${identifier}": ${matches}. Use a UUID to disambiguate.`
|
|
10702
11267
|
);
|
|
10703
11268
|
}
|
|
10704
|
-
|
|
11269
|
+
result2 = await client.execute({
|
|
10705
11270
|
sql: `SELECT * FROM tasks WHERE title LIKE ?${scope.sql}`,
|
|
10706
11271
|
args: [`%${identifier}%`, ...scope.args]
|
|
10707
11272
|
});
|
|
10708
|
-
if (
|
|
10709
|
-
if (
|
|
10710
|
-
const active =
|
|
11273
|
+
if (result2.rows.length === 1) return result2.rows[0];
|
|
11274
|
+
if (result2.rows.length > 1) {
|
|
11275
|
+
const active = result2.rows.filter(
|
|
10711
11276
|
(r) => !["done", "cancelled"].includes(String(r.status))
|
|
10712
11277
|
);
|
|
10713
11278
|
if (active.length === 1) return active[0];
|
|
10714
|
-
const matches = (active.length > 1 ? active :
|
|
11279
|
+
const matches = (active.length > 1 ? active : result2.rows).map((r) => `"${String(r.title)}" (${String(r.status)}, ${String(r.id)})`).join(", ");
|
|
10715
11280
|
throw new Error(
|
|
10716
11281
|
`Multiple tasks match "${identifier}": ${matches}. Use a UUID to disambiguate.`
|
|
10717
11282
|
);
|
|
@@ -10921,11 +11486,11 @@ async function listTasks(input) {
|
|
|
10921
11486
|
args.push(...scope.args);
|
|
10922
11487
|
}
|
|
10923
11488
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
10924
|
-
const
|
|
11489
|
+
const result2 = await client.execute({
|
|
10925
11490
|
sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC LIMIT 1000`,
|
|
10926
11491
|
args
|
|
10927
11492
|
});
|
|
10928
|
-
return
|
|
11493
|
+
return result2.rows.map((r) => ({
|
|
10929
11494
|
id: String(r.id),
|
|
10930
11495
|
title: String(r.title),
|
|
10931
11496
|
assignedTo: String(r.assigned_to),
|
|
@@ -11317,30 +11882,30 @@ async function dispatchTaskToEmployee(input) {
|
|
|
11317
11882
|
if (!exeSession) return { dispatched: "session_missing" };
|
|
11318
11883
|
const sessionName = employeeSessionName(input.assignedTo, exeSession);
|
|
11319
11884
|
if (transport.isAlive(sessionName)) {
|
|
11320
|
-
const
|
|
11321
|
-
const dispatched =
|
|
11885
|
+
const result2 = sendIntercom(sessionName);
|
|
11886
|
+
const dispatched = result2 === "acknowledged" || result2 === "debounced" || result2 === "queued" ? "verified" : result2 === "delivered" ? "sent_unverified" : "session_dead";
|
|
11322
11887
|
process.stderr.write(
|
|
11323
|
-
`[dispatch-audit] intercom \u2192 ${sessionName} | task="${input.title}" [${input.priority}] | result=${dispatched} (sendIntercom=${
|
|
11888
|
+
`[dispatch-audit] intercom \u2192 ${sessionName} | task="${input.title}" [${input.priority}] | result=${dispatched} (sendIntercom=${result2})
|
|
11324
11889
|
`
|
|
11325
11890
|
);
|
|
11326
11891
|
return { dispatched, session: sessionName, crossProject };
|
|
11327
11892
|
} else {
|
|
11328
11893
|
const projectDir = input.projectDir ?? process.cwd();
|
|
11329
|
-
const
|
|
11894
|
+
const result2 = ensureEmployee(input.assignedTo, exeSession, projectDir, {
|
|
11330
11895
|
autoInstance: isMultiInstance(input.assignedTo)
|
|
11331
11896
|
});
|
|
11332
|
-
if (
|
|
11897
|
+
if (result2.status === "failed") {
|
|
11333
11898
|
process.stderr.write(
|
|
11334
|
-
`[dispatch-audit] SPAWN FAILED \u2192 ${input.assignedTo} | task="${input.title}" [${input.priority}] | error=${
|
|
11899
|
+
`[dispatch-audit] SPAWN FAILED \u2192 ${input.assignedTo} | task="${input.title}" [${input.priority}] | error=${result2.error}
|
|
11335
11900
|
`
|
|
11336
11901
|
);
|
|
11337
11902
|
return { dispatched: "session_missing" };
|
|
11338
11903
|
}
|
|
11339
11904
|
process.stderr.write(
|
|
11340
|
-
`[dispatch-audit] SPAWNED \u2192 ${
|
|
11905
|
+
`[dispatch-audit] SPAWNED \u2192 ${result2.sessionName} | task="${input.title}" [${input.priority}]
|
|
11341
11906
|
`
|
|
11342
11907
|
);
|
|
11343
|
-
return { dispatched: "spawned", session:
|
|
11908
|
+
return { dispatched: "spawned", session: result2.sessionName, crossProject };
|
|
11344
11909
|
}
|
|
11345
11910
|
} catch {
|
|
11346
11911
|
return { dispatched: "session_missing" };
|
|
@@ -11398,13 +11963,13 @@ async function storeBehavior(opts) {
|
|
|
11398
11963
|
}
|
|
11399
11964
|
async function listBehaviorsByDomain(agentId, domain) {
|
|
11400
11965
|
const client = getClient();
|
|
11401
|
-
const
|
|
11966
|
+
const result2 = await client.execute({
|
|
11402
11967
|
sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector
|
|
11403
11968
|
FROM behaviors
|
|
11404
11969
|
WHERE agent_id = ? AND domain = ? AND active = 1`,
|
|
11405
11970
|
args: [agentId, domain]
|
|
11406
11971
|
});
|
|
11407
|
-
return
|
|
11972
|
+
return result2.rows.map((r) => ({
|
|
11408
11973
|
id: String(r.id),
|
|
11409
11974
|
agent_id: String(r.agent_id),
|
|
11410
11975
|
project_name: r.project_name ? String(r.project_name) : null,
|
|
@@ -11419,11 +11984,11 @@ async function listBehaviorsByDomain(agentId, domain) {
|
|
|
11419
11984
|
}
|
|
11420
11985
|
async function deactivateBehavior(id) {
|
|
11421
11986
|
const client = getClient();
|
|
11422
|
-
const
|
|
11987
|
+
const result2 = await client.execute({
|
|
11423
11988
|
sql: `UPDATE behaviors SET active = 0, updated_at = ? WHERE id = ? AND active = 1`,
|
|
11424
11989
|
args: [(/* @__PURE__ */ new Date()).toISOString(), id]
|
|
11425
11990
|
});
|
|
11426
|
-
return (
|
|
11991
|
+
return (result2.rowsAffected ?? 0) > 0;
|
|
11427
11992
|
}
|
|
11428
11993
|
var init_behaviors = __esm({
|
|
11429
11994
|
"src/lib/behaviors.ts"() {
|
|
@@ -11448,15 +12013,15 @@ __export(skill_learning_exports, {
|
|
|
11448
12013
|
import crypto9 from "crypto";
|
|
11449
12014
|
async function extractTrajectory(taskId, agentId) {
|
|
11450
12015
|
const client = getClient();
|
|
11451
|
-
const
|
|
12016
|
+
const result2 = await client.execute({
|
|
11452
12017
|
sql: `SELECT tool_name, raw_text
|
|
11453
12018
|
FROM memories
|
|
11454
12019
|
WHERE task_id = ? AND agent_id = ?
|
|
11455
12020
|
ORDER BY timestamp ASC`,
|
|
11456
12021
|
args: [taskId, agentId]
|
|
11457
12022
|
});
|
|
11458
|
-
if (
|
|
11459
|
-
const rawTools =
|
|
12023
|
+
if (result2.rows.length === 0) return [];
|
|
12024
|
+
const rawTools = result2.rows.map((r) => {
|
|
11460
12025
|
const toolName = String(r.tool_name);
|
|
11461
12026
|
if (toolName === "Bash") {
|
|
11462
12027
|
const text3 = String(r.raw_text);
|
|
@@ -11501,7 +12066,7 @@ async function storeTrajectory(opts) {
|
|
|
11501
12066
|
async function findSimilarTrajectories(signature, threshold = DEFAULT_SKILL_THRESHOLD) {
|
|
11502
12067
|
const client = getClient();
|
|
11503
12068
|
const hash = hashSignature(signature);
|
|
11504
|
-
const
|
|
12069
|
+
const result2 = await client.execute({
|
|
11505
12070
|
sql: `SELECT id, task_id, agent_id, project_name, task_title, signature, signature_hash, tool_count, skill_id, created_at
|
|
11506
12071
|
FROM trajectories
|
|
11507
12072
|
WHERE signature_hash = ?
|
|
@@ -11521,7 +12086,7 @@ async function findSimilarTrajectories(signature, threshold = DEFAULT_SKILL_THRE
|
|
|
11521
12086
|
skillId: r.skill_id ? String(r.skill_id) : null,
|
|
11522
12087
|
createdAt: String(r.created_at)
|
|
11523
12088
|
});
|
|
11524
|
-
const matches =
|
|
12089
|
+
const matches = result2.rows.map(mapRow);
|
|
11525
12090
|
if (matches.length >= threshold) return matches;
|
|
11526
12091
|
const nearResult = await client.execute({
|
|
11527
12092
|
sql: `SELECT id, task_id, agent_id, project_name, task_title, signature, signature_hash, tool_count, skill_id, created_at
|
|
@@ -11651,7 +12216,7 @@ async function sweepTrajectories(threshold, model) {
|
|
|
11651
12216
|
if (!config2.skillLearning) return { clustersProcessed: 0, skillsExtracted: 0 };
|
|
11652
12217
|
const t = threshold ?? config2.skillThreshold;
|
|
11653
12218
|
const client = getClient();
|
|
11654
|
-
const
|
|
12219
|
+
const result2 = await client.execute({
|
|
11655
12220
|
sql: `SELECT signature_hash, COUNT(*) as cnt
|
|
11656
12221
|
FROM trajectories
|
|
11657
12222
|
WHERE skill_id IS NULL
|
|
@@ -11663,7 +12228,7 @@ async function sweepTrajectories(threshold, model) {
|
|
|
11663
12228
|
});
|
|
11664
12229
|
let clustersProcessed = 0;
|
|
11665
12230
|
let skillsExtracted = 0;
|
|
11666
|
-
for (const row of
|
|
12231
|
+
for (const row of result2.rows) {
|
|
11667
12232
|
const hash = String(row.signature_hash);
|
|
11668
12233
|
const trajResult = await client.execute({
|
|
11669
12234
|
sql: `SELECT id, task_id, agent_id, project_name, task_title, signature, signature_hash, tool_count, created_at
|
|
@@ -11746,18 +12311,18 @@ __export(tasks_exports, {
|
|
|
11746
12311
|
import path25 from "path";
|
|
11747
12312
|
import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync9, unlinkSync as unlinkSync7 } from "fs";
|
|
11748
12313
|
async function createTask(input) {
|
|
11749
|
-
const
|
|
11750
|
-
if (!input.skipDispatch &&
|
|
12314
|
+
const result2 = await createTaskCore(input);
|
|
12315
|
+
if (!input.skipDispatch && result2.status !== "blocked" && !process.env.VITEST) {
|
|
11751
12316
|
dispatchTaskToEmployee({
|
|
11752
12317
|
assignedTo: input.assignedTo,
|
|
11753
12318
|
title: input.title,
|
|
11754
12319
|
priority: input.priority,
|
|
11755
|
-
taskFile:
|
|
11756
|
-
initialStatus:
|
|
12320
|
+
taskFile: result2.taskFile,
|
|
12321
|
+
initialStatus: result2.status,
|
|
11757
12322
|
projectName: input.projectName
|
|
11758
12323
|
});
|
|
11759
12324
|
}
|
|
11760
|
-
return
|
|
12325
|
+
return result2;
|
|
11761
12326
|
}
|
|
11762
12327
|
async function updateTask(input) {
|
|
11763
12328
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
@@ -11937,7 +12502,7 @@ __export(identity_exports, {
|
|
|
11937
12502
|
import { existsSync as existsSync20, mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync12 } from "fs";
|
|
11938
12503
|
import { readdirSync as readdirSync7 } from "fs";
|
|
11939
12504
|
import path26 from "path";
|
|
11940
|
-
import { createHash as
|
|
12505
|
+
import { createHash as createHash4 } from "crypto";
|
|
11941
12506
|
function ensureDir2() {
|
|
11942
12507
|
if (!existsSync20(IDENTITY_DIR2)) {
|
|
11943
12508
|
mkdirSync10(IDENTITY_DIR2, { recursive: true });
|
|
@@ -11984,7 +12549,7 @@ function parseFrontmatter(raw) {
|
|
|
11984
12549
|
};
|
|
11985
12550
|
}
|
|
11986
12551
|
function contentHash(content) {
|
|
11987
|
-
return
|
|
12552
|
+
return createHash4("sha256").update(content).digest("hex").slice(0, 16);
|
|
11988
12553
|
}
|
|
11989
12554
|
function getIdentity(agentId) {
|
|
11990
12555
|
const filePath = identityPath(agentId);
|
|
@@ -12664,20 +13229,20 @@ async function sendMessage(input) {
|
|
|
12664
13229
|
} catch {
|
|
12665
13230
|
}
|
|
12666
13231
|
const sentScope = strictSessionScopeFilter(sessionScope);
|
|
12667
|
-
const
|
|
13232
|
+
const result2 = await client.execute({
|
|
12668
13233
|
sql: `SELECT * FROM messages WHERE id = ?${sentScope.sql}`,
|
|
12669
13234
|
args: [id, ...sentScope.args]
|
|
12670
13235
|
});
|
|
12671
|
-
return rowToMessage(
|
|
13236
|
+
return rowToMessage(result2.rows[0]);
|
|
12672
13237
|
}
|
|
12673
13238
|
async function deliverCrossMachineMessage(messageId, targetDevice) {
|
|
12674
13239
|
const client = getClient();
|
|
12675
|
-
const
|
|
13240
|
+
const result2 = await client.execute({
|
|
12676
13241
|
sql: "SELECT * FROM messages WHERE id = ?",
|
|
12677
13242
|
args: [messageId]
|
|
12678
13243
|
});
|
|
12679
|
-
if (
|
|
12680
|
-
const msg = rowToMessage(
|
|
13244
|
+
if (result2.rows.length === 0) return false;
|
|
13245
|
+
const msg = rowToMessage(result2.rows[0]);
|
|
12681
13246
|
if (msg.status !== "pending") return false;
|
|
12682
13247
|
if (!_wsClientSend) {
|
|
12683
13248
|
return false;
|
|
@@ -12704,12 +13269,12 @@ async function deliverCrossMachineMessage(messageId, targetDevice) {
|
|
|
12704
13269
|
}
|
|
12705
13270
|
async function deliverLocalMessage(messageId) {
|
|
12706
13271
|
const client = getClient();
|
|
12707
|
-
const
|
|
13272
|
+
const result2 = await client.execute({
|
|
12708
13273
|
sql: "SELECT * FROM messages WHERE id = ?",
|
|
12709
13274
|
args: [messageId]
|
|
12710
13275
|
});
|
|
12711
|
-
if (
|
|
12712
|
-
const msg = rowToMessage(
|
|
13276
|
+
if (result2.rows.length === 0) return false;
|
|
13277
|
+
const msg = rowToMessage(result2.rows[0]);
|
|
12713
13278
|
if (msg.status !== "pending") return false;
|
|
12714
13279
|
const targetAgent = msg.targetAgent;
|
|
12715
13280
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -13036,7 +13601,7 @@ __export(db_backup_exports, {
|
|
|
13036
13601
|
listBackups: () => listBackups,
|
|
13037
13602
|
rotateBackups: () => rotateBackups
|
|
13038
13603
|
});
|
|
13039
|
-
import { copyFileSync, existsSync as existsSync27, mkdirSync as mkdirSync13, readdirSync as readdirSync10, unlinkSync as unlinkSync9, statSync as
|
|
13604
|
+
import { copyFileSync, existsSync as existsSync27, mkdirSync as mkdirSync13, readdirSync as readdirSync10, unlinkSync as unlinkSync9, statSync as statSync5 } from "fs";
|
|
13040
13605
|
import path32 from "path";
|
|
13041
13606
|
function findActiveDb() {
|
|
13042
13607
|
for (const name of DB_NAMES) {
|
|
@@ -13080,7 +13645,7 @@ function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
|
|
|
13080
13645
|
if (!file.endsWith(".db") && !file.endsWith(".db-wal") && !file.endsWith(".db-shm")) continue;
|
|
13081
13646
|
const filePath = path32.join(BACKUP_DIR, file);
|
|
13082
13647
|
try {
|
|
13083
|
-
const stat =
|
|
13648
|
+
const stat = statSync5(filePath);
|
|
13084
13649
|
if (stat.mtimeMs < cutoff) {
|
|
13085
13650
|
unlinkSync9(filePath);
|
|
13086
13651
|
deleted++;
|
|
@@ -13098,7 +13663,7 @@ function listBackups() {
|
|
|
13098
13663
|
const files = readdirSync10(BACKUP_DIR).filter((f) => f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm"));
|
|
13099
13664
|
return files.map((name) => {
|
|
13100
13665
|
const p = path32.join(BACKUP_DIR, name);
|
|
13101
|
-
const stat =
|
|
13666
|
+
const stat = statSync5(p);
|
|
13102
13667
|
return { path: p, name, size: stat.size, date: stat.mtime };
|
|
13103
13668
|
}).sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
13104
13669
|
} catch {
|
|
@@ -13317,10 +13882,10 @@ function isCrdtSyncEnabled() {
|
|
|
13317
13882
|
async function rebuildFromDb() {
|
|
13318
13883
|
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
13319
13884
|
const client = getClient2();
|
|
13320
|
-
const
|
|
13885
|
+
const result2 = await client.execute(
|
|
13321
13886
|
"SELECT id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, author_device_id, scope FROM memories"
|
|
13322
13887
|
);
|
|
13323
|
-
const memories =
|
|
13888
|
+
const memories = result2.rows.map((row) => ({
|
|
13324
13889
|
id: String(row.id),
|
|
13325
13890
|
agent_id: row.agent_id,
|
|
13326
13891
|
agent_role: row.agent_role,
|
|
@@ -13361,8 +13926,8 @@ init_database();
|
|
|
13361
13926
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13362
13927
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13363
13928
|
import { spawn as spawn4 } from "child_process";
|
|
13364
|
-
import { existsSync as
|
|
13365
|
-
import
|
|
13929
|
+
import { existsSync as existsSync38, openSync as openSync3, mkdirSync as mkdirSync21, closeSync as closeSync3, readFileSync as readFileSync31 } from "fs";
|
|
13930
|
+
import path49 from "path";
|
|
13366
13931
|
import os20 from "os";
|
|
13367
13932
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
13368
13933
|
|
|
@@ -13915,7 +14480,7 @@ async function searchConversations(query, limit) {
|
|
|
13915
14480
|
try {
|
|
13916
14481
|
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
13917
14482
|
const client = getClient2();
|
|
13918
|
-
const
|
|
14483
|
+
const result2 = await client.execute({
|
|
13919
14484
|
sql: `SELECT c.id, c.platform, c.sender_id, c.sender_name,
|
|
13920
14485
|
c.content_text, c.agent_response, c.agent_name, c.timestamp
|
|
13921
14486
|
FROM conversations c
|
|
@@ -13924,9 +14489,9 @@ async function searchConversations(query, limit) {
|
|
|
13924
14489
|
LIMIT ?`,
|
|
13925
14490
|
args: [query, limit]
|
|
13926
14491
|
});
|
|
13927
|
-
return
|
|
14492
|
+
return result2.rows.map((row, i) => ({
|
|
13928
14493
|
source: "conversations",
|
|
13929
|
-
score: 1 - i / Math.max(
|
|
14494
|
+
score: 1 - i / Math.max(result2.rows.length, 1),
|
|
13930
14495
|
timestamp: row.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
13931
14496
|
snippet: truncate(
|
|
13932
14497
|
(row.content_text ?? "") + (row.agent_response ? ` \u2192 ${row.agent_response}` : ""),
|
|
@@ -14138,7 +14703,7 @@ function registerGetSessionContext(server2) {
|
|
|
14138
14703
|
},
|
|
14139
14704
|
async ({ session_id, target_timestamp, window_size, max_chars, body_chars }) => {
|
|
14140
14705
|
const client = getClient();
|
|
14141
|
-
const
|
|
14706
|
+
const result2 = await client.execute({
|
|
14142
14707
|
sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
14143
14708
|
tool_name, project_name,
|
|
14144
14709
|
has_error, raw_text, vector, task_id
|
|
@@ -14148,7 +14713,7 @@ function registerGetSessionContext(server2) {
|
|
|
14148
14713
|
LIMIT 500`,
|
|
14149
14714
|
args: [session_id]
|
|
14150
14715
|
});
|
|
14151
|
-
if (
|
|
14716
|
+
if (result2.rows.length === 0) {
|
|
14152
14717
|
return {
|
|
14153
14718
|
content: [
|
|
14154
14719
|
{
|
|
@@ -14158,7 +14723,7 @@ function registerGetSessionContext(server2) {
|
|
|
14158
14723
|
]
|
|
14159
14724
|
};
|
|
14160
14725
|
}
|
|
14161
|
-
const sorted =
|
|
14726
|
+
const sorted = result2.rows.map((row) => ({
|
|
14162
14727
|
id: row.id,
|
|
14163
14728
|
agent_id: row.agent_id,
|
|
14164
14729
|
agent_role: row.agent_role,
|
|
@@ -14225,7 +14790,7 @@ function registerGetMemoryById(server2) {
|
|
|
14225
14790
|
const canReadOtherAgent = agentRole === "COO" || agentRole === "CTO";
|
|
14226
14791
|
const ownerAgent = requestedAgent && canReadOtherAgent ? requestedAgent : agentId;
|
|
14227
14792
|
const client = getClient();
|
|
14228
|
-
const
|
|
14793
|
+
const result2 = await client.execute({
|
|
14229
14794
|
sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
14230
14795
|
tool_name, project_name, has_error, raw_text, task_id,
|
|
14231
14796
|
importance, status, memory_type, source_path, source_type
|
|
@@ -14234,14 +14799,14 @@ function registerGetMemoryById(server2) {
|
|
|
14234
14799
|
LIMIT 1`,
|
|
14235
14800
|
args: [id, ownerAgent]
|
|
14236
14801
|
});
|
|
14237
|
-
if (
|
|
14802
|
+
if (result2.rows.length === 0) {
|
|
14238
14803
|
const extra = requestedAgent && !canReadOtherAgent ? " Non-COO/CTO agents may only fetch their own memories." : "";
|
|
14239
14804
|
return {
|
|
14240
14805
|
content: [{ type: "text", text: `No memory found for id '${id}'.${extra}` }],
|
|
14241
14806
|
isError: true
|
|
14242
14807
|
};
|
|
14243
14808
|
}
|
|
14244
|
-
const row =
|
|
14809
|
+
const row = result2.rows[0];
|
|
14245
14810
|
const lines = [
|
|
14246
14811
|
`id: ${asString(row.id)}`,
|
|
14247
14812
|
`agent_id: ${asString(row.agent_id)}`,
|
|
@@ -14301,12 +14866,12 @@ function registerConsolidateMemories(server2) {
|
|
|
14301
14866
|
embedFn = embed2;
|
|
14302
14867
|
} catch {
|
|
14303
14868
|
}
|
|
14304
|
-
const
|
|
14869
|
+
const result2 = await runConsolidation2(client, {
|
|
14305
14870
|
model: consolidationModel,
|
|
14306
14871
|
maxCalls: max_clusters,
|
|
14307
14872
|
embedFn
|
|
14308
14873
|
});
|
|
14309
|
-
if (
|
|
14874
|
+
if (result2.clustersProcessed === 0) {
|
|
14310
14875
|
return {
|
|
14311
14876
|
content: [{
|
|
14312
14877
|
type: "text",
|
|
@@ -14318,8 +14883,8 @@ function registerConsolidateMemories(server2) {
|
|
|
14318
14883
|
content: [{
|
|
14319
14884
|
type: "text",
|
|
14320
14885
|
text: `Consolidation complete:
|
|
14321
|
-
- Clusters processed: ${
|
|
14322
|
-
- Memories consolidated: ${
|
|
14886
|
+
- Clusters processed: ${result2.clustersProcessed}
|
|
14887
|
+
- Memories consolidated: ${result2.memoriesConsolidated}
|
|
14323
14888
|
|
|
14324
14889
|
Consolidated summaries stored as tier-1 (importance=9) memories.`
|
|
14325
14890
|
}]
|
|
@@ -14748,10 +15313,10 @@ function registerCreateTask(server2) {
|
|
|
14748
15313
|
skipDispatch: true
|
|
14749
15314
|
});
|
|
14750
15315
|
try {
|
|
14751
|
-
const { existsSync:
|
|
15316
|
+
const { existsSync: existsSync39, mkdirSync: mkdirSync22, writeFileSync: writeFileSync23 } = await import("fs");
|
|
14752
15317
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
14753
15318
|
const idPath = identityPath2(assigned_to);
|
|
14754
|
-
if (!
|
|
15319
|
+
if (!existsSync39(idPath)) {
|
|
14755
15320
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
14756
15321
|
const employees = await loadEmployees2();
|
|
14757
15322
|
const emp = employees.find((e) => e.name === assigned_to);
|
|
@@ -14760,8 +15325,8 @@ function registerCreateTask(server2) {
|
|
|
14760
15325
|
const template = getTemplateForTitle2(emp.role);
|
|
14761
15326
|
if (template) {
|
|
14762
15327
|
const dir = (await import("path")).dirname(idPath);
|
|
14763
|
-
if (!
|
|
14764
|
-
|
|
15328
|
+
if (!existsSync39(dir)) mkdirSync22(dir, { recursive: true });
|
|
15329
|
+
writeFileSync23(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
|
|
14765
15330
|
}
|
|
14766
15331
|
}
|
|
14767
15332
|
}
|
|
@@ -14776,22 +15341,22 @@ function registerCreateTask(server2) {
|
|
|
14776
15341
|
const useAutoInstance = isMultiInstance2(assigned_to);
|
|
14777
15342
|
const { loadConfigSync: loadConfigSync2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
14778
15343
|
const cfg = loadConfigSync2();
|
|
14779
|
-
const
|
|
15344
|
+
const result2 = ensureEmployee(assigned_to, exeSession, process.cwd(), {
|
|
14780
15345
|
autoInstance: useAutoInstance,
|
|
14781
15346
|
maxAutoInstances: useAutoInstance ? cfg.sessionLifecycle.maxAutoInstances : void 0
|
|
14782
15347
|
});
|
|
14783
|
-
switch (
|
|
15348
|
+
switch (result2.status) {
|
|
14784
15349
|
case "intercom_sent":
|
|
14785
15350
|
dispatchStatus = `
|
|
14786
|
-
Dispatched: intercom sent to ${
|
|
15351
|
+
Dispatched: intercom sent to ${result2.sessionName}`;
|
|
14787
15352
|
break;
|
|
14788
15353
|
case "spawned":
|
|
14789
15354
|
dispatchStatus = `
|
|
14790
|
-
Dispatched: spawned ${
|
|
15355
|
+
Dispatched: spawned ${result2.sessionName}`;
|
|
14791
15356
|
break;
|
|
14792
15357
|
case "failed":
|
|
14793
15358
|
dispatchStatus = `
|
|
14794
|
-
Dispatch failed: ${
|
|
15359
|
+
Dispatch failed: ${result2.error ?? "unknown"}`;
|
|
14795
15360
|
break;
|
|
14796
15361
|
}
|
|
14797
15362
|
}
|
|
@@ -14909,7 +15474,7 @@ async function diagnoseMissingTask(client, identifier) {
|
|
|
14909
15474
|
args.push(`%${identifier}%`);
|
|
14910
15475
|
clauses.push("title LIKE ?");
|
|
14911
15476
|
args.push(`%${identifier}%`);
|
|
14912
|
-
const
|
|
15477
|
+
const result2 = await client.execute({
|
|
14913
15478
|
sql: `SELECT id, title, status, assigned_to, project_name, session_scope, task_file
|
|
14914
15479
|
FROM tasks
|
|
14915
15480
|
WHERE ${clauses.map((c) => `(${c})`).join(" OR ")}
|
|
@@ -14917,10 +15482,10 @@ async function diagnoseMissingTask(client, identifier) {
|
|
|
14917
15482
|
LIMIT 10`,
|
|
14918
15483
|
args
|
|
14919
15484
|
});
|
|
14920
|
-
if (
|
|
15485
|
+
if (result2.rows.length === 0) return null;
|
|
14921
15486
|
const scoped = sessionScopeFilter();
|
|
14922
15487
|
const scopeNote = scoped.args.length > 0 ? `Current session scope appears to be ${String(scoped.args[0])}. get_task is session-scoped by default.` : "No current session scope detected.";
|
|
14923
|
-
const matches =
|
|
15488
|
+
const matches = result2.rows.map((r) => {
|
|
14924
15489
|
const id = String(r.id);
|
|
14925
15490
|
return `- ${id.slice(0, 8)} ${String(r.title)} [${String(r.status)}] assigned_to=${String(r.assigned_to)} project=${String(r.project_name)} session_scope=${String(r.session_scope ?? "NULL")}`;
|
|
14926
15491
|
}).join("\n");
|
|
@@ -15062,7 +15627,7 @@ function registerUpdateTask(server2) {
|
|
|
15062
15627
|
result: z15.string().optional().describe("Result summary (include when status=done)")
|
|
15063
15628
|
}
|
|
15064
15629
|
},
|
|
15065
|
-
async ({ task_id, status: rawStatus, result }) => {
|
|
15630
|
+
async ({ task_id, status: rawStatus, result: result2 }) => {
|
|
15066
15631
|
let status = rawStatus;
|
|
15067
15632
|
if (status === "done") {
|
|
15068
15633
|
try {
|
|
@@ -15129,7 +15694,7 @@ function registerUpdateTask(server2) {
|
|
|
15129
15694
|
task = await updateTask({
|
|
15130
15695
|
taskId: task_id,
|
|
15131
15696
|
status,
|
|
15132
|
-
result,
|
|
15697
|
+
result: result2,
|
|
15133
15698
|
baseDir: process.cwd(),
|
|
15134
15699
|
callerAgentId
|
|
15135
15700
|
});
|
|
@@ -15205,7 +15770,7 @@ function registerCloseTask(server2) {
|
|
|
15205
15770
|
status: z16.enum(["closed", "done", "blocked", "cancelled"]).optional().default("closed").describe("Completion status (default: closed \u2014 terminal archive state)")
|
|
15206
15771
|
}
|
|
15207
15772
|
},
|
|
15208
|
-
async ({ task_id, result, status }) => {
|
|
15773
|
+
async ({ task_id, result: result2, status }) => {
|
|
15209
15774
|
const agent = getActiveAgent();
|
|
15210
15775
|
const canClose = canCoordinate(agent.agentId, agent.agentRole) || CLOSE_TASK_ALLOWED_ROLES.has(agent.agentRole ?? "");
|
|
15211
15776
|
if (agent.agentId && !canClose) {
|
|
@@ -15243,7 +15808,7 @@ function registerCloseTask(server2) {
|
|
|
15243
15808
|
const task = await updateTask({
|
|
15244
15809
|
taskId: task_id,
|
|
15245
15810
|
status,
|
|
15246
|
-
result,
|
|
15811
|
+
result: result2,
|
|
15247
15812
|
baseDir,
|
|
15248
15813
|
skipReviewCreation: true
|
|
15249
15814
|
});
|
|
@@ -15613,7 +16178,7 @@ function registerAcknowledgeMessages(server2) {
|
|
|
15613
16178
|
const agentId = agent.agentId || "default";
|
|
15614
16179
|
const client = getClient();
|
|
15615
16180
|
const scope = strictSessionScopeFilter();
|
|
15616
|
-
const
|
|
16181
|
+
const result2 = await client.execute({
|
|
15617
16182
|
sql: `UPDATE messages SET status = 'acknowledged', processed_at = datetime('now')
|
|
15618
16183
|
WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}`,
|
|
15619
16184
|
args: [agentId, ...scope.args]
|
|
@@ -15622,7 +16187,7 @@ function registerAcknowledgeMessages(server2) {
|
|
|
15622
16187
|
content: [
|
|
15623
16188
|
{
|
|
15624
16189
|
type: "text",
|
|
15625
|
-
text: `Acknowledged ${
|
|
16190
|
+
text: `Acknowledged ${result2.rowsAffected} message(s) for ${agentId}.`
|
|
15626
16191
|
}
|
|
15627
16192
|
]
|
|
15628
16193
|
};
|
|
@@ -15689,8 +16254,8 @@ async function createReminder(text3, dueDate) {
|
|
|
15689
16254
|
async function listReminders(includeCompleted = false) {
|
|
15690
16255
|
const client = getClient();
|
|
15691
16256
|
const sql = includeCompleted ? `SELECT id, text, created_at, due_date, completed_at FROM reminders ORDER BY due_date ASC NULLS LAST LIMIT 500` : `SELECT id, text, created_at, due_date, completed_at FROM reminders WHERE completed_at IS NULL ORDER BY due_date ASC NULLS LAST LIMIT 500`;
|
|
15692
|
-
const
|
|
15693
|
-
return
|
|
16257
|
+
const result2 = await client.execute(sql);
|
|
16258
|
+
return result2.rows.map((row) => ({
|
|
15694
16259
|
id: String(row.id),
|
|
15695
16260
|
text: String(row.text),
|
|
15696
16261
|
createdAt: String(row.created_at),
|
|
@@ -15701,18 +16266,18 @@ async function listReminders(includeCompleted = false) {
|
|
|
15701
16266
|
async function completeReminder(idOrText) {
|
|
15702
16267
|
const client = getClient();
|
|
15703
16268
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15704
|
-
let
|
|
16269
|
+
let result2 = await client.execute({
|
|
15705
16270
|
sql: `SELECT id, text FROM reminders WHERE id = ? AND completed_at IS NULL`,
|
|
15706
16271
|
args: [idOrText]
|
|
15707
16272
|
});
|
|
15708
|
-
if (
|
|
15709
|
-
|
|
16273
|
+
if (result2.rows.length === 0) {
|
|
16274
|
+
result2 = await client.execute({
|
|
15710
16275
|
sql: `SELECT id, text FROM reminders WHERE completed_at IS NULL AND text LIKE '%' || ? || '%' LIMIT 1`,
|
|
15711
16276
|
args: [idOrText]
|
|
15712
16277
|
});
|
|
15713
16278
|
}
|
|
15714
|
-
if (
|
|
15715
|
-
const row =
|
|
16279
|
+
if (result2.rows.length === 0) return null;
|
|
16280
|
+
const row = result2.rows[0];
|
|
15716
16281
|
const id = String(row.id);
|
|
15717
16282
|
await client.execute({
|
|
15718
16283
|
sql: `UPDATE reminders SET completed_at = ? WHERE id = ?`,
|
|
@@ -15854,7 +16419,7 @@ function registerListBehaviors(server2) {
|
|
|
15854
16419
|
args.push(proj);
|
|
15855
16420
|
}
|
|
15856
16421
|
const where = conditions.join(" AND ");
|
|
15857
|
-
const
|
|
16422
|
+
const result2 = await client.execute({
|
|
15858
16423
|
sql: `SELECT id, agent_id, project_name, domain, content, active, created_at, updated_at
|
|
15859
16424
|
FROM behaviors
|
|
15860
16425
|
WHERE ${where}
|
|
@@ -15862,7 +16427,7 @@ function registerListBehaviors(server2) {
|
|
|
15862
16427
|
LIMIT 50`,
|
|
15863
16428
|
args
|
|
15864
16429
|
});
|
|
15865
|
-
const behaviors =
|
|
16430
|
+
const behaviors = result2.rows.map((r) => rowToBehavior(r));
|
|
15866
16431
|
if (behaviors.length === 0) {
|
|
15867
16432
|
return {
|
|
15868
16433
|
content: [{ type: "text", text: "No behaviors found." }]
|
|
@@ -16059,11 +16624,11 @@ function registerDeactivateBehavior(server2) {
|
|
|
16059
16624
|
};
|
|
16060
16625
|
}
|
|
16061
16626
|
const client = getClient();
|
|
16062
|
-
const
|
|
16627
|
+
const result2 = await client.execute({
|
|
16063
16628
|
sql: `SELECT id, agent_id, content, domain, priority FROM behaviors WHERE id = ?`,
|
|
16064
16629
|
args: [behavior_id]
|
|
16065
16630
|
});
|
|
16066
|
-
if (
|
|
16631
|
+
if (result2.rows.length === 0) {
|
|
16067
16632
|
return {
|
|
16068
16633
|
content: [{
|
|
16069
16634
|
type: "text",
|
|
@@ -16072,7 +16637,7 @@ function registerDeactivateBehavior(server2) {
|
|
|
16072
16637
|
isError: true
|
|
16073
16638
|
};
|
|
16074
16639
|
}
|
|
16075
|
-
const row =
|
|
16640
|
+
const row = result2.rows[0];
|
|
16076
16641
|
const wasActive = await deactivateBehavior(behavior_id);
|
|
16077
16642
|
if (!wasActive) {
|
|
16078
16643
|
return {
|
|
@@ -16186,7 +16751,7 @@ async function embedTexts(texts) {
|
|
|
16186
16751
|
);
|
|
16187
16752
|
results.push(
|
|
16188
16753
|
...settled.map(
|
|
16189
|
-
(
|
|
16754
|
+
(result2) => result2.status === "fulfilled" ? result2.value : null
|
|
16190
16755
|
)
|
|
16191
16756
|
);
|
|
16192
16757
|
}
|
|
@@ -16304,7 +16869,7 @@ var MIN_IMPORTANCE = 1;
|
|
|
16304
16869
|
var MAX_IMPORTANCE = 10;
|
|
16305
16870
|
async function listDocumentsByWorkspace(workspace_id, limit = DEFAULT_LIST_LIMIT, offset = 0) {
|
|
16306
16871
|
const client = getClient();
|
|
16307
|
-
const
|
|
16872
|
+
const result2 = await client.execute({
|
|
16308
16873
|
sql: `SELECT d.*, COUNT(m.id) AS chunk_count
|
|
16309
16874
|
FROM documents d
|
|
16310
16875
|
LEFT JOIN memories m ON m.document_id = d.id
|
|
@@ -16314,7 +16879,7 @@ async function listDocumentsByWorkspace(workspace_id, limit = DEFAULT_LIST_LIMIT
|
|
|
16314
16879
|
LIMIT ? OFFSET ?`,
|
|
16315
16880
|
args: [workspace_id, limit, offset]
|
|
16316
16881
|
});
|
|
16317
|
-
return
|
|
16882
|
+
return result2.rows.map((row) => {
|
|
16318
16883
|
const r = row;
|
|
16319
16884
|
return {
|
|
16320
16885
|
...rowToDocument(r),
|
|
@@ -16346,11 +16911,11 @@ async function updateChunksImportance(document_id, importance) {
|
|
|
16346
16911
|
);
|
|
16347
16912
|
}
|
|
16348
16913
|
const client = getClient();
|
|
16349
|
-
const
|
|
16914
|
+
const result2 = await client.execute({
|
|
16350
16915
|
sql: "UPDATE memories SET importance = ? WHERE document_id = ?",
|
|
16351
16916
|
args: [importance, document_id]
|
|
16352
16917
|
});
|
|
16353
|
-
return Number(
|
|
16918
|
+
return Number(result2.rowsAffected) || 0;
|
|
16354
16919
|
}
|
|
16355
16920
|
|
|
16356
16921
|
// src/mcp/tools/ingest-document.ts
|
|
@@ -16381,11 +16946,11 @@ function registerIngestDocument(server2) {
|
|
|
16381
16946
|
const { assertMemoryLimit: assertMemoryLimit2 } = await Promise.resolve().then(() => (init_plan_limits(), plan_limits_exports));
|
|
16382
16947
|
await assertFeature2("wiki");
|
|
16383
16948
|
await assertMemoryLimit2();
|
|
16384
|
-
const
|
|
16949
|
+
const result2 = await ingestDocument(input);
|
|
16385
16950
|
return {
|
|
16386
16951
|
content: [{
|
|
16387
16952
|
type: "text",
|
|
16388
|
-
text: JSON.stringify(
|
|
16953
|
+
text: JSON.stringify(result2)
|
|
16389
16954
|
}]
|
|
16390
16955
|
};
|
|
16391
16956
|
} catch (err) {
|
|
@@ -17046,7 +17611,7 @@ function registerQueryConversations(server2) {
|
|
|
17046
17611
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
17047
17612
|
const limit = params.limit ?? 25;
|
|
17048
17613
|
args.push(limit);
|
|
17049
|
-
const
|
|
17614
|
+
const result2 = await client.execute({
|
|
17050
17615
|
sql: `SELECT c.id, c.platform, c.sender_id, c.sender_name, c.sender_phone,
|
|
17051
17616
|
c.sender_email, c.channel_id, c.thread_id, c.content_text,
|
|
17052
17617
|
c.agent_response, c.agent_name, c.timestamp
|
|
@@ -17056,7 +17621,7 @@ function registerQueryConversations(server2) {
|
|
|
17056
17621
|
LIMIT ?`,
|
|
17057
17622
|
args
|
|
17058
17623
|
});
|
|
17059
|
-
const conversations =
|
|
17624
|
+
const conversations = result2.rows.map((row) => ({
|
|
17060
17625
|
id: row.id,
|
|
17061
17626
|
platform: row.platform,
|
|
17062
17627
|
sender: {
|
|
@@ -17668,8 +18233,8 @@ async function generateGraphReport(client, projectName) {
|
|
|
17668
18233
|
}).slice(0, 10);
|
|
17669
18234
|
let hyperedgeCount = 0;
|
|
17670
18235
|
try {
|
|
17671
|
-
const
|
|
17672
|
-
hyperedgeCount = Number(
|
|
18236
|
+
const result2 = await client.execute("SELECT COUNT(*) as cnt FROM hyperedges");
|
|
18237
|
+
hyperedgeCount = Number(result2.rows[0]?.cnt ?? 0);
|
|
17673
18238
|
} catch {
|
|
17674
18239
|
}
|
|
17675
18240
|
const nodeLabels = new Map(nodes.map((n) => [n.id, n.label]));
|
|
@@ -17723,12 +18288,12 @@ function registerExportGraph(server2) {
|
|
|
17723
18288
|
}
|
|
17724
18289
|
const html = await exportGraphHTML(client);
|
|
17725
18290
|
const fs = await import("fs");
|
|
17726
|
-
const
|
|
18291
|
+
const path50 = await import("path");
|
|
17727
18292
|
const os21 = await import("os");
|
|
17728
|
-
const outDir =
|
|
18293
|
+
const outDir = path50.join(os21.homedir(), ".exe-os", "exports");
|
|
17729
18294
|
fs.mkdirSync(outDir, { recursive: true });
|
|
17730
18295
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
17731
|
-
const filePath =
|
|
18296
|
+
const filePath = path50.join(outDir, `graph-${timestamp}.html`);
|
|
17732
18297
|
fs.writeFileSync(filePath, html, "utf-8");
|
|
17733
18298
|
return {
|
|
17734
18299
|
content: [
|
|
@@ -17766,6 +18331,198 @@ import crypto13 from "crypto";
|
|
|
17766
18331
|
|
|
17767
18332
|
// src/lib/code-chunker.ts
|
|
17768
18333
|
import ts from "typescript";
|
|
18334
|
+
function chunkSourceFile(source, fileName = "file.ts") {
|
|
18335
|
+
const sourceFile = ts.createSourceFile(
|
|
18336
|
+
fileName,
|
|
18337
|
+
source,
|
|
18338
|
+
ts.ScriptTarget.Latest,
|
|
18339
|
+
true,
|
|
18340
|
+
// setParentNodes
|
|
18341
|
+
fileName.endsWith(".tsx") || fileName.endsWith(".jsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS
|
|
18342
|
+
);
|
|
18343
|
+
const chunks = [];
|
|
18344
|
+
const lines = source.split("\n");
|
|
18345
|
+
const importLines = [];
|
|
18346
|
+
function getLineNumber(pos) {
|
|
18347
|
+
return sourceFile.getLineAndCharacterOfPosition(pos).line + 1;
|
|
18348
|
+
}
|
|
18349
|
+
function getLeadingComment(node) {
|
|
18350
|
+
const fullText = sourceFile.getFullText();
|
|
18351
|
+
const ranges = ts.getLeadingCommentRanges(fullText, node.getFullStart());
|
|
18352
|
+
if (!ranges || ranges.length === 0) return void 0;
|
|
18353
|
+
return ranges.map((r) => fullText.slice(r.pos, r.end)).join("\n");
|
|
18354
|
+
}
|
|
18355
|
+
function getNodeText(node) {
|
|
18356
|
+
return node.getText(sourceFile);
|
|
18357
|
+
}
|
|
18358
|
+
function getName(node) {
|
|
18359
|
+
if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
|
|
18360
|
+
return node.name?.getText(sourceFile) ?? "(anonymous)";
|
|
18361
|
+
}
|
|
18362
|
+
if (ts.isClassDeclaration(node)) {
|
|
18363
|
+
return node.name?.getText(sourceFile) ?? "(anonymous class)";
|
|
18364
|
+
}
|
|
18365
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
18366
|
+
return node.name.getText(sourceFile);
|
|
18367
|
+
}
|
|
18368
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
18369
|
+
return node.name.getText(sourceFile);
|
|
18370
|
+
}
|
|
18371
|
+
if (ts.isEnumDeclaration(node)) {
|
|
18372
|
+
return node.name.getText(sourceFile);
|
|
18373
|
+
}
|
|
18374
|
+
if (ts.isVariableStatement(node)) {
|
|
18375
|
+
const decls = node.declarationList.declarations;
|
|
18376
|
+
return decls.map((d) => d.name.getText(sourceFile)).join(", ");
|
|
18377
|
+
}
|
|
18378
|
+
if (ts.isExportAssignment(node)) {
|
|
18379
|
+
return "default export";
|
|
18380
|
+
}
|
|
18381
|
+
return "(unknown)";
|
|
18382
|
+
}
|
|
18383
|
+
function visitTopLevel(node) {
|
|
18384
|
+
if (ts.isImportDeclaration(node)) {
|
|
18385
|
+
importLines.push({
|
|
18386
|
+
start: getLineNumber(node.getStart(sourceFile)),
|
|
18387
|
+
end: getLineNumber(node.getEnd())
|
|
18388
|
+
});
|
|
18389
|
+
return;
|
|
18390
|
+
}
|
|
18391
|
+
if (ts.isFunctionDeclaration(node)) {
|
|
18392
|
+
chunks.push({
|
|
18393
|
+
kind: "function",
|
|
18394
|
+
name: getName(node),
|
|
18395
|
+
text: getNodeText(node),
|
|
18396
|
+
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18397
|
+
endLine: getLineNumber(node.getEnd()),
|
|
18398
|
+
comment: getLeadingComment(node)
|
|
18399
|
+
});
|
|
18400
|
+
return;
|
|
18401
|
+
}
|
|
18402
|
+
if (ts.isClassDeclaration(node)) {
|
|
18403
|
+
chunks.push({
|
|
18404
|
+
kind: "class",
|
|
18405
|
+
name: getName(node),
|
|
18406
|
+
text: getNodeText(node),
|
|
18407
|
+
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18408
|
+
endLine: getLineNumber(node.getEnd()),
|
|
18409
|
+
comment: getLeadingComment(node)
|
|
18410
|
+
});
|
|
18411
|
+
return;
|
|
18412
|
+
}
|
|
18413
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
18414
|
+
chunks.push({
|
|
18415
|
+
kind: "type",
|
|
18416
|
+
name: getName(node),
|
|
18417
|
+
text: getNodeText(node),
|
|
18418
|
+
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18419
|
+
endLine: getLineNumber(node.getEnd()),
|
|
18420
|
+
comment: getLeadingComment(node)
|
|
18421
|
+
});
|
|
18422
|
+
return;
|
|
18423
|
+
}
|
|
18424
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
18425
|
+
chunks.push({
|
|
18426
|
+
kind: "type",
|
|
18427
|
+
name: getName(node),
|
|
18428
|
+
text: getNodeText(node),
|
|
18429
|
+
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18430
|
+
endLine: getLineNumber(node.getEnd()),
|
|
18431
|
+
comment: getLeadingComment(node)
|
|
18432
|
+
});
|
|
18433
|
+
return;
|
|
18434
|
+
}
|
|
18435
|
+
if (ts.isEnumDeclaration(node)) {
|
|
18436
|
+
chunks.push({
|
|
18437
|
+
kind: "type",
|
|
18438
|
+
name: getName(node),
|
|
18439
|
+
text: getNodeText(node),
|
|
18440
|
+
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18441
|
+
endLine: getLineNumber(node.getEnd()),
|
|
18442
|
+
comment: getLeadingComment(node)
|
|
18443
|
+
});
|
|
18444
|
+
return;
|
|
18445
|
+
}
|
|
18446
|
+
if (ts.isVariableStatement(node)) {
|
|
18447
|
+
const decls = node.declarationList.declarations;
|
|
18448
|
+
const isFnLike = decls.some(
|
|
18449
|
+
(d) => d.initializer && (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer))
|
|
18450
|
+
);
|
|
18451
|
+
chunks.push({
|
|
18452
|
+
kind: isFnLike ? "function" : "variable",
|
|
18453
|
+
name: getName(node),
|
|
18454
|
+
text: getNodeText(node),
|
|
18455
|
+
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18456
|
+
endLine: getLineNumber(node.getEnd()),
|
|
18457
|
+
comment: getLeadingComment(node)
|
|
18458
|
+
});
|
|
18459
|
+
return;
|
|
18460
|
+
}
|
|
18461
|
+
if (ts.isExportAssignment(node)) {
|
|
18462
|
+
chunks.push({
|
|
18463
|
+
kind: "export",
|
|
18464
|
+
name: "default export",
|
|
18465
|
+
text: getNodeText(node),
|
|
18466
|
+
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18467
|
+
endLine: getLineNumber(node.getEnd()),
|
|
18468
|
+
comment: getLeadingComment(node)
|
|
18469
|
+
});
|
|
18470
|
+
return;
|
|
18471
|
+
}
|
|
18472
|
+
if (ts.isExpressionStatement(node)) {
|
|
18473
|
+
const text3 = getNodeText(node);
|
|
18474
|
+
if (text3.length > 10) {
|
|
18475
|
+
chunks.push({
|
|
18476
|
+
kind: "other",
|
|
18477
|
+
name: text3.slice(0, 40).replace(/\n/g, " "),
|
|
18478
|
+
text: text3,
|
|
18479
|
+
startLine: getLineNumber(node.getStart(sourceFile)),
|
|
18480
|
+
endLine: getLineNumber(node.getEnd())
|
|
18481
|
+
});
|
|
18482
|
+
}
|
|
18483
|
+
return;
|
|
18484
|
+
}
|
|
18485
|
+
}
|
|
18486
|
+
sourceFile.statements.forEach(visitTopLevel);
|
|
18487
|
+
if (importLines.length > 0) {
|
|
18488
|
+
const startLine = importLines[0].start;
|
|
18489
|
+
const endLine = importLines[importLines.length - 1].end;
|
|
18490
|
+
const importText = lines.slice(startLine - 1, endLine).join("\n");
|
|
18491
|
+
chunks.unshift({
|
|
18492
|
+
kind: "import",
|
|
18493
|
+
name: `${importLines.length} imports`,
|
|
18494
|
+
text: importText,
|
|
18495
|
+
startLine,
|
|
18496
|
+
endLine
|
|
18497
|
+
});
|
|
18498
|
+
}
|
|
18499
|
+
chunks.sort((a, b) => a.startLine - b.startLine);
|
|
18500
|
+
return chunks;
|
|
18501
|
+
}
|
|
18502
|
+
function summarizeChunk(chunk, filePath) {
|
|
18503
|
+
const location = `${filePath}:${chunk.startLine}-${chunk.endLine}`;
|
|
18504
|
+
const comment = chunk.comment ? chunk.comment.replace(/\/\*\*|\*\/|\*\s?/g, "").trim().split("\n")[0] : "";
|
|
18505
|
+
switch (chunk.kind) {
|
|
18506
|
+
case "function":
|
|
18507
|
+
return `Function ${chunk.name} in ${location}${comment ? ` \u2014 ${comment}` : ""}`;
|
|
18508
|
+
case "class":
|
|
18509
|
+
return `Class ${chunk.name} in ${location}${comment ? ` \u2014 ${comment}` : ""}`;
|
|
18510
|
+
case "type":
|
|
18511
|
+
return `Type ${chunk.name} in ${location}`;
|
|
18512
|
+
case "import":
|
|
18513
|
+
return `Imports (${chunk.name}) in ${filePath}`;
|
|
18514
|
+
case "variable":
|
|
18515
|
+
return `Variable ${chunk.name} in ${location}`;
|
|
18516
|
+
case "export":
|
|
18517
|
+
return `Default export in ${location}`;
|
|
18518
|
+
default:
|
|
18519
|
+
return `${chunk.kind} in ${location}`;
|
|
18520
|
+
}
|
|
18521
|
+
}
|
|
18522
|
+
function isChunkable(filePath) {
|
|
18523
|
+
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
18524
|
+
return ext === "ts" || ext === "tsx" || ext === "js" || ext === "jsx";
|
|
18525
|
+
}
|
|
17769
18526
|
|
|
17770
18527
|
// src/lib/graph-rag.ts
|
|
17771
18528
|
function normalizeEntityName(name) {
|
|
@@ -17869,14 +18626,14 @@ function registerMergeEntities(server2) {
|
|
|
17869
18626
|
};
|
|
17870
18627
|
}
|
|
17871
18628
|
try {
|
|
17872
|
-
const
|
|
18629
|
+
const result2 = await mergeEntities(client, sourceId, targetId);
|
|
17873
18630
|
return {
|
|
17874
18631
|
content: [
|
|
17875
18632
|
{
|
|
17876
18633
|
type: "text",
|
|
17877
18634
|
text: `Merged "${source_name}" \u2192 "${target_name}":
|
|
17878
|
-
- ${
|
|
17879
|
-
- ${
|
|
18635
|
+
- ${result2.relationshipsMoved} relationships moved
|
|
18636
|
+
- ${result2.memoriesMoved} memory links moved
|
|
17880
18637
|
- "${source_name}" registered as alias for "${target_name}"`
|
|
17881
18638
|
}
|
|
17882
18639
|
]
|
|
@@ -18159,10 +18916,10 @@ function registerSetAgentConfig(server2) {
|
|
|
18159
18916
|
isError: true
|
|
18160
18917
|
};
|
|
18161
18918
|
}
|
|
18162
|
-
const
|
|
18163
|
-
if (!
|
|
18919
|
+
const result2 = setAgentRuntime(agent_id, runtime, model, reasoning_effort);
|
|
18920
|
+
if (!result2.ok) {
|
|
18164
18921
|
return {
|
|
18165
|
-
content: [{ type: "text", text:
|
|
18922
|
+
content: [{ type: "text", text: result2.error }],
|
|
18166
18923
|
isError: true
|
|
18167
18924
|
};
|
|
18168
18925
|
}
|
|
@@ -18339,7 +19096,7 @@ async function getAgentSpend(period = "7d") {
|
|
|
18339
19096
|
}
|
|
18340
19097
|
}
|
|
18341
19098
|
}
|
|
18342
|
-
const
|
|
19099
|
+
const result2 = Array.from(agentTotals.entries()).map(([agentId, t]) => ({
|
|
18343
19100
|
agentId,
|
|
18344
19101
|
inputTokens: t.input,
|
|
18345
19102
|
outputTokens: t.output,
|
|
@@ -18349,8 +19106,8 @@ async function getAgentSpend(period = "7d") {
|
|
|
18349
19106
|
sessions: t.sessions.size,
|
|
18350
19107
|
period
|
|
18351
19108
|
})).sort((a, b) => b.costUSD - a.costUSD);
|
|
18352
|
-
_spendCache.set(period, { result, expires: Date.now() + CACHE_TTL_MS });
|
|
18353
|
-
return
|
|
19109
|
+
_spendCache.set(period, { result: result2, expires: Date.now() + CACHE_TTL_MS });
|
|
19110
|
+
return result2;
|
|
18354
19111
|
}
|
|
18355
19112
|
async function extractSessionUsage(jsonlPath) {
|
|
18356
19113
|
let input = 0;
|
|
@@ -18914,14 +19671,14 @@ async function fetchMemoriesWithVectors(client, projectFilter, agentFilter2) {
|
|
|
18914
19671
|
args.push(agentFilter2);
|
|
18915
19672
|
}
|
|
18916
19673
|
const where = " WHERE " + clauses.join(" AND ");
|
|
18917
|
-
const
|
|
19674
|
+
const result2 = await client.execute({
|
|
18918
19675
|
sql: `SELECT id, agent_id, raw_text, timestamp, project_name, vector
|
|
18919
19676
|
FROM memories${where}
|
|
18920
19677
|
ORDER BY project_name, timestamp DESC`,
|
|
18921
19678
|
args
|
|
18922
19679
|
});
|
|
18923
19680
|
const byProject = /* @__PURE__ */ new Map();
|
|
18924
|
-
for (const row of
|
|
19681
|
+
for (const row of result2.rows) {
|
|
18925
19682
|
const project = row.project_name;
|
|
18926
19683
|
const vec = row.vector == null ? null : Array.isArray(row.vector) ? row.vector : Array.from(row.vector);
|
|
18927
19684
|
if (!vec || vec.length === 0) continue;
|
|
@@ -19338,11 +20095,11 @@ async function auditStats(client, flags) {
|
|
|
19338
20095
|
async function auditNullVectors(client, flags) {
|
|
19339
20096
|
const { clause, args } = agentFilter(flags);
|
|
19340
20097
|
const where = clause ? clause + " AND vector IS NULL" : " WHERE vector IS NULL";
|
|
19341
|
-
const
|
|
20098
|
+
const result2 = await client.execute({
|
|
19342
20099
|
sql: `SELECT COUNT(*) as cnt FROM memories${where}`,
|
|
19343
20100
|
args
|
|
19344
20101
|
});
|
|
19345
|
-
return Number(
|
|
20102
|
+
return Number(result2.rows[0].cnt);
|
|
19346
20103
|
}
|
|
19347
20104
|
async function auditDuplicates(client, flags) {
|
|
19348
20105
|
const { clause, args } = agentFilter(flags);
|
|
@@ -19391,14 +20148,14 @@ async function auditDuplicates(client, flags) {
|
|
|
19391
20148
|
async function auditBloated(client, flags) {
|
|
19392
20149
|
const { clause, args } = agentFilter(flags);
|
|
19393
20150
|
const where = clause ? clause + " AND LENGTH(raw_text) > 5120 AND tool_name != 'ConversationBackfill'" : " WHERE LENGTH(raw_text) > 5120 AND tool_name != 'ConversationBackfill'";
|
|
19394
|
-
const
|
|
20151
|
+
const result2 = await client.execute({
|
|
19395
20152
|
sql: `SELECT id, agent_id, LENGTH(raw_text) as size, tool_name
|
|
19396
20153
|
FROM memories${where}
|
|
19397
20154
|
ORDER BY size DESC
|
|
19398
20155
|
LIMIT 100`,
|
|
19399
20156
|
args
|
|
19400
20157
|
});
|
|
19401
|
-
return
|
|
20158
|
+
return result2.rows.map((r) => ({
|
|
19402
20159
|
id: r.id,
|
|
19403
20160
|
agent_id: r.agent_id,
|
|
19404
20161
|
size: Number(r.size),
|
|
@@ -19413,7 +20170,7 @@ async function auditFts(client) {
|
|
|
19413
20170
|
return { memoryCount: mc, ftsCount: fc, inSync: mc === fc };
|
|
19414
20171
|
}
|
|
19415
20172
|
async function auditOrphanedProjects(client) {
|
|
19416
|
-
const
|
|
20173
|
+
const result2 = await client.execute(
|
|
19417
20174
|
`SELECT project_name, COUNT(*) as cnt
|
|
19418
20175
|
FROM memories
|
|
19419
20176
|
WHERE tool_name != 'ConversationBackfill'
|
|
@@ -19422,7 +20179,7 @@ async function auditOrphanedProjects(client) {
|
|
|
19422
20179
|
);
|
|
19423
20180
|
const orphans = [];
|
|
19424
20181
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
19425
|
-
for (const row of
|
|
20182
|
+
for (const row of result2.rows) {
|
|
19426
20183
|
const name = row.project_name;
|
|
19427
20184
|
const count = Number(row.cnt);
|
|
19428
20185
|
const exists = existsSync28(path33.join(home, name)) || existsSync28(path33.join(home, "..", name)) || existsSync28(path33.join(process.cwd(), "..", name));
|
|
@@ -19701,7 +20458,7 @@ function formatReport(report, flags) {
|
|
|
19701
20458
|
if (!kh.masterKeyPresent) {
|
|
19702
20459
|
lines.push("\u{1F534} Recovery key: master key missing \u2014 import phrase or run setup before syncing");
|
|
19703
20460
|
} else if (!kh.recoveryBackupMarked) {
|
|
19704
|
-
lines.push("\u{1F534} Recovery backup: not confirmed \u2014 run `exe-os link
|
|
20461
|
+
lines.push("\u{1F534} Recovery backup: not confirmed \u2014 run `exe-os cloud link --show-full` in your local Terminal and save the 24-word phrase");
|
|
19705
20462
|
} else {
|
|
19706
20463
|
const suffix = kh.recoveryBackupConfirmedAt ? ` (${kh.recoveryBackupConfirmedAt.slice(0, 10)}${kh.recoveryBackupSource ? ` via ${kh.recoveryBackupSource}` : ""})` : "";
|
|
19707
20464
|
lines.push(`\u{1F7E2} Recovery backup: confirmed${suffix}`);
|
|
@@ -19873,12 +20630,12 @@ async function fixBloated(client, bloated, dryRun) {
|
|
|
19873
20630
|
let chunksCreated = 0;
|
|
19874
20631
|
const CHUNK_SIZE = 2048;
|
|
19875
20632
|
for (const b of bloated) {
|
|
19876
|
-
const
|
|
20633
|
+
const result2 = await client.execute({
|
|
19877
20634
|
sql: `SELECT * FROM memories WHERE id = ?`,
|
|
19878
20635
|
args: [b.id]
|
|
19879
20636
|
});
|
|
19880
|
-
if (
|
|
19881
|
-
const row =
|
|
20637
|
+
if (result2.rows.length === 0) continue;
|
|
20638
|
+
const row = result2.rows[0];
|
|
19882
20639
|
const text3 = row.raw_text;
|
|
19883
20640
|
const chunks = splitAtSentences(text3, CHUNK_SIZE);
|
|
19884
20641
|
if (chunks.length <= 1) continue;
|
|
@@ -20112,15 +20869,15 @@ function registerRunConsolidation(server2) {
|
|
|
20112
20869
|
};
|
|
20113
20870
|
}
|
|
20114
20871
|
const config2 = await loadConfig();
|
|
20115
|
-
const
|
|
20872
|
+
const result2 = await runConsolidation(client, {
|
|
20116
20873
|
model: config2.consolidationModel || "claude-haiku-4-5-20251001",
|
|
20117
20874
|
maxCalls: 10
|
|
20118
20875
|
});
|
|
20119
20876
|
const lines = [];
|
|
20120
20877
|
lines.push("## Consolidation Complete\n");
|
|
20121
|
-
lines.push(`- **Clusters processed:** ${
|
|
20122
|
-
lines.push(`- **Memories consolidated:** ${
|
|
20123
|
-
lines.push(`- **Remaining unconsolidated:** ${(pending -
|
|
20878
|
+
lines.push(`- **Clusters processed:** ${result2.clustersProcessed}`);
|
|
20879
|
+
lines.push(`- **Memories consolidated:** ${result2.memoriesConsolidated}`);
|
|
20880
|
+
lines.push(`- **Remaining unconsolidated:** ${(pending - result2.memoriesConsolidated).toLocaleString()}`);
|
|
20124
20881
|
return {
|
|
20125
20882
|
content: [{ type: "text", text: lines.join("\n") }]
|
|
20126
20883
|
};
|
|
@@ -20143,7 +20900,7 @@ import { z as z58 } from "zod";
|
|
|
20143
20900
|
|
|
20144
20901
|
// src/lib/cloud-sync.ts
|
|
20145
20902
|
init_database();
|
|
20146
|
-
import { readFileSync as readFileSync23, writeFileSync as writeFileSync17, existsSync as existsSync30, readdirSync as readdirSync11, mkdirSync as mkdirSync15, appendFileSync as appendFileSync2, unlinkSync as unlinkSync11, openSync as openSync2, closeSync as closeSync2, statSync as
|
|
20903
|
+
import { readFileSync as readFileSync23, writeFileSync as writeFileSync17, existsSync as existsSync30, readdirSync as readdirSync11, mkdirSync as mkdirSync15, appendFileSync as appendFileSync2, unlinkSync as unlinkSync11, openSync as openSync2, closeSync as closeSync2, statSync as statSync6 } from "fs";
|
|
20147
20904
|
import crypto15 from "crypto";
|
|
20148
20905
|
import path35 from "path";
|
|
20149
20906
|
import { homedir as homedir6 } from "os";
|
|
@@ -20265,18 +21022,36 @@ function loadPgClient() {
|
|
|
20265
21022
|
const { pathToFileURL: pathToFileURL6 } = await import("url");
|
|
20266
21023
|
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
20267
21024
|
if (explicitPath) {
|
|
20268
|
-
const
|
|
20269
|
-
const
|
|
20270
|
-
if (!
|
|
20271
|
-
return new
|
|
21025
|
+
const mod = await import(pathToFileURL6(explicitPath).href);
|
|
21026
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
21027
|
+
if (!Ctor) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
21028
|
+
return new Ctor();
|
|
20272
21029
|
}
|
|
20273
21030
|
const exeDbRoot = process.env.EXE_DB_ROOT ?? path35.join(homedir6(), "exe-db");
|
|
20274
|
-
const
|
|
20275
|
-
|
|
20276
|
-
|
|
20277
|
-
|
|
20278
|
-
|
|
20279
|
-
|
|
21031
|
+
const packagePath = path35.join(exeDbRoot, "package.json");
|
|
21032
|
+
if (existsSync30(packagePath)) {
|
|
21033
|
+
const req = createRequire6(packagePath);
|
|
21034
|
+
const entry = req.resolve("@prisma/client");
|
|
21035
|
+
const mod = await import(pathToFileURL6(entry).href);
|
|
21036
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
21037
|
+
if (!Ctor) throw new Error("No PrismaClient");
|
|
21038
|
+
return new Ctor();
|
|
21039
|
+
}
|
|
21040
|
+
const { Pool } = await import("pg");
|
|
21041
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
21042
|
+
return {
|
|
21043
|
+
async $queryRawUnsafe(query, ...values) {
|
|
21044
|
+
const result2 = await pool.query(query, values);
|
|
21045
|
+
return result2.rows;
|
|
21046
|
+
},
|
|
21047
|
+
async $executeRawUnsafe(query, ...values) {
|
|
21048
|
+
const result2 = await pool.query(query, values);
|
|
21049
|
+
return result2.rowCount ?? 0;
|
|
21050
|
+
},
|
|
21051
|
+
async $disconnect() {
|
|
21052
|
+
await pool.end();
|
|
21053
|
+
}
|
|
21054
|
+
};
|
|
20280
21055
|
})().catch(() => {
|
|
20281
21056
|
_pgFailed = true;
|
|
20282
21057
|
_pgPromise = null;
|
|
@@ -20297,7 +21072,7 @@ async function pushToPostgres(records) {
|
|
|
20297
21072
|
let inserted = 0;
|
|
20298
21073
|
for (const rec of records) {
|
|
20299
21074
|
try {
|
|
20300
|
-
await prisma.$executeRawUnsafe(
|
|
21075
|
+
const changed = await prisma.$executeRawUnsafe(
|
|
20301
21076
|
`INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
|
|
20302
21077
|
VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
|
|
20303
21078
|
ON CONFLICT (source, source_id, event_type) DO NOTHING`,
|
|
@@ -20306,7 +21081,7 @@ async function pushToPostgres(records) {
|
|
|
20306
21081
|
JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
|
|
20307
21082
|
rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
|
|
20308
21083
|
);
|
|
20309
|
-
inserted
|
|
21084
|
+
inserted += Number(changed ?? 0);
|
|
20310
21085
|
} catch {
|
|
20311
21086
|
}
|
|
20312
21087
|
}
|
|
@@ -20430,32 +21205,62 @@ async function cloudPull(sinceVersion, config2) {
|
|
|
20430
21205
|
if (!response.ok) return { records: [], maxVersion: sinceVersion };
|
|
20431
21206
|
const data = await response.json();
|
|
20432
21207
|
const allRecords = [];
|
|
20433
|
-
|
|
21208
|
+
let maxReadableVersion = sinceVersion;
|
|
21209
|
+
let skippedBlobs = 0;
|
|
21210
|
+
for (const { version, blob } of data.blobs ?? []) {
|
|
20434
21211
|
try {
|
|
20435
21212
|
const compressed = decryptSyncBlob(blob);
|
|
20436
21213
|
const json = decompress(compressed).toString("utf8");
|
|
20437
21214
|
const records = JSON.parse(json);
|
|
20438
21215
|
allRecords.push(...records);
|
|
21216
|
+
const recordMax = records.reduce((max, rec) => {
|
|
21217
|
+
const v = Number(rec.version ?? 0);
|
|
21218
|
+
return Number.isFinite(v) ? Math.max(max, v) : max;
|
|
21219
|
+
}, 0);
|
|
21220
|
+
const blobVersion = Number(version ?? 0);
|
|
21221
|
+
maxReadableVersion = Math.max(
|
|
21222
|
+
maxReadableVersion,
|
|
21223
|
+
Number.isFinite(blobVersion) ? blobVersion : 0,
|
|
21224
|
+
recordMax
|
|
21225
|
+
);
|
|
20439
21226
|
} catch {
|
|
21227
|
+
skippedBlobs++;
|
|
20440
21228
|
continue;
|
|
20441
21229
|
}
|
|
20442
21230
|
}
|
|
20443
|
-
|
|
21231
|
+
if (skippedBlobs > 0) {
|
|
21232
|
+
logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); pull cursor advanced only to last readable version ${maxReadableVersion}`);
|
|
21233
|
+
}
|
|
21234
|
+
return { records: allRecords, maxVersion: maxReadableVersion };
|
|
20444
21235
|
} catch (err) {
|
|
20445
21236
|
logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
|
|
20446
21237
|
return { records: [], maxVersion: sinceVersion };
|
|
20447
21238
|
}
|
|
20448
21239
|
}
|
|
20449
|
-
var
|
|
20450
|
-
async function
|
|
21240
|
+
var CLOUD_REUPLOAD_REQUIRED_MESSAGE = "Cloud sync is blocked because this device rotated its memory encryption key. Run `exe-os cloud reupload` first to re-upload the cloud backup with the new key.";
|
|
21241
|
+
async function getCloudReuploadRequired(client = getClient()) {
|
|
20451
21242
|
try {
|
|
20452
21243
|
await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
|
|
20453
|
-
const
|
|
20454
|
-
return String(
|
|
21244
|
+
const result2 = await client.execute("SELECT key, value FROM sync_meta WHERE key IN ('cloud_reupload_required', 'cloud_relink_required')");
|
|
21245
|
+
return result2.rows.some((row) => String(row.value ?? "") === "1");
|
|
20455
21246
|
} catch {
|
|
20456
21247
|
return false;
|
|
20457
21248
|
}
|
|
20458
21249
|
}
|
|
21250
|
+
async function clearCloudReuploadRequired(client = getClient()) {
|
|
21251
|
+
await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
|
|
21252
|
+
await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '0')");
|
|
21253
|
+
await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_relink_required', '0')");
|
|
21254
|
+
await client.execute({
|
|
21255
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reuploaded_at', ?)",
|
|
21256
|
+
args: [(/* @__PURE__ */ new Date()).toISOString()]
|
|
21257
|
+
});
|
|
21258
|
+
await client.execute("DELETE FROM sync_meta WHERE key IN ('last_cloud_pull_version', 'last_cloud_push_version')");
|
|
21259
|
+
}
|
|
21260
|
+
async function markCloudReuploadRequired(client = getClient()) {
|
|
21261
|
+
await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
|
|
21262
|
+
await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1')");
|
|
21263
|
+
}
|
|
20459
21264
|
async function cloudSync(config2) {
|
|
20460
21265
|
if (!isSyncCryptoInitialized()) {
|
|
20461
21266
|
try {
|
|
@@ -20477,10 +21282,10 @@ async function cloudSync(config2) {
|
|
|
20477
21282
|
throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
|
|
20478
21283
|
}
|
|
20479
21284
|
try {
|
|
20480
|
-
if (await
|
|
21285
|
+
if (await getCloudReuploadRequired(client)) throw new Error(CLOUD_REUPLOAD_REQUIRED_MESSAGE);
|
|
20481
21286
|
} catch (err) {
|
|
20482
21287
|
const msg = err instanceof Error ? err.message : String(err);
|
|
20483
|
-
if (msg.includes("
|
|
21288
|
+
if (msg === CLOUD_REUPLOAD_REQUIRED_MESSAGE || msg.includes("key rotation")) throw err;
|
|
20484
21289
|
}
|
|
20485
21290
|
try {
|
|
20486
21291
|
const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
@@ -20738,7 +21543,7 @@ async function cloudSync(config2) {
|
|
|
20738
21543
|
const { getLatestBackup: getLatestBackup2 } = await Promise.resolve().then(() => (init_db_backup(), db_backup_exports));
|
|
20739
21544
|
const latestBackup = getLatestBackup2();
|
|
20740
21545
|
if (latestBackup) {
|
|
20741
|
-
const backupSize =
|
|
21546
|
+
const backupSize = statSync6(latestBackup).size;
|
|
20742
21547
|
const MAX_CLOUD_BACKUP_BYTES = 50 * 1024 * 1024;
|
|
20743
21548
|
if (backupSize <= MAX_CLOUD_BACKUP_BYTES) {
|
|
20744
21549
|
const backupData = readFileSync23(latestBackup);
|
|
@@ -21048,8 +21853,8 @@ async function cloudPullBlob(route, config2) {
|
|
|
21048
21853
|
}
|
|
21049
21854
|
async function cloudPushGlobalProcedures(config2) {
|
|
21050
21855
|
const client = getClient();
|
|
21051
|
-
const
|
|
21052
|
-
const rows =
|
|
21856
|
+
const result2 = await client.execute("SELECT * FROM company_procedures LIMIT 1000");
|
|
21857
|
+
const rows = result2.rows;
|
|
21053
21858
|
const { ok } = await cloudPushBlob(
|
|
21054
21859
|
"/sync/push-global-procedures",
|
|
21055
21860
|
rows,
|
|
@@ -21093,8 +21898,8 @@ async function cloudPullGlobalProcedures(config2) {
|
|
|
21093
21898
|
}
|
|
21094
21899
|
async function cloudPushBehaviors(config2) {
|
|
21095
21900
|
const client = getClient();
|
|
21096
|
-
const
|
|
21097
|
-
const rows =
|
|
21901
|
+
const result2 = await client.execute("SELECT * FROM behaviors LIMIT 10000");
|
|
21902
|
+
const rows = result2.rows;
|
|
21098
21903
|
const { ok } = await cloudPushBlob(
|
|
21099
21904
|
"/sync/push-behaviors",
|
|
21100
21905
|
rows,
|
|
@@ -21249,8 +22054,8 @@ async function cloudPullGraphRAG(config2) {
|
|
|
21249
22054
|
}
|
|
21250
22055
|
async function cloudPushTasks(config2) {
|
|
21251
22056
|
const client = getClient();
|
|
21252
|
-
const
|
|
21253
|
-
const rows =
|
|
22057
|
+
const result2 = await client.execute("SELECT * FROM tasks LIMIT 10000");
|
|
22058
|
+
const rows = result2.rows;
|
|
21254
22059
|
const { ok } = await cloudPushBlob(
|
|
21255
22060
|
"/sync/push-tasks",
|
|
21256
22061
|
rows,
|
|
@@ -21295,8 +22100,8 @@ async function cloudPullTasks(config2) {
|
|
|
21295
22100
|
}
|
|
21296
22101
|
async function cloudPushConversations(config2) {
|
|
21297
22102
|
const client = getClient();
|
|
21298
|
-
const
|
|
21299
|
-
const rows =
|
|
22103
|
+
const result2 = await client.execute("SELECT * FROM conversations LIMIT 50000");
|
|
22104
|
+
const rows = result2.rows;
|
|
21300
22105
|
const { ok } = await cloudPushBlob(
|
|
21301
22106
|
"/sync/push-conversations",
|
|
21302
22107
|
rows,
|
|
@@ -21402,31 +22207,138 @@ async function cloudPullDocuments(config2) {
|
|
|
21402
22207
|
|
|
21403
22208
|
// src/mcp/tools/cloud-sync.ts
|
|
21404
22209
|
init_config();
|
|
22210
|
+
init_keychain();
|
|
22211
|
+
init_store();
|
|
22212
|
+
init_database();
|
|
22213
|
+
function result(text3, isError = false) {
|
|
22214
|
+
return { content: [{ type: "text", text: text3 }], ...isError ? { isError: true } : {} };
|
|
22215
|
+
}
|
|
22216
|
+
function shortKeyFingerprint(key) {
|
|
22217
|
+
if (!key) return "missing";
|
|
22218
|
+
return `${key.toString("hex").slice(0, 8)}...`;
|
|
22219
|
+
}
|
|
22220
|
+
async function cloudStatusText() {
|
|
22221
|
+
const config2 = await loadConfig();
|
|
22222
|
+
const key = await getMasterKey();
|
|
22223
|
+
const cloud = config2.cloud;
|
|
22224
|
+
let reuploadRequired = false;
|
|
22225
|
+
let localMemoryVersion = 0;
|
|
22226
|
+
let lastPushedMemoryVersion = 0;
|
|
22227
|
+
let lastPullVersion = 0;
|
|
22228
|
+
let pendingMemoryCount = 0;
|
|
22229
|
+
let lastReuploadedAt = null;
|
|
22230
|
+
try {
|
|
22231
|
+
await initStore({ lightweight: true });
|
|
22232
|
+
const client = getClient();
|
|
22233
|
+
reuploadRequired = await getCloudReuploadRequired(client);
|
|
22234
|
+
const maxVersion = await client.execute("SELECT COALESCE(MAX(version), 0) as version FROM memories WHERE scope IS NULL OR scope != 'personal'");
|
|
22235
|
+
localMemoryVersion = Number(maxVersion.rows[0]?.version ?? 0);
|
|
22236
|
+
const meta = await client.execute("SELECT key, value FROM sync_meta WHERE key IN ('last_cloud_push_version', 'last_cloud_pull_version', 'cloud_reuploaded_at', 'cloud_relinked_at')");
|
|
22237
|
+
for (const row of meta.rows) {
|
|
22238
|
+
const keyName = String(row.key ?? "");
|
|
22239
|
+
const value = String(row.value ?? "");
|
|
22240
|
+
if (keyName === "last_cloud_push_version") lastPushedMemoryVersion = Number(value || 0);
|
|
22241
|
+
if (keyName === "last_cloud_pull_version") lastPullVersion = Number(value || 0);
|
|
22242
|
+
if (keyName === "cloud_reuploaded_at" || keyName === "cloud_relinked_at") lastReuploadedAt = value;
|
|
22243
|
+
}
|
|
22244
|
+
const pending = await client.execute({
|
|
22245
|
+
sql: "SELECT COUNT(*) as cnt FROM memories WHERE (scope IS NULL OR scope != 'personal') AND version > ?",
|
|
22246
|
+
args: [lastPushedMemoryVersion]
|
|
22247
|
+
});
|
|
22248
|
+
pendingMemoryCount = Number(pending.rows[0]?.cnt ?? 0);
|
|
22249
|
+
} catch (err) {
|
|
22250
|
+
pendingMemoryCount = -1;
|
|
22251
|
+
}
|
|
22252
|
+
const lines = [];
|
|
22253
|
+
lines.push("## Exe Cloud Status");
|
|
22254
|
+
lines.push("");
|
|
22255
|
+
lines.push(`- Encryption key: ${key ? `present (${shortKeyFingerprint(key)})` : "missing"}`);
|
|
22256
|
+
if (cloud?.apiKey && cloud?.endpoint) {
|
|
22257
|
+
lines.push(`- Cloud account: connected (${cloud.apiKey.slice(0, 10)}...${cloud.apiKey.slice(-4)})`);
|
|
22258
|
+
lines.push(`- Endpoint: ${cloud.endpoint}`);
|
|
22259
|
+
} else {
|
|
22260
|
+
lines.push("- Cloud account: not configured");
|
|
22261
|
+
}
|
|
22262
|
+
lines.push("- Status check: read-only; no data was uploaded or downloaded");
|
|
22263
|
+
lines.push("");
|
|
22264
|
+
if (!cloud?.apiKey || !cloud?.endpoint) {
|
|
22265
|
+
lines.push("**Sync status:** NOT CONFIGURED");
|
|
22266
|
+
lines.push("Run `exe-os cloud` in Terminal to set up Exe Cloud.");
|
|
22267
|
+
} else if (reuploadRequired) {
|
|
22268
|
+
lines.push("**Sync status:** NOT SYNCING \u2014 blocked for safety");
|
|
22269
|
+
lines.push("**Why:** this device changed its memory encryption key.");
|
|
22270
|
+
lines.push('**Required fix:** run MCP `cloud_sync` with `action="reupload"` and confirmation, or run `exe-os cloud reupload` in Terminal.');
|
|
22271
|
+
lines.push("After re-upload, automatic background sync will resume.");
|
|
22272
|
+
} else if (pendingMemoryCount > 0) {
|
|
22273
|
+
lines.push("**Sync status:** SYNC ENABLED \u2014 waiting for background sync");
|
|
22274
|
+
lines.push(`**Pending upload:** ${pendingMemoryCount.toLocaleString()} local memor${pendingMemoryCount === 1 ? "y" : "ies"}`);
|
|
22275
|
+
lines.push('To sync now, run MCP `cloud_sync` with `action="sync"` or Terminal `exe-os cloud sync`.');
|
|
22276
|
+
} else {
|
|
22277
|
+
lines.push("**Sync status:** SYNC ENABLED \u2014 no obvious memory backlog");
|
|
22278
|
+
}
|
|
22279
|
+
lines.push("");
|
|
22280
|
+
lines.push(`- Memory cursor: local=${localMemoryVersion} last pushed=${lastPushedMemoryVersion} last pulled=${lastPullVersion}`);
|
|
22281
|
+
if (lastReuploadedAt) lines.push(`- Last re-upload: ${lastReuploadedAt}`);
|
|
22282
|
+
lines.push("");
|
|
22283
|
+
lines.push("Terminal-only safety rule: full recovery phrases are never returned by MCP. To reveal the phrase, the user must run `exe-os cloud link --show-full` directly in their local Terminal.");
|
|
22284
|
+
return lines.join("\n");
|
|
22285
|
+
}
|
|
21405
22286
|
function registerCloudSync(server2) {
|
|
21406
22287
|
server2.registerTool(
|
|
21407
22288
|
"cloud_sync",
|
|
21408
22289
|
{
|
|
21409
22290
|
title: "Cloud Sync",
|
|
21410
|
-
description: "
|
|
22291
|
+
description: "Consolidated Exe Cloud tool. Supports read-only status, normal sync-now, and explicitly confirmed post-key-rotation re-upload. Does not reveal recovery phrases and does not rotate keys.",
|
|
21411
22292
|
inputSchema: {
|
|
21412
|
-
|
|
22293
|
+
action: z58.enum(["status", "sync", "reupload"]).default("sync").describe("Cloud operation. status is read-only; sync is normal sync-now; reupload repairs cloud after key rotation."),
|
|
22294
|
+
force: z58.boolean().default(false).describe("Reserved for sync compatibility; normal cloud sync already runs when requested."),
|
|
22295
|
+
confirm_local_db_source_of_truth: z58.string().optional().describe('Required for action=reupload. Must exactly equal "LOCAL DB IS SOURCE OF TRUTH".')
|
|
21413
22296
|
}
|
|
21414
22297
|
},
|
|
21415
|
-
async () => {
|
|
22298
|
+
async ({ action = "sync", confirm_local_db_source_of_truth }) => {
|
|
21416
22299
|
try {
|
|
22300
|
+
if (action === "status") {
|
|
22301
|
+
return result(await cloudStatusText());
|
|
22302
|
+
}
|
|
21417
22303
|
const config2 = await loadConfig();
|
|
21418
22304
|
const cloud = config2.cloud;
|
|
21419
22305
|
if (!cloud?.apiKey || !cloud?.endpoint) {
|
|
21420
|
-
return
|
|
21421
|
-
|
|
21422
|
-
|
|
21423
|
-
|
|
21424
|
-
|
|
21425
|
-
|
|
21426
|
-
|
|
21427
|
-
|
|
22306
|
+
return result("Cloud sync not configured. Run `exe-os cloud` in Terminal to set up Exe Cloud.", true);
|
|
22307
|
+
}
|
|
22308
|
+
if (action === "reupload") {
|
|
22309
|
+
await initStore({ lightweight: true });
|
|
22310
|
+
const client = getClient();
|
|
22311
|
+
const required = await getCloudReuploadRequired(client);
|
|
22312
|
+
if (!required) {
|
|
22313
|
+
return result("Cloud re-upload is not currently required. Normal cloud sync is safe to run.");
|
|
22314
|
+
}
|
|
22315
|
+
if (confirm_local_db_source_of_truth !== "LOCAL DB IS SOURCE OF TRUTH") {
|
|
22316
|
+
return result(
|
|
22317
|
+
'Cloud re-upload is blocked until explicitly confirmed. This operation treats the local DB as source of truth and re-uploads cloud data encrypted with the current key. Retry with confirm_local_db_source_of_truth="LOCAL DB IS SOURCE OF TRUTH", or run `exe-os cloud reupload` in Terminal.',
|
|
22318
|
+
true
|
|
22319
|
+
);
|
|
22320
|
+
}
|
|
22321
|
+
await clearCloudReuploadRequired(client);
|
|
22322
|
+
let syncResult2;
|
|
22323
|
+
try {
|
|
22324
|
+
syncResult2 = await cloudSync({ apiKey: cloud.apiKey, endpoint: cloud.endpoint });
|
|
22325
|
+
} catch (err) {
|
|
22326
|
+
await markCloudReuploadRequired(client);
|
|
22327
|
+
throw err;
|
|
22328
|
+
}
|
|
22329
|
+
return result([
|
|
22330
|
+
"## Cloud Re-upload Complete",
|
|
22331
|
+
"",
|
|
22332
|
+
"Normal cloud sync is safe again. Other devices must reconnect using the current 24-word recovery phrase.",
|
|
22333
|
+
"",
|
|
22334
|
+
`- Active memories: ${syncResult2.totalMemories.toLocaleString()} (\u2191 ${syncResult2.pushed.toLocaleString()} syncable rows pushed, \u2193 ${syncResult2.pulled.toLocaleString()} pulled)`,
|
|
22335
|
+
`- Behaviors: \u2191 ${syncResult2.behaviors.pushed.toLocaleString()}, \u2193 ${syncResult2.behaviors.pulled.toLocaleString()}`,
|
|
22336
|
+
`- GraphRAG: \u2191 ${syncResult2.graphrag.pushed.toLocaleString()}, \u2193 ${syncResult2.graphrag.pulled.toLocaleString()}`,
|
|
22337
|
+
`- Tasks: \u2191 ${syncResult2.tasks.pushed.toLocaleString()}, \u2193 ${syncResult2.tasks.pulled.toLocaleString()}`,
|
|
22338
|
+
`- Documents: \u2191 ${syncResult2.documents.pushed.toLocaleString()}, \u2193 ${syncResult2.documents.pulled.toLocaleString()}`
|
|
22339
|
+
].join("\n"));
|
|
21428
22340
|
}
|
|
21429
|
-
const
|
|
22341
|
+
const syncResult = await cloudSync({
|
|
21430
22342
|
apiKey: cloud.apiKey,
|
|
21431
22343
|
endpoint: cloud.endpoint
|
|
21432
22344
|
});
|
|
@@ -21434,27 +22346,18 @@ function registerCloudSync(server2) {
|
|
|
21434
22346
|
lines.push("## Cloud Sync Complete\n");
|
|
21435
22347
|
lines.push("| Category | Pushed | Pulled |");
|
|
21436
22348
|
lines.push("|----------|--------|--------|");
|
|
21437
|
-
lines.push(`| Memories | ${
|
|
21438
|
-
lines.push(`| Behaviors | ${
|
|
21439
|
-
lines.push(`| GraphRAG | ${
|
|
21440
|
-
lines.push(`| Tasks | ${
|
|
21441
|
-
lines.push(`| Conversations | ${
|
|
21442
|
-
lines.push(`| Documents | ${
|
|
22349
|
+
lines.push(`| Memories | ${syncResult.pushed.toLocaleString()} | ${syncResult.pulled.toLocaleString()} |`);
|
|
22350
|
+
lines.push(`| Behaviors | ${syncResult.behaviors.pushed.toLocaleString()} | ${syncResult.behaviors.pulled.toLocaleString()} |`);
|
|
22351
|
+
lines.push(`| GraphRAG | ${syncResult.graphrag.pushed.toLocaleString()} | ${syncResult.graphrag.pulled.toLocaleString()} |`);
|
|
22352
|
+
lines.push(`| Tasks | ${syncResult.tasks.pushed.toLocaleString()} | ${syncResult.tasks.pulled.toLocaleString()} |`);
|
|
22353
|
+
lines.push(`| Conversations | ${syncResult.conversations.pushed.toLocaleString()} | ${syncResult.conversations.pulled.toLocaleString()} |`);
|
|
22354
|
+
lines.push(`| Documents | ${syncResult.documents.pushed.toLocaleString()} | ${syncResult.documents.pulled.toLocaleString()} |`);
|
|
21443
22355
|
lines.push("");
|
|
21444
|
-
lines.push(`**
|
|
21445
|
-
lines.push(`**Roster:** ${
|
|
21446
|
-
return
|
|
21447
|
-
content: [{ type: "text", text: lines.join("\n") }]
|
|
21448
|
-
};
|
|
22356
|
+
lines.push(`**Active memories:** ${syncResult.totalMemories.toLocaleString()}`);
|
|
22357
|
+
lines.push(`**Roster:** ${syncResult.roster.employees} employees, ${syncResult.roster.identities} identities`);
|
|
22358
|
+
return result(lines.join("\n"));
|
|
21449
22359
|
} catch (err) {
|
|
21450
|
-
return {
|
|
21451
|
-
content: [
|
|
21452
|
-
{
|
|
21453
|
-
type: "text",
|
|
21454
|
-
text: `Cloud sync failed: ${err instanceof Error ? err.message : String(err)}`
|
|
21455
|
-
}
|
|
21456
|
-
]
|
|
21457
|
-
};
|
|
22360
|
+
return result(`Cloud operation failed: ${err instanceof Error ? err.message : String(err)}`, true);
|
|
21458
22361
|
}
|
|
21459
22362
|
}
|
|
21460
22363
|
);
|
|
@@ -21967,14 +22870,14 @@ function registerBackupVps(server2) {
|
|
|
21967
22870
|
r2AccessKeyId: input.r2AccessKeyId,
|
|
21968
22871
|
r2SecretAccessKey: input.r2SecretAccessKey
|
|
21969
22872
|
};
|
|
21970
|
-
const
|
|
22873
|
+
const result2 = await createBackup2(options);
|
|
21971
22874
|
return {
|
|
21972
22875
|
content: [
|
|
21973
22876
|
{
|
|
21974
22877
|
type: "text",
|
|
21975
|
-
text: `Backup complete: ${
|
|
21976
|
-
${
|
|
21977
|
-
${
|
|
22878
|
+
text: `Backup complete: ${result2.key}
|
|
22879
|
+
${result2.sizeBytes} bytes
|
|
22880
|
+
${result2.timestamp}`
|
|
21978
22881
|
}
|
|
21979
22882
|
]
|
|
21980
22883
|
};
|
|
@@ -22007,18 +22910,18 @@ ${result.timestamp}`
|
|
|
22007
22910
|
r2AccessKeyId: input.r2AccessKeyId,
|
|
22008
22911
|
r2SecretAccessKey: input.r2SecretAccessKey
|
|
22009
22912
|
};
|
|
22010
|
-
const
|
|
22913
|
+
const result2 = await listBackups2(options);
|
|
22011
22914
|
return {
|
|
22012
22915
|
content: [
|
|
22013
22916
|
{
|
|
22014
22917
|
type: "text",
|
|
22015
|
-
text: formatBackups(
|
|
22918
|
+
text: formatBackups(result2)
|
|
22016
22919
|
}
|
|
22017
22920
|
]
|
|
22018
22921
|
};
|
|
22019
22922
|
}
|
|
22020
22923
|
case "health": {
|
|
22021
|
-
const
|
|
22924
|
+
const result2 = await backupHealth({
|
|
22022
22925
|
r2Bucket: input.r2Bucket,
|
|
22023
22926
|
r2Endpoint: input.r2Endpoint,
|
|
22024
22927
|
r2AccessKeyId: input.r2AccessKeyId,
|
|
@@ -22029,10 +22932,10 @@ ${result.timestamp}`
|
|
|
22029
22932
|
{
|
|
22030
22933
|
type: "text",
|
|
22031
22934
|
text: [
|
|
22032
|
-
`last_backup: ${
|
|
22033
|
-
`backup_count: ${
|
|
22034
|
-
`total_size_bytes: ${
|
|
22035
|
-
`oldest_backup: ${
|
|
22935
|
+
`last_backup: ${result2.lastBackup ?? "none"}`,
|
|
22936
|
+
`backup_count: ${result2.backupCount}`,
|
|
22937
|
+
`total_size_bytes: ${result2.totalSizeBytes}`,
|
|
22938
|
+
`oldest_backup: ${result2.oldestBackup ?? "none"}`
|
|
22036
22939
|
].join("\n")
|
|
22037
22940
|
}
|
|
22038
22941
|
]
|
|
@@ -22140,9 +23043,9 @@ var HostingerApiClient = class {
|
|
|
22140
23043
|
}
|
|
22141
23044
|
this.lastRequestTime = Date.now();
|
|
22142
23045
|
}
|
|
22143
|
-
async request(method,
|
|
23046
|
+
async request(method, path50, body) {
|
|
22144
23047
|
await this.rateLimit();
|
|
22145
|
-
const url = `${this.baseUrl}${
|
|
23048
|
+
const url = `${this.baseUrl}${path50}`;
|
|
22146
23049
|
const headers = {
|
|
22147
23050
|
Authorization: `Bearer ${this.apiKey}`,
|
|
22148
23051
|
"Content-Type": "application/json",
|
|
@@ -22215,8 +23118,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
|
|
|
22215
23118
|
}
|
|
22216
23119
|
return envelope.result;
|
|
22217
23120
|
}
|
|
22218
|
-
function buildUrl(zoneId,
|
|
22219
|
-
const normalizedPath =
|
|
23121
|
+
function buildUrl(zoneId, path50 = "/dns_records", query) {
|
|
23122
|
+
const normalizedPath = path50.startsWith("/") ? path50 : `/${path50}`;
|
|
22220
23123
|
const url = new URL(
|
|
22221
23124
|
`${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
|
|
22222
23125
|
);
|
|
@@ -22380,7 +23283,7 @@ function registerDeployClient(server2) {
|
|
|
22380
23283
|
};
|
|
22381
23284
|
}
|
|
22382
23285
|
try {
|
|
22383
|
-
const
|
|
23286
|
+
const result2 = await executeDeployment({
|
|
22384
23287
|
client_name,
|
|
22385
23288
|
domain,
|
|
22386
23289
|
region,
|
|
@@ -22395,7 +23298,7 @@ function registerDeployClient(server2) {
|
|
|
22395
23298
|
content: [
|
|
22396
23299
|
{
|
|
22397
23300
|
type: "text",
|
|
22398
|
-
text: JSON.stringify(
|
|
23301
|
+
text: JSON.stringify(result2, null, 2)
|
|
22399
23302
|
}
|
|
22400
23303
|
]
|
|
22401
23304
|
};
|
|
@@ -23507,8 +24410,8 @@ Available: ${available.length > 0 ? available.join(", ") : "none"}`
|
|
|
23507
24410
|
]
|
|
23508
24411
|
};
|
|
23509
24412
|
}
|
|
23510
|
-
const
|
|
23511
|
-
if (!
|
|
24413
|
+
const result2 = applyPack(industry, project);
|
|
24414
|
+
if (!result2) {
|
|
23512
24415
|
return {
|
|
23513
24416
|
content: [
|
|
23514
24417
|
{
|
|
@@ -23521,22 +24424,22 @@ Available: ${available.length > 0 ? available.join(", ") : "none"}`
|
|
|
23521
24424
|
const lines = [
|
|
23522
24425
|
`Applied "${industry}" starter pack to project "${project}":
|
|
23523
24426
|
`,
|
|
23524
|
-
`**CRM Custom Objects** (${
|
|
23525
|
-
...
|
|
24427
|
+
`**CRM Custom Objects** (${result2.customObjects.count}):`,
|
|
24428
|
+
...result2.customObjects.names.map((n) => ` - ${n}`),
|
|
23526
24429
|
"",
|
|
23527
|
-
`**Triggers Created** (${
|
|
23528
|
-
...
|
|
24430
|
+
`**Triggers Created** (${result2.triggers.created}):`,
|
|
24431
|
+
...result2.triggers.ids.map((id, i) => {
|
|
23529
24432
|
const t = pack.triggers[i];
|
|
23530
24433
|
return ` - ${t?.name ?? "Unknown"} (${id})`;
|
|
23531
24434
|
}),
|
|
23532
24435
|
"",
|
|
23533
|
-
`**Wiki Seeds** (${
|
|
23534
|
-
...
|
|
24436
|
+
`**Wiki Seeds** (${result2.wikiSeeds.count}):`,
|
|
24437
|
+
...result2.wikiSeeds.titles.map((t) => ` - ${t}`)
|
|
23535
24438
|
];
|
|
23536
|
-
if (
|
|
24439
|
+
if (result2.pendingVariables.length > 0) {
|
|
23537
24440
|
lines.push("");
|
|
23538
24441
|
lines.push("**Requires manual configuration:**");
|
|
23539
|
-
for (const pv of
|
|
24442
|
+
for (const pv of result2.pendingVariables) {
|
|
23540
24443
|
const vars = Object.entries(pv.variables).map(([k, v]) => ` - {{${k}}}: ${v}`).join("\n");
|
|
23541
24444
|
lines.push(` ${pv.trigger}:
|
|
23542
24445
|
${vars}`);
|
|
@@ -23612,7 +24515,7 @@ ${err.message}`
|
|
|
23612
24515
|
|
|
23613
24516
|
// src/mcp/tools/load-skill.ts
|
|
23614
24517
|
import { z as z69 } from "zod";
|
|
23615
|
-
import { readFileSync as readFileSync26, readdirSync as readdirSync13, statSync as
|
|
24518
|
+
import { readFileSync as readFileSync26, readdirSync as readdirSync13, statSync as statSync7 } from "fs";
|
|
23616
24519
|
import path42 from "path";
|
|
23617
24520
|
import { homedir as homedir7 } from "os";
|
|
23618
24521
|
var SKILLS_DIR = path42.join(homedir7(), ".claude", "skills");
|
|
@@ -23622,9 +24525,9 @@ function listAvailableSkills() {
|
|
|
23622
24525
|
return entries.filter((entry) => {
|
|
23623
24526
|
try {
|
|
23624
24527
|
const entryPath = path42.join(SKILLS_DIR, entry);
|
|
23625
|
-
if (!
|
|
24528
|
+
if (!statSync7(entryPath).isDirectory()) return false;
|
|
23626
24529
|
const skillFile = path42.join(entryPath, "SKILL.md");
|
|
23627
|
-
|
|
24530
|
+
statSync7(skillFile);
|
|
23628
24531
|
return true;
|
|
23629
24532
|
} catch {
|
|
23630
24533
|
return false;
|
|
@@ -23927,11 +24830,11 @@ async function importIdentities(identities, updatedBy) {
|
|
|
23927
24830
|
}
|
|
23928
24831
|
async function getActiveProcedureTitles() {
|
|
23929
24832
|
const client = getClient();
|
|
23930
|
-
const
|
|
24833
|
+
const result2 = await client.execute({
|
|
23931
24834
|
sql: "SELECT title FROM company_procedures WHERE active = 1",
|
|
23932
24835
|
args: []
|
|
23933
24836
|
});
|
|
23934
|
-
return new Set(
|
|
24837
|
+
return new Set(result2.rows.map((row) => String(row.title)));
|
|
23935
24838
|
}
|
|
23936
24839
|
async function exportOrchestration(createdBy) {
|
|
23937
24840
|
const client = getClient();
|
|
@@ -24121,11 +25024,11 @@ function registerImportOrchestration(server2) {
|
|
|
24121
25024
|
await initStore();
|
|
24122
25025
|
const raw = readFileSync28(package_path, "utf-8");
|
|
24123
25026
|
const pkg = validatePackage(JSON.parse(raw));
|
|
24124
|
-
const
|
|
25027
|
+
const result2 = await importOrchestration(pkg, merge_strategy);
|
|
24125
25028
|
return {
|
|
24126
25029
|
content: [{
|
|
24127
25030
|
type: "text",
|
|
24128
|
-
text: `Imported ${
|
|
25031
|
+
text: `Imported ${result2.imported.roster} roster entries, ${result2.imported.identities} identities, ${result2.imported.behaviors} behaviors, ${result2.imported.procedures} procedures using ${merge_strategy} strategy`
|
|
24129
25032
|
}]
|
|
24130
25033
|
};
|
|
24131
25034
|
} catch (err) {
|
|
@@ -24236,11 +25139,11 @@ Domain: ${domain ?? "none"}`
|
|
|
24236
25139
|
};
|
|
24237
25140
|
}
|
|
24238
25141
|
const client = getClient();
|
|
24239
|
-
const
|
|
25142
|
+
const result2 = await client.execute({
|
|
24240
25143
|
sql: "SELECT id, title, content, priority, domain FROM company_procedures WHERE id = ?",
|
|
24241
25144
|
args: [procedure_id]
|
|
24242
25145
|
});
|
|
24243
|
-
if (
|
|
25146
|
+
if (result2.rows.length === 0) {
|
|
24244
25147
|
return {
|
|
24245
25148
|
content: [{
|
|
24246
25149
|
type: "text",
|
|
@@ -24249,7 +25152,7 @@ Domain: ${domain ?? "none"}`
|
|
|
24249
25152
|
isError: true
|
|
24250
25153
|
};
|
|
24251
25154
|
}
|
|
24252
|
-
const row =
|
|
25155
|
+
const row = result2.rows[0];
|
|
24253
25156
|
const wasActive = await deactivateGlobalProcedure(procedure_id);
|
|
24254
25157
|
if (!wasActive) {
|
|
24255
25158
|
return {
|
|
@@ -24387,15 +25290,20 @@ function registerConfig(server2) {
|
|
|
24387
25290
|
subaction: z73.enum(["store", "list", "deactivate"]).optional().describe("Nested action for company_procedure/global_procedure"),
|
|
24388
25291
|
max_clusters: z73.coerce.number().optional().describe("Consolidation max clusters"),
|
|
24389
25292
|
force: z73.boolean().optional().describe("Force operation where supported"),
|
|
25293
|
+
cloud_action: z73.enum(["status", "sync", "reupload"]).optional().describe("Nested operation for action=cloud_sync"),
|
|
25294
|
+
confirm_local_db_source_of_truth: z73.string().optional().describe("Required for cloud_action=reupload; must exactly equal LOCAL DB IS SOURCE OF TRUTH"),
|
|
24390
25295
|
domain_name: z73.string().optional().describe("Client deployment domain"),
|
|
24391
25296
|
phase: z73.enum(["phase_1_coo", "phase_2_executives", "phase_3_parallel_org", "1", "2", "3"]).optional().describe("Orchestration phase for orchestration_phase action")
|
|
24392
25297
|
}
|
|
24393
25298
|
}, async (input, extra) => {
|
|
24394
25299
|
const action = input.action;
|
|
24395
|
-
const { action: _action, subaction, ...args } = input;
|
|
25300
|
+
const { action: _action, subaction, cloud_action, ...args } = input;
|
|
24396
25301
|
if (action === "company_procedure" || action === "global_procedure") {
|
|
24397
25302
|
args.action = subaction ?? "list";
|
|
24398
25303
|
}
|
|
25304
|
+
if (action === "cloud_sync" && cloud_action) {
|
|
25305
|
+
args.action = cloud_action;
|
|
25306
|
+
}
|
|
24399
25307
|
if (action === "export_orchestration" && !args.output_path) return errorResult8('config action "export_orchestration" requires output_path');
|
|
24400
25308
|
if (action === "import_orchestration" && !args.input_path) return errorResult8('config action "import_orchestration" requires input_path');
|
|
24401
25309
|
if (action === "activate_license" && !args.license_key) return errorResult8('config action "activate_license" requires license_key');
|
|
@@ -24793,7 +25701,7 @@ function registerBehavior(server2) {
|
|
|
24793
25701
|
args.push(proj);
|
|
24794
25702
|
}
|
|
24795
25703
|
const where = conditions.join(" AND ");
|
|
24796
|
-
const
|
|
25704
|
+
const result3 = await client2.execute({
|
|
24797
25705
|
sql: `SELECT id, agent_id, project_name, domain, content, active, created_at, updated_at
|
|
24798
25706
|
FROM behaviors
|
|
24799
25707
|
WHERE ${where}
|
|
@@ -24801,7 +25709,7 @@ function registerBehavior(server2) {
|
|
|
24801
25709
|
LIMIT 50`,
|
|
24802
25710
|
args
|
|
24803
25711
|
});
|
|
24804
|
-
const behaviors =
|
|
25712
|
+
const behaviors = result3.rows.map((r) => rowToBehavior2(r));
|
|
24805
25713
|
if (behaviors.length === 0) {
|
|
24806
25714
|
return {
|
|
24807
25715
|
content: [{ type: "text", text: "No behaviors found." }]
|
|
@@ -24900,11 +25808,11 @@ Use /exe-forget to remove any that this supersedes.`;
|
|
|
24900
25808
|
};
|
|
24901
25809
|
}
|
|
24902
25810
|
const client = getClient();
|
|
24903
|
-
const
|
|
25811
|
+
const result2 = await client.execute({
|
|
24904
25812
|
sql: "SELECT id, agent_id, content, domain, priority FROM behaviors WHERE id = ?",
|
|
24905
25813
|
args: [behavior_id]
|
|
24906
25814
|
});
|
|
24907
|
-
if (
|
|
25815
|
+
if (result2.rows.length === 0) {
|
|
24908
25816
|
return {
|
|
24909
25817
|
content: [{
|
|
24910
25818
|
type: "text",
|
|
@@ -24913,7 +25821,7 @@ Use /exe-forget to remove any that this supersedes.`;
|
|
|
24913
25821
|
isError: true
|
|
24914
25822
|
};
|
|
24915
25823
|
}
|
|
24916
|
-
const row =
|
|
25824
|
+
const row = result2.rows[0];
|
|
24917
25825
|
const wasActive = await deactivateBehavior(behavior_id);
|
|
24918
25826
|
if (!wasActive) {
|
|
24919
25827
|
return {
|
|
@@ -25119,11 +26027,11 @@ function registerDeactivateGlobalProcedure(server2) {
|
|
|
25119
26027
|
};
|
|
25120
26028
|
}
|
|
25121
26029
|
const client = getClient();
|
|
25122
|
-
const
|
|
26030
|
+
const result2 = await client.execute({
|
|
25123
26031
|
sql: "SELECT id, title, content, priority, domain FROM company_procedures WHERE id = ?",
|
|
25124
26032
|
args: [procedure_id]
|
|
25125
26033
|
});
|
|
25126
|
-
if (
|
|
26034
|
+
if (result2.rows.length === 0) {
|
|
25127
26035
|
return {
|
|
25128
26036
|
content: [{
|
|
25129
26037
|
type: "text",
|
|
@@ -25132,7 +26040,7 @@ function registerDeactivateGlobalProcedure(server2) {
|
|
|
25132
26040
|
isError: true
|
|
25133
26041
|
};
|
|
25134
26042
|
}
|
|
25135
|
-
const row =
|
|
26043
|
+
const row = result2.rows[0];
|
|
25136
26044
|
const wasActive = await deactivateGlobalProcedure(procedure_id);
|
|
25137
26045
|
if (!wasActive) {
|
|
25138
26046
|
return {
|
|
@@ -25261,14 +26169,14 @@ function registerGetDecision(server2) {
|
|
|
25261
26169
|
},
|
|
25262
26170
|
async ({ domain }) => {
|
|
25263
26171
|
const client = getClient();
|
|
25264
|
-
const
|
|
26172
|
+
const result2 = await client.execute({
|
|
25265
26173
|
sql: `SELECT id, agent_id, timestamp, raw_text, status, supersedes_id, project_name
|
|
25266
26174
|
FROM memories
|
|
25267
26175
|
WHERE memory_type = 'decision' AND source_path = ?
|
|
25268
26176
|
ORDER BY timestamp DESC`,
|
|
25269
26177
|
args: [domain]
|
|
25270
26178
|
});
|
|
25271
|
-
if (
|
|
26179
|
+
if (result2.rows.length === 0) {
|
|
25272
26180
|
return {
|
|
25273
26181
|
content: [
|
|
25274
26182
|
{
|
|
@@ -25278,7 +26186,7 @@ function registerGetDecision(server2) {
|
|
|
25278
26186
|
]
|
|
25279
26187
|
};
|
|
25280
26188
|
}
|
|
25281
|
-
const decisions =
|
|
26189
|
+
const decisions = result2.rows.map((r) => ({
|
|
25282
26190
|
id: String(r.id),
|
|
25283
26191
|
agent_id: String(r.agent_id),
|
|
25284
26192
|
timestamp: String(r.timestamp),
|
|
@@ -25852,10 +26760,429 @@ Upstream status: ${upstreamStatus}`
|
|
|
25852
26760
|
);
|
|
25853
26761
|
}
|
|
25854
26762
|
|
|
25855
|
-
// src/mcp/tools/
|
|
26763
|
+
// src/mcp/tools/code-context.ts
|
|
25856
26764
|
import { z as z87 } from "zod";
|
|
26765
|
+
|
|
26766
|
+
// src/lib/code-context-index.ts
|
|
26767
|
+
init_config();
|
|
26768
|
+
import crypto19 from "crypto";
|
|
26769
|
+
import path48 from "path";
|
|
26770
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync20, readFileSync as readFileSync30, statSync as statSync8, writeFileSync as writeFileSync22 } from "fs";
|
|
26771
|
+
import { spawnSync } from "child_process";
|
|
26772
|
+
var INDEX_VERSION = 1;
|
|
26773
|
+
var DEFAULT_MAX_FILES = 2e3;
|
|
26774
|
+
var CODE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
26775
|
+
var IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build"]);
|
|
26776
|
+
function normalizeProjectRoot(projectRoot) {
|
|
26777
|
+
return path48.resolve(projectRoot || process.cwd());
|
|
26778
|
+
}
|
|
26779
|
+
function hashText(text3) {
|
|
26780
|
+
return crypto19.createHash("sha256").update(text3).digest("hex");
|
|
26781
|
+
}
|
|
26782
|
+
function indexDir() {
|
|
26783
|
+
const dir = path48.join(EXE_AI_DIR, "code-context");
|
|
26784
|
+
mkdirSync20(dir, { recursive: true });
|
|
26785
|
+
return dir;
|
|
26786
|
+
}
|
|
26787
|
+
function getCodeContextIndexPath(projectRoot) {
|
|
26788
|
+
const root = normalizeProjectRoot(projectRoot);
|
|
26789
|
+
const rootHash = hashText(root).slice(0, 16);
|
|
26790
|
+
return path48.join(indexDir(), `${rootHash}.json`);
|
|
26791
|
+
}
|
|
26792
|
+
function currentBranch(projectRoot) {
|
|
26793
|
+
const result2 = spawnSync("git", ["branch", "--show-current"], {
|
|
26794
|
+
cwd: projectRoot,
|
|
26795
|
+
encoding: "utf8",
|
|
26796
|
+
timeout: 2e3
|
|
26797
|
+
});
|
|
26798
|
+
const branch = result2.status === 0 ? result2.stdout.trim() : "";
|
|
26799
|
+
return branch || "detached-or-unknown";
|
|
26800
|
+
}
|
|
26801
|
+
function shouldIgnore(relPath) {
|
|
26802
|
+
const parts = relPath.split(path48.sep);
|
|
26803
|
+
return parts.some((part) => IGNORE_SEGMENTS.has(part));
|
|
26804
|
+
}
|
|
26805
|
+
function isCodeFile(relPath) {
|
|
26806
|
+
return CODE_EXTENSIONS.has(path48.extname(relPath).toLowerCase());
|
|
26807
|
+
}
|
|
26808
|
+
function listCodeFiles(projectRoot, maxFiles) {
|
|
26809
|
+
const git = spawnSync("git", ["ls-files", "*.ts", "*.tsx", "*.js", "*.jsx"], {
|
|
26810
|
+
cwd: projectRoot,
|
|
26811
|
+
encoding: "utf8",
|
|
26812
|
+
timeout: 5e3,
|
|
26813
|
+
maxBuffer: 1024 * 1024 * 8
|
|
26814
|
+
});
|
|
26815
|
+
let files = [];
|
|
26816
|
+
if (git.status === 0 && git.stdout.trim()) {
|
|
26817
|
+
files = git.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
26818
|
+
} else {
|
|
26819
|
+
const rg = spawnSync("rg", ["--files", "-g", "*.ts", "-g", "*.tsx", "-g", "*.js", "-g", "*.jsx"], {
|
|
26820
|
+
cwd: projectRoot,
|
|
26821
|
+
encoding: "utf8",
|
|
26822
|
+
timeout: 5e3,
|
|
26823
|
+
maxBuffer: 1024 * 1024 * 8
|
|
26824
|
+
});
|
|
26825
|
+
if (rg.status === 0 && rg.stdout.trim()) {
|
|
26826
|
+
files = rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
26827
|
+
}
|
|
26828
|
+
}
|
|
26829
|
+
return files.filter((file) => isCodeFile(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
|
|
26830
|
+
}
|
|
26831
|
+
function parseImportPaths(importText) {
|
|
26832
|
+
const paths = [];
|
|
26833
|
+
const fromRegex = /from\s+["']([^"']+)["']/g;
|
|
26834
|
+
let match;
|
|
26835
|
+
while ((match = fromRegex.exec(importText)) !== null) paths.push(match[1]);
|
|
26836
|
+
const bareRegex = /^import\s+["']([^"']+)["']/gm;
|
|
26837
|
+
while ((match = bareRegex.exec(importText)) !== null) {
|
|
26838
|
+
if (!paths.includes(match[1])) paths.push(match[1]);
|
|
26839
|
+
}
|
|
26840
|
+
const requireRegex = /require\(["']([^"']+)["']\)/g;
|
|
26841
|
+
while ((match = requireRegex.exec(importText)) !== null) {
|
|
26842
|
+
if (!paths.includes(match[1])) paths.push(match[1]);
|
|
26843
|
+
}
|
|
26844
|
+
return paths;
|
|
26845
|
+
}
|
|
26846
|
+
function resolveImport(fromFile, importPath, allFiles) {
|
|
26847
|
+
if (!importPath.startsWith(".")) return null;
|
|
26848
|
+
const base = path48.posix.normalize(path48.posix.join(path48.posix.dirname(fromFile.replaceAll(path48.sep, "/")), importPath));
|
|
26849
|
+
const withoutKnownExt = base.replace(/\.(?:js|jsx|ts|tsx)$/, "");
|
|
26850
|
+
const candidates = [
|
|
26851
|
+
base,
|
|
26852
|
+
`${withoutKnownExt}.ts`,
|
|
26853
|
+
`${withoutKnownExt}.tsx`,
|
|
26854
|
+
`${withoutKnownExt}.js`,
|
|
26855
|
+
`${withoutKnownExt}.jsx`,
|
|
26856
|
+
`${base}.ts`,
|
|
26857
|
+
`${base}.tsx`,
|
|
26858
|
+
`${base}.js`,
|
|
26859
|
+
`${base}.jsx`,
|
|
26860
|
+
path48.posix.join(base, "index.ts"),
|
|
26861
|
+
path48.posix.join(base, "index.tsx"),
|
|
26862
|
+
path48.posix.join(base, "index.js"),
|
|
26863
|
+
path48.posix.join(base, "index.jsx")
|
|
26864
|
+
];
|
|
26865
|
+
return candidates.find((candidate) => allFiles.has(candidate)) ?? null;
|
|
26866
|
+
}
|
|
26867
|
+
function symbolId(filePath, chunk) {
|
|
26868
|
+
return hashText(`${filePath}:${chunk.kind}:${chunk.name}:${chunk.startLine}:${chunk.endLine}`).slice(0, 24);
|
|
26869
|
+
}
|
|
26870
|
+
function loadIndex(projectRoot) {
|
|
26871
|
+
const file = getCodeContextIndexPath(projectRoot);
|
|
26872
|
+
if (!existsSync37(file)) return null;
|
|
26873
|
+
try {
|
|
26874
|
+
const parsed = JSON.parse(readFileSync30(file, "utf8"));
|
|
26875
|
+
if (parsed.version !== INDEX_VERSION || parsed.projectRoot !== projectRoot) return null;
|
|
26876
|
+
return parsed;
|
|
26877
|
+
} catch {
|
|
26878
|
+
return null;
|
|
26879
|
+
}
|
|
26880
|
+
}
|
|
26881
|
+
function saveIndex(index) {
|
|
26882
|
+
writeFileSync22(getCodeContextIndexPath(index.projectRoot), JSON.stringify(index, null, 2));
|
|
26883
|
+
}
|
|
26884
|
+
function buildFileRecord(projectRoot, relPath, allFiles, previous) {
|
|
26885
|
+
const absPath = path48.join(projectRoot, relPath);
|
|
26886
|
+
let stat;
|
|
26887
|
+
try {
|
|
26888
|
+
stat = statSync8(absPath);
|
|
26889
|
+
} catch {
|
|
26890
|
+
return null;
|
|
26891
|
+
}
|
|
26892
|
+
if (!stat.isFile()) return null;
|
|
26893
|
+
const source = readFileSync30(absPath, "utf8");
|
|
26894
|
+
const hash = hashText(source);
|
|
26895
|
+
if (previous && previous.hash === hash && previous.mtimeMs === stat.mtimeMs && previous.size === stat.size) {
|
|
26896
|
+
return previous;
|
|
26897
|
+
}
|
|
26898
|
+
if (!isChunkable(relPath)) return null;
|
|
26899
|
+
const chunks = chunkSourceFile(source, relPath);
|
|
26900
|
+
const imports = chunks.filter((chunk) => chunk.kind === "import").flatMap((chunk) => parseImportPaths(chunk.text));
|
|
26901
|
+
const resolvedImports = imports.map((importPath) => resolveImport(relPath, importPath, allFiles)).filter((file) => Boolean(file));
|
|
26902
|
+
const symbols = chunks.filter((chunk) => chunk.kind !== "import" && chunk.name !== "(unknown)").map((chunk) => ({
|
|
26903
|
+
id: symbolId(relPath, chunk),
|
|
26904
|
+
name: chunk.name,
|
|
26905
|
+
kind: chunk.kind,
|
|
26906
|
+
filePath: relPath,
|
|
26907
|
+
startLine: chunk.startLine,
|
|
26908
|
+
endLine: chunk.endLine,
|
|
26909
|
+
summary: summarizeChunk(chunk, relPath),
|
|
26910
|
+
text: chunk.text.slice(0, 6e3),
|
|
26911
|
+
comment: chunk.comment
|
|
26912
|
+
}));
|
|
26913
|
+
return {
|
|
26914
|
+
path: relPath,
|
|
26915
|
+
absPath,
|
|
26916
|
+
hash,
|
|
26917
|
+
mtimeMs: stat.mtimeMs,
|
|
26918
|
+
size: stat.size,
|
|
26919
|
+
imports,
|
|
26920
|
+
resolvedImports,
|
|
26921
|
+
symbols
|
|
26922
|
+
};
|
|
26923
|
+
}
|
|
26924
|
+
function buildCodeContextIndex(options = {}) {
|
|
26925
|
+
const projectRoot = normalizeProjectRoot(options.projectRoot);
|
|
26926
|
+
const maxFiles = options.maxFiles ?? DEFAULT_MAX_FILES;
|
|
26927
|
+
const branch = currentBranch(projectRoot);
|
|
26928
|
+
const previous = options.force ? null : loadIndex(projectRoot);
|
|
26929
|
+
const files = listCodeFiles(projectRoot, maxFiles);
|
|
26930
|
+
const allFiles = new Set(files.map((file) => file.replaceAll(path48.sep, "/")));
|
|
26931
|
+
const fileRecords = {};
|
|
26932
|
+
for (const rel of files) {
|
|
26933
|
+
const normalized = rel.replaceAll(path48.sep, "/");
|
|
26934
|
+
const record = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
|
|
26935
|
+
if (record) fileRecords[normalized] = record;
|
|
26936
|
+
}
|
|
26937
|
+
const index = {
|
|
26938
|
+
version: INDEX_VERSION,
|
|
26939
|
+
projectRoot,
|
|
26940
|
+
rootHash: hashText(projectRoot).slice(0, 16),
|
|
26941
|
+
branch,
|
|
26942
|
+
indexedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26943
|
+
files: fileRecords
|
|
26944
|
+
};
|
|
26945
|
+
saveIndex(index);
|
|
26946
|
+
return index;
|
|
26947
|
+
}
|
|
26948
|
+
function loadOrBuildCodeContextIndex(options = {}) {
|
|
26949
|
+
const projectRoot = normalizeProjectRoot(options.projectRoot);
|
|
26950
|
+
if (!options.force) {
|
|
26951
|
+
const loaded = loadIndex(projectRoot);
|
|
26952
|
+
if (loaded) {
|
|
26953
|
+
const currentFiles = listCodeFiles(projectRoot, options.maxFiles ?? DEFAULT_MAX_FILES);
|
|
26954
|
+
const unchanged = currentFiles.every((rel) => {
|
|
26955
|
+
const normalized = rel.replaceAll(path48.sep, "/");
|
|
26956
|
+
const existing = loaded.files[normalized];
|
|
26957
|
+
if (!existing) return false;
|
|
26958
|
+
try {
|
|
26959
|
+
const stat = statSync8(path48.join(projectRoot, normalized));
|
|
26960
|
+
return stat.mtimeMs === existing.mtimeMs && stat.size === existing.size;
|
|
26961
|
+
} catch {
|
|
26962
|
+
return false;
|
|
26963
|
+
}
|
|
26964
|
+
});
|
|
26965
|
+
if (unchanged && Object.keys(loaded.files).length === currentFiles.length) return loaded;
|
|
26966
|
+
}
|
|
26967
|
+
}
|
|
26968
|
+
return buildCodeContextIndex(options);
|
|
26969
|
+
}
|
|
26970
|
+
function tokenize(query) {
|
|
26971
|
+
return query.toLowerCase().split(/[^a-z0-9_.$/-]+/).map((s) => s.trim()).filter((s) => s.length >= 2);
|
|
26972
|
+
}
|
|
26973
|
+
function scoreSymbol(symbol, terms) {
|
|
26974
|
+
const haystacks = {
|
|
26975
|
+
name: symbol.name.toLowerCase(),
|
|
26976
|
+
path: symbol.filePath.toLowerCase(),
|
|
26977
|
+
summary: symbol.summary.toLowerCase(),
|
|
26978
|
+
text: symbol.text.toLowerCase()
|
|
26979
|
+
};
|
|
26980
|
+
let score = 0;
|
|
26981
|
+
const matches = [];
|
|
26982
|
+
for (const term of terms) {
|
|
26983
|
+
if (haystacks.name === term) {
|
|
26984
|
+
score += 80;
|
|
26985
|
+
matches.push(`name=${term}`);
|
|
26986
|
+
continue;
|
|
26987
|
+
}
|
|
26988
|
+
if (haystacks.name.includes(term)) {
|
|
26989
|
+
score += 40;
|
|
26990
|
+
matches.push(`name~${term}`);
|
|
26991
|
+
}
|
|
26992
|
+
if (haystacks.path.includes(term)) {
|
|
26993
|
+
score += 15;
|
|
26994
|
+
matches.push(`path~${term}`);
|
|
26995
|
+
}
|
|
26996
|
+
if (haystacks.summary.includes(term)) {
|
|
26997
|
+
score += 12;
|
|
26998
|
+
matches.push(`summary~${term}`);
|
|
26999
|
+
}
|
|
27000
|
+
if (haystacks.text.includes(term)) {
|
|
27001
|
+
score += 4;
|
|
27002
|
+
matches.push(`text~${term}`);
|
|
27003
|
+
}
|
|
27004
|
+
}
|
|
27005
|
+
return { score, matches };
|
|
27006
|
+
}
|
|
27007
|
+
function searchCodeContext(query, options = {}) {
|
|
27008
|
+
const terms = tokenize(query);
|
|
27009
|
+
if (terms.length === 0) return [];
|
|
27010
|
+
const index = loadOrBuildCodeContextIndex(options);
|
|
27011
|
+
const results = [];
|
|
27012
|
+
for (const file of Object.values(index.files)) {
|
|
27013
|
+
for (const symbol of file.symbols) {
|
|
27014
|
+
const scored = scoreSymbol(symbol, terms);
|
|
27015
|
+
if (scored.score > 0) results.push({ symbol, score: scored.score, matches: scored.matches });
|
|
27016
|
+
}
|
|
27017
|
+
}
|
|
27018
|
+
return results.sort((a, b) => b.score - a.score || a.symbol.filePath.localeCompare(b.symbol.filePath)).slice(0, options.limit ?? 20);
|
|
27019
|
+
}
|
|
27020
|
+
function dependentsMap(index) {
|
|
27021
|
+
const map = /* @__PURE__ */ new Map();
|
|
27022
|
+
for (const file of Object.values(index.files)) {
|
|
27023
|
+
for (const dep of file.resolvedImports) {
|
|
27024
|
+
if (!map.has(dep)) map.set(dep, /* @__PURE__ */ new Set());
|
|
27025
|
+
map.get(dep).add(file.path);
|
|
27026
|
+
}
|
|
27027
|
+
}
|
|
27028
|
+
return map;
|
|
27029
|
+
}
|
|
27030
|
+
function findSymbols(index, symbolName, limit = 20) {
|
|
27031
|
+
const q = symbolName.toLowerCase();
|
|
27032
|
+
const exact = [];
|
|
27033
|
+
const fuzzy = [];
|
|
27034
|
+
for (const file of Object.values(index.files)) {
|
|
27035
|
+
for (const symbol of file.symbols) {
|
|
27036
|
+
if (symbol.name.toLowerCase() === q) exact.push(symbol);
|
|
27037
|
+
else if (symbol.name.toLowerCase().includes(q) || symbol.summary.toLowerCase().includes(q)) fuzzy.push(symbol);
|
|
27038
|
+
}
|
|
27039
|
+
}
|
|
27040
|
+
return [...exact, ...fuzzy].slice(0, limit);
|
|
27041
|
+
}
|
|
27042
|
+
function traceCodeSymbol(symbolName, options = {}) {
|
|
27043
|
+
const index = loadOrBuildCodeContextIndex(options);
|
|
27044
|
+
const matches = findSymbols(index, symbolName, options.limit ?? 20);
|
|
27045
|
+
const dependents = dependentsMap(index);
|
|
27046
|
+
return {
|
|
27047
|
+
query: symbolName,
|
|
27048
|
+
matches,
|
|
27049
|
+
definitions: matches.map((symbol) => {
|
|
27050
|
+
const file = index.files[symbol.filePath];
|
|
27051
|
+
return {
|
|
27052
|
+
symbol,
|
|
27053
|
+
imports: file.resolvedImports,
|
|
27054
|
+
dependents: [...dependents.get(symbol.filePath) ?? /* @__PURE__ */ new Set()].sort(),
|
|
27055
|
+
relatedSymbols: file.symbols.filter((s) => s.id !== symbol.id).slice(0, 20)
|
|
27056
|
+
};
|
|
27057
|
+
})
|
|
27058
|
+
};
|
|
27059
|
+
}
|
|
27060
|
+
function resolveTargetFile(index, input) {
|
|
27061
|
+
if (input.filePath) {
|
|
27062
|
+
const normalized = input.filePath.replaceAll(path48.sep, "/").replace(/^\.\//, "");
|
|
27063
|
+
if (index.files[normalized]) return { filePath: normalized, target: normalized };
|
|
27064
|
+
const suffix = Object.keys(index.files).find((file) => file.endsWith(normalized));
|
|
27065
|
+
if (suffix) return { filePath: suffix, target: input.filePath };
|
|
27066
|
+
}
|
|
27067
|
+
if (input.symbol) {
|
|
27068
|
+
const match = findSymbols(index, input.symbol, 1)[0];
|
|
27069
|
+
if (match) return { filePath: match.filePath, target: input.symbol };
|
|
27070
|
+
}
|
|
27071
|
+
return null;
|
|
27072
|
+
}
|
|
27073
|
+
function analyzeBlastRadius(input) {
|
|
27074
|
+
const index = loadOrBuildCodeContextIndex({ projectRoot: input.projectRoot, force: input.force });
|
|
27075
|
+
const target = resolveTargetFile(index, { filePath: input.filePath, symbol: input.symbol });
|
|
27076
|
+
if (!target) return null;
|
|
27077
|
+
const dependents = dependentsMap(index);
|
|
27078
|
+
const maxDepth = input.depth ?? 2;
|
|
27079
|
+
const impacted = /* @__PURE__ */ new Map();
|
|
27080
|
+
const queue = [{ filePath: target.filePath, distance: 0 }];
|
|
27081
|
+
impacted.set(target.filePath, { distance: 0, reason: "target" });
|
|
27082
|
+
while (queue.length > 0) {
|
|
27083
|
+
const item = queue.shift();
|
|
27084
|
+
if (item.distance >= maxDepth) continue;
|
|
27085
|
+
for (const dep of dependents.get(item.filePath) ?? []) {
|
|
27086
|
+
if (!impacted.has(dep)) {
|
|
27087
|
+
impacted.set(dep, { distance: item.distance + 1, reason: `imports ${item.filePath}` });
|
|
27088
|
+
queue.push({ filePath: dep, distance: item.distance + 1 });
|
|
27089
|
+
}
|
|
27090
|
+
}
|
|
27091
|
+
}
|
|
27092
|
+
const targetBase = path48.basename(target.filePath).replace(/\.[^.]+$/, "").toLowerCase();
|
|
27093
|
+
const symbolLower = input.symbol?.toLowerCase();
|
|
27094
|
+
const tests = Object.keys(index.files).filter((file) => {
|
|
27095
|
+
const lower = file.toLowerCase();
|
|
27096
|
+
return (lower.includes("test") || lower.includes("spec")) && (lower.includes(targetBase) || (symbolLower ? index.files[file].symbols.some((s) => s.text.toLowerCase().includes(symbolLower)) : false));
|
|
27097
|
+
});
|
|
27098
|
+
for (const test of tests) {
|
|
27099
|
+
if (!impacted.has(test)) impacted.set(test, { distance: 1, reason: "related test/spec" });
|
|
27100
|
+
}
|
|
27101
|
+
const impactedFiles = [...impacted.entries()].map(([filePath, value]) => ({ filePath, distance: value.distance, reason: value.reason })).sort((a, b) => a.distance - b.distance || a.filePath.localeCompare(b.filePath));
|
|
27102
|
+
const nonTestImpacted = impactedFiles.filter((f) => !f.filePath.match(/(test|spec)\./i)).length;
|
|
27103
|
+
const riskLevel = nonTestImpacted >= 8 ? "high" : nonTestImpacted >= 4 ? "medium" : "low";
|
|
27104
|
+
return {
|
|
27105
|
+
target: target.target,
|
|
27106
|
+
targetFile: target.filePath,
|
|
27107
|
+
impactedFiles,
|
|
27108
|
+
tests,
|
|
27109
|
+
symbolsInTarget: index.files[target.filePath]?.symbols ?? [],
|
|
27110
|
+
riskLevel
|
|
27111
|
+
};
|
|
27112
|
+
}
|
|
27113
|
+
function getCodeContextStats(options = {}) {
|
|
27114
|
+
const index = loadOrBuildCodeContextIndex(options);
|
|
27115
|
+
return {
|
|
27116
|
+
projectRoot: index.projectRoot,
|
|
27117
|
+
branch: index.branch,
|
|
27118
|
+
indexedAt: index.indexedAt,
|
|
27119
|
+
files: Object.keys(index.files).length,
|
|
27120
|
+
symbols: Object.values(index.files).reduce((sum, file) => sum + file.symbols.length, 0),
|
|
27121
|
+
imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0),
|
|
27122
|
+
indexPath: getCodeContextIndexPath(index.projectRoot)
|
|
27123
|
+
};
|
|
27124
|
+
}
|
|
27125
|
+
|
|
27126
|
+
// src/mcp/tools/code-context.ts
|
|
27127
|
+
function errorResult10(text3) {
|
|
27128
|
+
return { content: [{ type: "text", text: text3 }], isError: true };
|
|
27129
|
+
}
|
|
27130
|
+
function jsonResult(value) {
|
|
27131
|
+
return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }] };
|
|
27132
|
+
}
|
|
27133
|
+
function registerCodeContext(server2) {
|
|
27134
|
+
server2.registerTool("code_context", {
|
|
27135
|
+
title: "Code Context",
|
|
27136
|
+
description: "Persistent codebase context engine. One consolidated tool to avoid MCP bloat. Actions: index, search, trace, blast_radius, stats.",
|
|
27137
|
+
inputSchema: {
|
|
27138
|
+
action: z87.enum(["index", "search", "trace", "blast_radius", "stats"]).describe("Code context operation"),
|
|
27139
|
+
project_root: z87.string().optional().describe("Repository root. Defaults to current working directory."),
|
|
27140
|
+
query: z87.string().optional().describe("Search query for action=search"),
|
|
27141
|
+
symbol: z87.string().optional().describe("Symbol/function/class/type name for trace or blast_radius"),
|
|
27142
|
+
file_path: z87.string().optional().describe("File path for blast_radius"),
|
|
27143
|
+
force: z87.boolean().optional().describe("Force rebuild before answering"),
|
|
27144
|
+
limit: z87.coerce.number().int().min(1).max(100).optional().describe("Max results"),
|
|
27145
|
+
depth: z87.coerce.number().int().min(1).max(5).optional().describe("Dependent traversal depth for blast_radius"),
|
|
27146
|
+
max_files: z87.coerce.number().int().min(1).max(1e4).optional().describe("Max code files to index")
|
|
27147
|
+
}
|
|
27148
|
+
}, async ({ action, project_root, query, symbol, file_path, force, limit, depth, max_files }) => {
|
|
27149
|
+
const opts = { projectRoot: project_root, force, maxFiles: max_files };
|
|
27150
|
+
if (action === "index") {
|
|
27151
|
+
const index = buildCodeContextIndex(opts);
|
|
27152
|
+
return jsonResult({
|
|
27153
|
+
projectRoot: index.projectRoot,
|
|
27154
|
+
branch: index.branch,
|
|
27155
|
+
indexedAt: index.indexedAt,
|
|
27156
|
+
files: Object.keys(index.files).length,
|
|
27157
|
+
symbols: Object.values(index.files).reduce((sum, file) => sum + file.symbols.length, 0),
|
|
27158
|
+
imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0)
|
|
27159
|
+
});
|
|
27160
|
+
}
|
|
27161
|
+
if (action === "stats") {
|
|
27162
|
+
return jsonResult(getCodeContextStats(opts));
|
|
27163
|
+
}
|
|
27164
|
+
if (action === "search") {
|
|
27165
|
+
if (!query) return errorResult10('code_context action "search" requires query');
|
|
27166
|
+
return jsonResult({ query, results: searchCodeContext(query, { ...opts, limit }) });
|
|
27167
|
+
}
|
|
27168
|
+
if (action === "trace") {
|
|
27169
|
+
if (!symbol) return errorResult10('code_context action "trace" requires symbol');
|
|
27170
|
+
return jsonResult(traceCodeSymbol(symbol, { ...opts, limit }));
|
|
27171
|
+
}
|
|
27172
|
+
if (action === "blast_radius") {
|
|
27173
|
+
if (!symbol && !file_path) return errorResult10('code_context action "blast_radius" requires symbol or file_path');
|
|
27174
|
+
const result2 = analyzeBlastRadius({ projectRoot: project_root, force, symbol, filePath: file_path, depth });
|
|
27175
|
+
if (!result2) return errorResult10(`No code context target found for ${symbol || file_path}`);
|
|
27176
|
+
return jsonResult(result2);
|
|
27177
|
+
}
|
|
27178
|
+
return errorResult10(`Unknown code_context action: ${String(action)}`);
|
|
27179
|
+
});
|
|
27180
|
+
}
|
|
27181
|
+
|
|
27182
|
+
// src/mcp/tools/support-inbox.ts
|
|
27183
|
+
import { z as z88 } from "zod";
|
|
25857
27184
|
var DEFAULT_ENDPOINT = "https://askexe.com/admin/support/bug-reports";
|
|
25858
|
-
var STATUS =
|
|
27185
|
+
var STATUS = z88.enum(["open", "triaged", "fixed", "closed", "wontfix"]);
|
|
25859
27186
|
function adminToken() {
|
|
25860
27187
|
return process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN;
|
|
25861
27188
|
}
|
|
@@ -25894,9 +27221,9 @@ function registerListBugReports(server2) {
|
|
|
25894
27221
|
title: "List Bug Reports",
|
|
25895
27222
|
description: "AskExe-internal only: list incoming customer bug reports from the support inbox.",
|
|
25896
27223
|
inputSchema: {
|
|
25897
|
-
status:
|
|
25898
|
-
severity:
|
|
25899
|
-
limit:
|
|
27224
|
+
status: z88.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("open"),
|
|
27225
|
+
severity: z88.enum(["p0", "p1", "p2", "p3"]).optional(),
|
|
27226
|
+
limit: z88.number().int().min(1).max(100).default(25)
|
|
25900
27227
|
}
|
|
25901
27228
|
},
|
|
25902
27229
|
async ({ status, severity, limit }) => {
|
|
@@ -25915,7 +27242,7 @@ function registerGetBugReport(server2) {
|
|
|
25915
27242
|
{
|
|
25916
27243
|
title: "Get Bug Report",
|
|
25917
27244
|
description: "AskExe-internal only: fetch one customer bug report with full markdown payload.",
|
|
25918
|
-
inputSchema: { id:
|
|
27245
|
+
inputSchema: { id: z88.string().min(8) }
|
|
25919
27246
|
},
|
|
25920
27247
|
async ({ id }) => {
|
|
25921
27248
|
const data = await requestJson(`${endpoint()}/${encodeURIComponent(id)}`);
|
|
@@ -25930,12 +27257,12 @@ function registerTriageBugReport(server2) {
|
|
|
25930
27257
|
title: "Triage Bug Report",
|
|
25931
27258
|
description: "AskExe-internal only: update bug report status and link task/commit/release metadata.",
|
|
25932
27259
|
inputSchema: {
|
|
25933
|
-
id:
|
|
27260
|
+
id: z88.string().min(8),
|
|
25934
27261
|
status: STATUS.optional(),
|
|
25935
|
-
triage_notes:
|
|
25936
|
-
linked_task_id:
|
|
25937
|
-
linked_commit:
|
|
25938
|
-
fixed_version:
|
|
27262
|
+
triage_notes: z88.string().optional(),
|
|
27263
|
+
linked_task_id: z88.string().optional(),
|
|
27264
|
+
linked_commit: z88.string().optional(),
|
|
27265
|
+
fixed_version: z88.string().optional()
|
|
25939
27266
|
}
|
|
25940
27267
|
},
|
|
25941
27268
|
async ({ id, status, triage_notes, linked_task_id, linked_commit, fixed_version }) => {
|
|
@@ -25959,7 +27286,7 @@ var TOOL_GATES = {
|
|
|
25959
27286
|
reminders: [],
|
|
25960
27287
|
// all agents — create, list, complete, reminder
|
|
25961
27288
|
"graph-read": [],
|
|
25962
|
-
// all agents — query_relationships, get_entity_neighbors, get_hot_entities, get_graph_stats, find_similar_trajectories
|
|
27289
|
+
// all agents — query_relationships, get_entity_neighbors, get_hot_entities, get_graph_stats, find_similar_trajectories, code_context
|
|
25963
27290
|
"graph-write": ["COO", "CTO"],
|
|
25964
27291
|
// merge_entities, export_graph
|
|
25965
27292
|
wiki: ["COO", "Wiki Agent"],
|
|
@@ -25998,6 +27325,7 @@ var TOOL_CATEGORIES = {
|
|
|
25998
27325
|
registerStoreDecision: "core",
|
|
25999
27326
|
registerGetDecision: "core",
|
|
26000
27327
|
registerCreateBugReport: "core",
|
|
27328
|
+
registerCodeContext: "graph-read",
|
|
26001
27329
|
registerListBugReports: "admin",
|
|
26002
27330
|
registerGetBugReport: "admin",
|
|
26003
27331
|
registerTriageBugReport: "admin",
|
|
@@ -26260,6 +27588,7 @@ function registerAllTools(server2) {
|
|
|
26260
27588
|
gate("registerStoreDecision", registerStoreDecision);
|
|
26261
27589
|
gate("registerGetDecision", registerGetDecision);
|
|
26262
27590
|
gate("registerCreateBugReport", registerCreateBugReport);
|
|
27591
|
+
gate("registerCodeContext", registerCodeContext);
|
|
26263
27592
|
if (process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN) {
|
|
26264
27593
|
gate("registerListBugReports", registerListBugReports);
|
|
26265
27594
|
gate("registerGetBugReport", registerGetBugReport);
|
|
@@ -26342,9 +27671,9 @@ async function withTrace(toolName, fn) {
|
|
|
26342
27671
|
return tracer.startActiveSpan(`mcp.tool.${toolName}`, async (span) => {
|
|
26343
27672
|
span.setAttribute("mcp.tool.name", toolName);
|
|
26344
27673
|
try {
|
|
26345
|
-
const
|
|
27674
|
+
const result2 = await fn();
|
|
26346
27675
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
26347
|
-
return
|
|
27676
|
+
return result2;
|
|
26348
27677
|
} catch (err) {
|
|
26349
27678
|
span.setStatus({
|
|
26350
27679
|
code: SpanStatusCode.ERROR,
|
|
@@ -26429,16 +27758,16 @@ try {
|
|
|
26429
27758
|
}
|
|
26430
27759
|
}, 3e4);
|
|
26431
27760
|
_ppidWatchdog.unref();
|
|
26432
|
-
const MCP_VERSION_PATH =
|
|
27761
|
+
const MCP_VERSION_PATH = path49.join(os20.homedir(), ".exe-os", "mcp-version");
|
|
26433
27762
|
let _currentMcpVersion = null;
|
|
26434
27763
|
try {
|
|
26435
|
-
_currentMcpVersion =
|
|
27764
|
+
_currentMcpVersion = existsSync38(MCP_VERSION_PATH) ? readFileSync31(MCP_VERSION_PATH, "utf8").trim() : null;
|
|
26436
27765
|
} catch {
|
|
26437
27766
|
}
|
|
26438
27767
|
const _versionWatchdog = setInterval(() => {
|
|
26439
27768
|
try {
|
|
26440
|
-
if (!
|
|
26441
|
-
const diskVersion =
|
|
27769
|
+
if (!existsSync38(MCP_VERSION_PATH)) return;
|
|
27770
|
+
const diskVersion = readFileSync31(MCP_VERSION_PATH, "utf8").trim();
|
|
26442
27771
|
if (_currentMcpVersion && diskVersion !== _currentMcpVersion) {
|
|
26443
27772
|
process.stderr.write(
|
|
26444
27773
|
`[exe-os] MCP version changed (${_currentMcpVersion} \u2192 ${diskVersion}). Hot-reloading...
|
|
@@ -26455,10 +27784,10 @@ try {
|
|
|
26455
27784
|
_backfillTimer = setInterval(async () => {
|
|
26456
27785
|
try {
|
|
26457
27786
|
const client = getClient();
|
|
26458
|
-
const
|
|
27787
|
+
const result2 = await client.execute(
|
|
26459
27788
|
"SELECT COUNT(*) as cnt FROM memories WHERE vector IS NULL"
|
|
26460
27789
|
);
|
|
26461
|
-
const nullCount = Number(
|
|
27790
|
+
const nullCount = Number(result2.rows[0]?.cnt) || 0;
|
|
26462
27791
|
if (nullCount === 0) return;
|
|
26463
27792
|
const { tryAcquireWorkerSlot: tryAcquireWorkerSlot2, registerWorkerPid: registerWorkerPid2 } = await Promise.resolve().then(() => (init_worker_gate(), worker_gate_exports));
|
|
26464
27793
|
if (!tryAcquireWorkerSlot2()) {
|
|
@@ -26471,14 +27800,14 @@ try {
|
|
|
26471
27800
|
`
|
|
26472
27801
|
);
|
|
26473
27802
|
const thisFile = fileURLToPath5(import.meta.url);
|
|
26474
|
-
const backfillPath =
|
|
26475
|
-
|
|
27803
|
+
const backfillPath = path49.resolve(
|
|
27804
|
+
path49.dirname(thisFile),
|
|
26476
27805
|
"../bin/backfill-vectors.js"
|
|
26477
27806
|
);
|
|
26478
|
-
if (
|
|
27807
|
+
if (existsSync38(backfillPath)) {
|
|
26479
27808
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
26480
|
-
const logPath =
|
|
26481
|
-
|
|
27809
|
+
const logPath = path49.join(exeDir, "workers.log");
|
|
27810
|
+
mkdirSync21(path49.dirname(logPath), { recursive: true });
|
|
26482
27811
|
let logFd = "ignore";
|
|
26483
27812
|
try {
|
|
26484
27813
|
logFd = openSync3(logPath, "a");
|